Webhooki
Webhooki to sposób, w jaki Twój backend dowiaduje się o rzeczach dziejących się asynchronicznie — kończących się sesjach, nagraniach kończących przetwarzanie, łańcuchach audytu kotwiczonych w TSA. Zarejestruj URL, weryfikuj podpis przy każdym POST, dyspozycjonuj według typu zdarzenia.
Typy zdarzeń
| Zdarzenie | Kiedy |
|---|---|
session.created | Nowa sesja przez SPA lub publiczne API |
session.completed | Sesja zakończona (ręcznie lub przez API) |
participant.joined | Użytkownik terenowy (lub obserwator) dołączył do sesji |
participant.left | Uczestnik rozłączył się |
evidence.added | Przechwycono nowy zrzut / adnotację / tablicę / klip / dokument |
recording.ready | Dostępne przetworzone artefakty nagrania |
audit.anchored | Głowica łańcucha zakotwiczona w jednym ze skonfigurowanych TSA |
signature.completed | Podpisany PDF (SES/AES/QES) jest gotowy do pobrania |
webhook.test | Wyzwalane przyciskiem "Test fire" w panelu administracyjnym SPA, z drobnym syntetycznym payloadem |
Kształt koperty
{
"id": "evt_01HGB9...",
"type": "session.completed",
"created_at": "2026-05-23T11:42:15Z",
"org_id": 42,
"data": {
/* event-specific payload — full resource shape */
}
} Pole id jest stabilne przy ponowieniach — użyj go jako klucza idempotencji po stronie odbiorcy.
Nagłówek podpisu
Każdy POST niesie nagłówek NB-Signature w formacie w stylu Stripe:
NB-Signature: t=1716461235,v1=5257a8...3e2c1f Wartość v1 to HMAC-SHA256(secret, "{timestamp}.{body}") w hex. Wartość t to znacznik czasu Unix z momentu podpisania. 5-minutowe okno tolerancji odrzuca powtórki starszych payloadów.
Weryfikacja w kodzie
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) Zachowanie przy ponowieniach
Dostarczenie jest uznawane za udane, gdy Twój endpoint zwróci 2xx w ciągu 30 s. Cokolwiek innego wyzwala wykładniczy backoff:
30s → 5m → 1h → 6h → 24h → DROPPED Po 50 kolejnych niepowodzeniach we wszystkich zdarzeniach endpoint automatycznie się wyłącza. Administrator organizacji może go ponownie włączyć z panelu administracyjnego SPA, gdy odbiorca wróci do działania.
Idempotencja po Twojej stronie
Dostarczenia webhooków mogą się powtarzać. Traktuj event.id jako klucz deduplikacji:
-- 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. Sekrety podpisujące
Każdy zarejestrowany endpoint webhooka ma własny sekret podpisujący whsec_*. Tekst jawny jest pokazywany dokładnie raz, gdy rejestrujesz endpoint (lub go rotujesz); potem przechowujemy kopię zaszyfrowaną Fernet i udostępniamy tylko pierwsze 6 znaków do identyfikacji.
Rotuj z SPA → Admin → Webhooki → Rotuj sekret. Istniejący odbiorcy będą odrzucać podpisane zdarzenia, dopóki nie zostaną zaktualizowani nowym sekretem — zaplanuj rotację wraz z oknem wdrożenia.
Test fire
Każdy zarejestrowany endpoint ma w panelu administracyjnym SPA przycisk "Test fire", który wysyła kopertę webhook.test, abyś mógł zweryfikować odbiorcę przed uruchomieniem produkcyjnym. Koperta testowa ma taki sam kształt jak prawdziwe zdarzenie, ale z syntetycznym payloadem oznaczonym "test": true w data.
Dziennik dostarczeń
SPA → Admin → Webhooki pokazuje ostatnie 100 dostarczeń per endpoint ze statusem HTTP, próbami, ostatnim ciałem odpowiedzi (skróconym) i znacznikami czasu. Filtruj według statusu / endpointu, aby debugować problemy odbiorcy.