Een webhook stuurt de beslissingen bot en bot_high van Bot Protection naar een endpoint van jou: de doorgifte naar een chatkanaal, een ticketwachtrij, je eigen fraudecontrole. Je server krijgt elke beslissing nog steeds van /v1/decide zoals voorheen; de webhook is een kopie, verzonden na het antwoord, en laat nooit een beslissing wachten.
Stel je endpoint in
Op de pagina Set-up van het dashboard, onder Webhook, schrijft een eigenaar het adres van het endpoint en kiest wat het krijgt:
- Elke beslissing van de gekozen klassen,
bot,bot_highof beide, in batches van hoogstens 100, seconden na de beslissing. - Een samenvatting elke 5, 15 of 60 minuten: hoeveel beslissingen
botenbot_higher waren.
Het adres is https://, op poort 443, met een naam zoals hooks.example.com, nooit een IP-adres. Korzen stuurt niets naar een adres van een privénetwerk, waar de naam ook naar verwijst, en controleert dat bij elke bezorging.
De eerste keer dat een endpoint wordt opgeslagen, toont Set-up het ondertekeningssecret, kz_whs_ en 64 tekens, één keer: kopieer het naar de configuratie van je endpoint. Maak een nieuw secret vervangt het, ook maar één keer getoond; 24 uur lang ondertekent het oude elke bezorging er nog naast, zodat je endpoint blijft controleren terwijl je de kopie bijwerkt. Stuur een test stuurt nu een ondertekende bezorging test en toont wat je endpoint antwoordde.
De payload
Elke bezorging is een POST met een JSON-body. Een batch beslissingen:
{
"type": "decisions",
"id": "0193a5f0-7c1e-7d2a-9b4e-3f1a2c9d8e01",
"decisions": [
{
"at": "2026-10-28T12:00:00.123Z",
"request_id": "…",
"class": "bot_high",
"action": "block",
"intended_action": "block",
"bot_score": 97,
"mode": "block",
"reasons": ["behaviour", "inconsistency"]
}
],
"dropped": 0
}
Elke beslissing is at, het moment van de beslissing, en deze leden van het antwoord dat /v1/decide je server gaf: request_id, class, bot_score, human_score, reasons, action, intended_action, mode, step_up, token_required, identity_limited, seen_before, crawler, decoy en own_server. Verder niets: nooit de naam van een regel, de context die je stuurde of de link van de challengepagina. dropped telt de beslissingen waarvoor deze batch geen ruimte had.
Een samenvatting:
{"type": "summary", "id": "…", "from": "2026-10-28T12:00:00Z", "to": "2026-10-28T12:15:00Z", "bot": 412, "bot_high": 37}
Een test: {"type": "test", "id": "…", "at": "…"}. Lees eerst type: er kan een nieuw type komen, en aan elk kan een veld worden toegevoegd.
id is bij elke poging van een bezorging hetzelfde: heb je hem al verwerkt, antwoord dan 2xx en laat hem vallen. Bezorgingen kunnen in een andere volgorde aankomen.
Wat een bezorging belooft
- Minstens één keer, eenmaal in de wachtrij. Een bezorging in de wachtrij wordt geprobeerd tot je endpoint
2xxantwoordt of gepauzeerd wordt, dus dezelfde bezorging kan twee keer komen: laat een herhaling vallen op haarid. - Best effort daarvoor. Elke server van Korzen houdt een beslissing tot 10 seconden vast voordat hij haar in de wachtrij zet, en zet alles wat hij vasthoudt in de wachtrij als hij stopt voor een deploy. Een server die crasht, verliest wat hij vasthield, en de beslissingen boven de ruimte van een batch of de uurlimiet van je abonnement worden geteld in
dropped, nooit verzonden. - De webhook is een kopie, niet het register. Elke beslissing blijft op de pagina Beslissingen van het dashboard, die de tools
list_decisionsenget_decisionvan de MCP-server ook lezen: zoek een beslissing daar op met haarrequest_id.
Controleer de handtekening
Elke bezorging draagt Korzen-Signature: t=<Unix-seconden>,v1=<hex>: de HMAC-SHA256, onder je ondertekeningssecret, van t, een punt en de body precies zoals ontvangen. Bereken hem over de ruwe bytes, vóór elke JSON-parsing, vergelijk in constante tijd en weiger een t die meer dan 5 minuten van je klok afwijkt, zodat een eenmaal opgenomen bezorging later niet opnieuw kan worden gestuurd. 24 uur na een nieuw secret volgt een tweede v1, onder het oude: accepteer een bezorging zodra een v1 klopt.
import { createHmac, timingSafeEqual } from "node:crypto"; // raw: the body as received (a Buffer); header: Korzen-Signature function verify(raw, header, secret) { const parts = header.split(",").map((p) => p.split("=")); const t = Number(parts.find(([k]) => k === "t")?.[1]); if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > 300) return false; const mac = createHmac("sha256", secret).update(`${t}.`).update(raw).digest(); return parts.some(([k, v]) => { const got = Buffer.from(k === "v1" ? v ?? "" : "", "hex"); return got.length === mac.length && timingSafeEqual(got, mac); }); }
import hashlib, hmac, time def verify(raw: bytes, header: str, secret: str) -> bool: parts = [p.partition("=")[::2] for p in header.split(",")] t = next((v for k, v in parts if k == "t"), "") if not t.isdigit() or abs(time.time() - int(t)) > 300: return False mac = hmac.new(secret.encode(), f"{t}.".encode() + raw, hashlib.sha256).hexdigest() return any(k == "v1" and hmac.compare_digest(mac, v) for k, v in parts)
Antwoorden, en nieuwe pogingen
Antwoord binnen 5 seconden met een willekeurige 2xx: de body wordt niet gelezen. Al het andere, ook een redirect, of geen antwoord op tijd, is een mislukking, en de bezorging wordt 30 seconden later opnieuw geprobeerd, daarna telkens na twee keer zo lang, 8 pogingen in ongeveer een uur. Doe het trage werk nadat je hebt geantwoord.
Zolang je endpoint faalt, wachten nieuwe beslissingen, tot 100, en de rest wordt geteld in dropped. Een endpoint dat een uur lang faalt, wordt gepauzeerd: er wordt niets naartoe gestuurd, de beslissingen in de tussentijd worden niet verzonden, en elke eigenaar krijgt een e-mail. Set-up toont de status of de fout van de laatste bezorging; Hervatten stuurt weer vanaf de volgende beslissing.
Limieten
Elk abonnement stuurt tot een aantal beslissingen per uur: de gratis proefperiode 1.000, Starter 10.000, Pro 50.000, Scale 200.000. Daarboven worden de beslissingen geteld in de dropped van de volgende batch, en het volgende uur begint opnieuw. Een samenvatting wordt altijd verzonden. Een webhook telt niet mee voor je maandelijkse beslissingen.