EN DIRECT · AUDIT CHAÎNÉ · ÉDR UE
SYSTÈME · 99,99% DISPONIBILITÉ
v 1.0 ↗ FAIT EN UE

Webhooks

Les webhooks permettent à votre backend d'apprendre les événements qui surviennent de façon asynchrone — sessions qui se terminent, enregistrements dont le post-traitement s'achève, chaînes d'audit ancrées à la TSA. Enregistrez une URL, vérifiez la signature sur chaque POST, dispatchez selon le type d'événement.

Types d'événements

ÉvénementQuand
session.createdNouvelle session via la SPA ou l'API publique
session.completedSession terminée (manuellement ou via l'API)
participant.joinedUn utilisateur terrain (ou un observateur) a rejoint une session
participant.leftUn participant s'est déconnecté
evidence.addedNouvelle capture / annotation / tableau blanc / clip / document capturé
recording.readyArtefacts d'enregistrement post-traités disponibles
audit.anchoredTête de chaîne ancrée auprès de l'une des TSA configurées
signature.completedUn PDF signé (SES/AES/QES) est prêt au téléchargement
webhook.testDéclenché par le bouton « Test fire » de l'admin de la SPA, avec un petit payload synthétique

Forme de l'enveloppe

{
  "id": "evt_01HGB9...",
  "type": "session.completed",
  "created_at": "2026-05-23T11:42:15Z",
  "org_id": 42,
  "data": {
    /* event-specific payload — full resource shape */
  }
}

L'id est stable d'une nouvelle tentative à l'autre — utilisez-le comme clé d'idempotence côté récepteur.

En-tête de signature

Chaque POST porte un en-tête NB-Signature au format de type Stripe :

NB-Signature: t=1716461235,v1=5257a8...3e2c1f

La valeur v1 est HMAC-SHA256(secret, "{timestamp}.{body}") en hexadécimal. La valeur t est un timestamp Unix au moment de la signature. Une fenêtre de tolérance de 5 minutes rejette les rejeux de payloads plus anciens.

Vérifier dans le code

Node

import { NexBasira, InvalidSignatureError } from "@nexbasira/node";

const nb = new NexBasira({ apiKey: "...", apiSecret: "..." });

// Express handler — important: use express.raw() so the body is the
// untouched bytes the signature was computed over.
app.post("/nb-webhook", express.raw({ type: "application/json" }), (req, res) => {
  const sig = req.header("NB-Signature")!;
  const secret = process.env.NB_WEBHOOK_SECRET!;
  let event;
  try {
    event = nb.webhooks.constructEvent(req.body, sig, secret);
  } catch (err) {
    if (err instanceof InvalidSignatureError) {
      return res.status(401).send("bad signature");
    }
    throw err;
  }
  // event is now type-narrowed by event.type
  return handleEvent(event, res);
});

Python

from nexbasira import NexBasira, InvalidSignatureError

nb = NexBasira(api_key="...", api_secret="...")

# Flask handler — get the raw body, not request.get_json()
@app.post("/nb-webhook")
def webhook():
    sig = request.headers["NB-Signature"]
    secret = os.environ["NB_WEBHOOK_SECRET"]
    try:
        event = nb.webhooks.construct_event(request.data, sig, secret)
    except InvalidSignatureError:
        return ("bad signature", 401)
    return handle_event(event)

Comportement des nouvelles tentatives

Une livraison est considérée comme réussie lorsque votre endpoint renvoie un 2xx en moins de 30 s. Tout le reste déclenche un backoff exponentiel :

30s 5m 1h 6h 24h DROPPED

Après 50 échecs consécutifs tous événements confondus, l'endpoint se désactive automatiquement. L'administrateur de l'organisation peut le réactiver depuis l'admin de la SPA une fois le récepteur rétabli.

Idempotence de votre côté

Les livraisons de webhook peuvent se répéter. Traitez event.id comme clé de déduplication :

-- Postgres example
INSERT INTO webhook_deliveries (event_id, processed_at)
VALUES ($1, NOW())
ON CONFLICT (event_id) DO NOTHING
RETURNING event_id;
-- If RETURNING is empty, this is a duplicate — skip the side-effect.

Secrets de signature

Chaque endpoint de webhook enregistré possède son propre secret de signature whsec_*. Le texte en clair est affiché une seule fois au moment où vous enregistrez l'endpoint (ou le faites tourner) ; ensuite, nous en stockons une copie chiffrée avec Fernet et n'exposons que les 6 premiers caractères pour l'identification.

Faites tourner depuis SPA → Admin → Webhooks → Faire tourner le secret. Les récepteurs existants rejetteront les événements signés jusqu'à leur mise à jour avec le nouveau secret — planifiez la rotation avec une fenêtre de déploiement.

Envoi de test

Chaque endpoint enregistré dispose d'un bouton « Test fire » dans l'admin de la SPA qui envoie une enveloppe webhook.test pour que vous puissiez vérifier votre récepteur avant la mise en production. L'enveloppe de test a la même forme qu'un événement réel, mais avec un payload synthétique marqué "test": true dans data.

Journal des livraisons

SPA → Admin → Webhooks affiche les 100 dernières livraisons par endpoint avec le statut HTTP, les tentatives, le dernier corps de réponse (tronqué) et les horodatages. Filtrez par statut / endpoint pour déboguer les problèmes de récepteur.