Webhook events
Every event Cascades delivers, the X-Cascades-Signature scheme, and the retry schedule.
How delivery works
Create a subscription with POST /webhook_subscriptions and store the signing_secret it returns โ it is shown exactly once. Every event is an HTTPS POST to your URL with a JSON envelope { id, event, created_at, data }. An empty event_filter subscribes to every event.
Signature
Each delivery carries X-Cascades-Signature: t=<unix>,v1=<hex>, where v1 is HMAC-SHA256(signing_secret, "<t>.<raw body>"). Verify against the raw request body, compare in constant time, and reject timestamps outside your tolerance (60 seconds is a reasonable cap). During the 24-hour grace window after rotate_secret, a v0= value signed with the previous secret rides alongside v1=; accept either.
Headers
| Header | Purpose |
|---|---|
X-Cascades-Event | Event type, e.g. adverse_action.completed. Branch on this.
|
X-Cascades-Delivery | Delivery row id. Use it as your idempotency / de-duplication key. |
X-Cascades-Signature | t=<unix_ts>,v1=<hex_hmac> โ plus v0= during a rotation grace window.
|
User-Agent | Cascades-Webhook/<version>.
|
Retries
Any non-2xx response, connection error or timeout (5s connect / 10s read) counts as a failure. Cascades retries after 1m, 5m, 30m, 2h, 8h, 24h; after six attempts the delivery is marked dead. Re-enqueue a failed or dead row with POST /webhook_deliveries/{id}/retry. Return 2xx as soon as the event is durably stored, defer heavy work, and dedupe on X-Cascades-Delivery.
Events
| Event | When it fires |
|---|---|
execution.completed |
An execution reached `completed` |
execution.failed |
An execution reached `failed` |
adverse_action.triggered |
An adverse-action notice was sent (or the pre-adverse waiting period started) |
adverse_action.completed |
The adverse-action flow finished |
adverse_action.failed |
A notice could not be delivered |
adverse_action.retry_scheduled |
A post-adverse notice delivery failed and a retry was scheduled |