KaizoCoreKaizoCore docs

Webhooks

Get a signed HTTP callback whenever KaizoCore makes a matching decision.

Setting up a webhook

From the dashboard's Webhooks page, or via the API:

POST /v1/webhooks
{ "url": "https://yourapp.com/webhooks/kaizocore", "event_types": ["BLOCK", "REVIEW"] }

The response includes a signing secret — shown in full exactly once. Store it; you'll need it to verify deliveries.

Event types

Webhooks subscribe to the same decision vocabulary used everywhere else: ALLOW, ALLOW_WATCH, SOFT_CHALLENGE, REVIEW, BLOCK. Most integrations only want BLOCK and REVIEW — the cases that need a downstream action — but any combination is valid.

Payload

{
  "event": "BLOCK",
  "request_id": "a6af22a9-3a6c-42e8-b28f-3e3184093d79",
  "decision": "BLOCK",
  "score": 91.2,
  "reasons": ["tor_ip", "bot_user_agent"],
  "timestamp": 1752480000
}

Verifying the signature

Every delivery includes:

X-KZ-Webhook-Signature: <hex HMAC-SHA256 of the raw request body>
X-KZ-Webhook-Ts: <unix timestamp>

Compute HMAC-SHA256(your_signing_secret, raw_body) and compare it to the header — the same signing pattern used throughout KaizoCore's own request authentication (see The /v1/decide API).

Delivery guarantees

  • Retries: up to 4 attempts total, with 2s / 4s / 8s backoff between them.
  • Unhealthy marking: after 10 consecutive failed deliveries, a webhook is marked unhealthy and stops receiving new deliveries — surfaced clearly in the dashboard rather than retried silently forever. Fix the endpoint and it resumes on the next matching event.
  • At-least-once: a delivery that times out on your end but actually succeeded may be retried — make your endpoint idempotent using request_id.

Known scope limit

Delivery currently runs in-process per decision rather than through a durable job queue — fine at typical traffic volumes, but a detail worth knowing if your endpoint is occasionally slow to respond: a slow endpoint extends how long that one decision's delivery goroutine stays open, up to the total retry window (~14 seconds), not indefinitely.

Managing webhooks

GET    /v1/webhooks              # list your webhooks (secrets masked)
DELETE /v1/webhooks/{id}
GET    /v1/webhooks/deliveries   # recent delivery attempts + status, across all your webhooks

On this page