LIVE · AUDIT-KETEN · EU-GEHOST
SYSTEEM · 99,99% UPTIME
v 1.0 ↗ GEMAAKT IN DE EU

Webhooks

Webhooks zijn hoe uw backend leert over zaken die asynchroon gebeuren — sessies die eindigen, opnames die klaar zijn met nabewerking, audit-ketens die verankeren bij de TSA. Registreer een URL, verifieer de handtekening op elke POST, dispatch op event-type.

Event-types

GebeurtenisWanneer
session.createdNieuwe sessie via SPA of publieke API
session.completedSessie beëindigd (handmatig of via API)
participant.joinedVeldgebruiker (of observer) is bij een sessie gekomen
participant.leftDeelnemer heeft de verbinding verbroken
evidence.addedNieuwe snapshot / annotatie / whiteboard / clip / document vastgelegd
recording.readyNabewerkte opname-artefacten beschikbaar
audit.anchoredKetenkop verankerd bij een van de geconfigureerde TSA's
signature.completedEen ondertekend PDF (SES/AES/QES) is klaar om te downloaden
webhook.testAfgevuurd door de knop "Test fire" in het SPA-admin, met een kleine synthetische payload

Envelope-vorm

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

De id is stabiel over retries heen — gebruik hem als uw idempotency-key aan de ontvangerkant.

Handtekening-header

Elke POST draagt een NB-Signature-header in Stripe-stijl-formaat:

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

De v1 waarde is HMAC-SHA256(secret, "{timestamp}.{body}") in hex. De t is een Unix-tijdstempel op het moment van ondertekenen. Een skew-venster van 5 minuten weigert replays van oudere payloads.

Verifiëren in 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)

Retry-gedrag

Een aflevering geldt als geslaagd wanneer uw endpoint binnen 30s een 2xx retourneert. Al het andere activeert exponentiële backoff:

30s 5m 1h 6h 24h DROPPED

Na 50 opeenvolgende mislukkingen over alle events schakelt het endpoint zichzelf uit. De org-admin kan het opnieuw inschakelen vanuit de SPA-admin zodra de ontvanger weer beschikbaar is.

Idempotentie aan uw kant

Webhook-afleveringen kunnen zich herhalen. Behandel event.id als de deduplicatiesleutel:

-- 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.

Ondertekeningsgeheimen

Elk geregistreerd webhook-endpoint heeft zijn eigen whsec_*-ondertekeningsgeheim. De platte tekst wordt precies één keer getoond wanneer u het endpoint registreert (of roteert); daarna bewaren we een Fernet-versleutelde kopie en tonen we alleen de eerste 6 tekens ter identificatie.

Roteer via SPA → Admin → Webhooks → Geheim roteren. Bestaande ontvangers weigeren ondertekende events totdat ze met het nieuwe geheim zijn bijgewerkt — plan de rotatie samen met een deploy-venster.

Testverzending

Elk geregistreerd endpoint heeft een knop "Testverzending" in de SPA-admin die een webhook.test-envelope verstuurt, zodat u uw ontvanger kunt verifiëren voordat u live gaat. De test-envelope heeft dezelfde vorm als een echt event, maar met een synthetische payload gemarkeerd met "test": true in data.

Afleveringslogboek

SPA → Admin → Webhooks toont de laatste 100 afleveringen per endpoint met HTTP-status, pogingen, laatste response-body (afgekapt) en tijdstempels. Filter op status / endpoint om ontvangerproblemen te debuggen.