Skip to main content

Delivery payloads

For http webhooks the body is the raw event:
type is policy_deny, anomaly, or session_suspended. For policy_deny events from the LLM gateway, toolName holds the model name, serverId is the connection slug, and sessionId is an empty string. Slack and Discord webhooks receive a formatted message instead. Delivery is fire-and-forget: a failed delivery is not retried.

Signing

http webhooks are signed. Slack and Discord webhooks are not, because they cannot verify custom headers. Each delivery carries two headers: The HMAC key is the full secret string, including the whsec_ prefix. The signed message is the timestamp, a dot, then the exact raw request body bytes. The secret is shown once, when you create the webhook and when you rotate it. Store it then. In the dashboard, rotate from Settings → Webhooks with the Rotate signing secret row action.
HTTP webhooks created before signing was added are unsigned until you rotate their secret. Rotation takes effect immediately, with no grace period. Update your receiver right after you rotate.
When you verify a delivery:
  1. Compute the HMAC over the raw body, not a re-serialized JSON object.
  2. Reject timestamps more than 5 minutes from your clock, to block replays.
  3. Compare signatures with a constant-time function.

Rotate a secret

The response is the webhook object plus a one-time secret. It returns 400 for Slack and Discord webhooks.