LIVE · LANȚ DE AUDIT · UE
SISTEM · 99,99% UPTIME
v 1.0 ↗ FĂCUT ÎN UE

Webhooks

Les webhooks permettent à votre backend d'être informé des événements qui se produisent de manière asynchrone — sessions qui se terminent, enregistrements qui achèvent leur post-traitement, chaînes d'audit qui s'ancrent au TSA. Enregistrez une URL, vérifiez la signature à 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 observateur) a rejoint une session
participant.leftParticipant 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 à l'un des TSA configurés
signature.completedUn PDF signé (SES/AES/QES) est prêt à être téléchargé
webhook.testDéclenché par le bouton « Test fire » dans l'administration de la SPA, avec un minuscule payload synthétique

Structure 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 */
  }
}

Le champ id est stable d'une 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érification 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 de relivraison

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 SPA une fois le récepteur rétabli.

Idempotence de votre côté

Les livraisons de webhooks 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 lors de l'enregistrement de l'endpoint (ou de sa rotation) ; ensuite, nous conservons une copie chiffrée avec Fernet et n'exposons que les 6 premiers caractères à des fins d'identification.

Effectuez la rotation depuis SPA → Admin → Webhooks → Rotation du secret. Les récepteurs existants rejetteront les événements signés tant qu'ils ne sont pas mis à 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 « Envoi de test » dans l'admin SPA qui expédie une enveloppe webhook.test afin 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 une charge utile synthétique marquée "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 diagnostiquer les problèmes de récepteur.