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énement | Quand |
|---|---|
session.created | Nouvelle session via la SPA ou l'API publique |
session.completed | Session terminée (manuellement ou via l'API) |
participant.joined | Un utilisateur terrain (ou observateur) a rejoint une session |
participant.left | Participant déconnecté |
evidence.added | Nouvelle capture / annotation / tableau blanc / clip / document capturé |
recording.ready | Artefacts d'enregistrement post-traités disponibles |
audit.anchored | Tête de chaîne ancrée à l'un des TSA configurés |
signature.completed | Un PDF signé (SES/AES/QES) est prêt à être téléchargé |
webhook.test | Dé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.