whsec_ prefix, as a UTF-8 HMAC key. Do not base64-decode it.
Signature format
Kato sends:Node.js verifier
Save this asverify-kato-webhook.mjs in your server project. It uses Node’s built-in crypto module.
Read the raw body first
In a server handler that receives a standardRequest, collect the bytes before any JSON parser runs:
KATO_WEBHOOK_SECRET from the endpoint’s one-time creation response. Apply an appropriate request-body size limit in your HTTP server.
If your framework consumes the request body automatically, configure this route to preserve the original body before verification. Never verify against JSON.stringify(request.body).
Accept events reliably
After verifying the signature and parsing JSON:- Check the payload version, expected workspace, event type, and required fields.
- In persistent storage, atomically accept the event ID and enqueue its work. Use a unique constraint on
(workspaceId, id). - Return
2xxonce the event is durably queued, or when the same event was already accepted. - Process the queued job with retries and make downstream side effects idempotent too.
2xx response so Kato can retry. Finish the HTTP response within the 10-second delivery timeout.
Replays and ordering
The five-minute signature tolerance limits reuse of captured requests; it does not replace event deduplication. Kato signs delivery attempts with a current timestamp, so a replayed old event can have a valid new signature. Use the signed payload’sid as the deduplication key. Manual replay preserves that event ID but creates a new delivery ID. Automatic retries may reuse the same delivery ID.
Do not rely on arrival order. For synchronization, fetch current state where a public endpoint exists, or apply your own event-ordering policy.
Troubleshoot verification failures
Test valid, tampered, expired, and duplicated events before relying on a subscription. Never log the signing secret.