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é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 un observateur) a rejoint une session |
participant.left | Un participant s'est 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 auprès de l'une des TSA configurées |
signature.completed | Un PDF signé (SES/AES/QES) est prêt au téléchargement |
webhook.test | Dé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.