LIVE · CATENA D'AUDIT · UE
SISTEMA · 99,99% UPTIME
v 1.0 ↗ FATTO IN UE

Webhook

I webhook sono il modo in cui il tuo backend viene a sapere di cose che accadono in modo asincrono — sessioni che terminano, registrazioni che completano la post-elaborazione, catene di audit che si ancorano al TSA. Registra un URL, verifica la firma su ogni POST, effettua il dispatch in base al tipo di evento.

Tipi di evento

EventoQuando
session.createdNuova sessione tramite SPA o API pubblica
session.completedSessione terminata (manualmente o via API)
participant.joinedUtente sul campo (o osservatore) entrato in una sessione
participant.leftPartecipante disconnesso
evidence.addedNuovo snapshot / annotazione / lavagna / clip / documento catturato
recording.readyArtefatti della registrazione post-elaborata disponibili
audit.anchoredTesta della catena ancorata a uno dei TSA configurati
signature.completedUn PDF firmato (SES/AES/QES) è pronto per il download
webhook.testAttivato dal pulsante "Test fire" nell'admin della SPA, con un payload sintetico minimo

Forma della envelope

{
  "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 è stabile tra un retry e l'altro — usalo come chiave di idempotenza sul lato ricevente.

Header della firma

Ogni POST porta un header NB-Signature in formato stile Stripe:

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

Il valore v1 è HMAC-SHA256(secret, "{timestamp}.{body}") in esadecimale. Il valore t è un timestamp Unix al momento della firma. Una finestra di scarto di 5 minuti rifiuta i replay di payload più vecchi.

Verifica nel codice

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)

Comportamento dei retry

Un recapito è considerato riuscito quando il tuo endpoint restituisce un 2xx entro 30s. Qualsiasi altra cosa innesca un backoff esponenziale:

30s 5m 1h 6h 24h DROPPED

Dopo 50 fallimenti consecutivi su tutti gli eventi, l'endpoint si auto-disabilita. L'admin dell'org può riabilitarlo dall'admin della SPA una volta che il ricevente è di nuovo attivo.

Idempotenza dal tuo lato

I recapiti dei webhook possono ripetersi. Tratta event.id come chiave di deduplicazione:

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

Signing secret

Ogni endpoint webhook registrato ha il proprio signing secret whsec_*. Il testo in chiaro viene mostrato esattamente una volta quando registri l'endpoint (o lo ruoti); dopodiché conserviamo una copia cifrata con Fernet e mostriamo solo i primi 6 caratteri per l'identificazione.

Ruota da SPA → Admin → Webhooks → Rotate secret. I receiver esistenti rifiuteranno gli eventi firmati finché non vengono aggiornati con il nuovo secret — pianifica la rotazione con una finestra di deploy.

Test fire

Ogni endpoint registrato ha un pulsante "Test fire" nell'admin SPA che invia un envelope webhook.test così puoi verificare il tuo receiver prima di andare in produzione. L'envelope di test ha la stessa forma di un evento reale ma con un payload sintetico contrassegnato con "test": true in data.

Log dei recapiti

SPA → Admin → Webhooks mostra gli ultimi 100 recapiti per endpoint con stato HTTP, tentativi, ultimo corpo della risposta (troncato) e timestamp. Filtra per stato / endpoint per fare il debug dei problemi del receiver.