Skip to main content
Verify every webhook before parsing or processing its payload. Use the complete endpoint signing secret, including the whsec_ prefix, as a UTF-8 HMAC key. Do not base64-decode it.

Signature format

Kato sends:
The digest is HMAC-SHA256 over:
The timestamp is Unix time in seconds. The body must be the original bytes received over HTTP; parsing and re-serializing JSON changes those bytes and can invalidate the signature.

Node.js verifier

Save this as verify-kato-webhook.mjs in your server project. It uses Node’s built-in crypto module.
This rejects malformed signatures and timestamps more than five minutes in either direction, and compares equal-length digests in constant time. Keep your server clock synchronized.

Read the raw body first

In a server handler that receives a standard Request, collect the bytes before any JSON parser runs:
This snippet belongs inside your request handler, after importing the verifier. Configure 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:
  1. Check the payload version, expected workspace, event type, and required fields.
  2. In persistent storage, atomically accept the event ID and enqueue its work. Use a unique constraint on (workspaceId, id).
  3. Return 2xx once the event is durably queued, or when the same event was already accepted.
  4. Process the queued job with retries and make downstream side effects idempotent too.
A durable queue or transactional outbox prevents a crash between recording an event and scheduling its work. An in-memory set loses deduplication state on restart. Recording an event as processed before its work is safely queued can lose work. If durable acceptance fails, return a non-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’s id 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.