Event types
| Event | Fires when |
|---|---|
budget.threshold_reached | A budget’s spend crosses its configured threshold (default 80%) within the current billing period |
policy.violation | A gateway policy blocks a request — a guardrail rejection, a model-allowlist 403, etc. |
anomaly.usage_spike | The last hour’s request volume is at least 3× the trailing 24-hour baseline (minimum 20 requests, to avoid noise on low-volume tenants) |
Creating an endpoint
type is required — generic or slack. The response includes a per-endpoint signing secret (whsec_...) — store it; it’s needed to verify deliveries.
Full endpoint references: GET /v1/webhook-endpoints, POST /v1/webhook-endpoints, PATCH /v1/webhook-endpoints/{id}, DELETE /v1/webhook-endpoints/{id}.
Endpoint URLs are validated at create/update time and again at delivery time (SSRF guard): private/loopback/link-local addresses and non-HTTPS URLs are rejected in production, and 3xx redirects are never followed. This also blocks DNS rebinding — a hostname that resolves to a public address at config time but a private one at delivery time is caught on the re-check.
Delivery format
Ageneric endpoint receives the full event envelope:
slack endpoint instead receives the native Slack incoming-webhook shape — { "text": "*Wardin · budget.threshold_reached*\n<message>" } — so it renders directly in the channel with no adapter needed.
Every POST also carries X-Wardin-Delivery-Id (the delivery’s id — use it to de-duplicate on your side) and X-Wardin-Event (the event type). generic endpoints additionally carry the signature header described below; Slack’s incoming-webhook receiver ignores custom headers, so signed endpoints are generic only.
Verifying the signature
generic deliveries carry an X-Wardin-Signature header in the form t=<unix_seconds>,v1=<hex_hmac> — a timestamp and an HMAC-SHA256 digest of <timestamp>.<raw_body>, keyed with your endpoint’s whsec_... secret. Binding the timestamp into the digest (rather than signing the body alone) lets you reject replayed deliveries outside a tolerance window.
Delivery guarantees
Delivery is at-least-once: an event is persisted as a delivery row before any network call is attempted, so a crash between “event happened” and “HTTP POST sent” can never lose it. A failed attempt (non-2xx, timeout, or transport error) is retried with exponential backoff (capped at one hour) until it either succeeds or exhausts the endpoint’s retry budget, at which point it’s dead-lettered — the endpoint’s status still reflects the last outcome, but that specific delivery stops retrying. Because delivery is at-least-once, not exactly-once, treatX-Wardin-Delivery-Id as an idempotency key if your receiver can’t tolerate a duplicate.
Test send
Send a syntheticwebhook.test event to one endpoint and get an immediate pass/fail result — useful for confirming a new endpoint and secret are wired correctly before relying on a real event:
POST /v1/webhook-endpoints/{id}/test.