Webhooks
Subscribe an HTTPS endpoint to organisation events, verify the signed payloads, and understand delivery and retries.
Webhooks push organisation events to an HTTPS endpoint you control, so you can react to changes without polling. Each subscription is signed with its own secret and delivered on a best-effort, retrying schedule.
Create a subscription
Create a subscription with a webhooks:write key whose member is an admin. A read scope is deliberately not enough to create one: minting a signing secret is a write action.
POST /api/v1/webhooks/subscriptions HTTP/1.1
Authorization: Bearer ck_live_a1b2c3d4_...
Content-Type: application/json{
"url": "https://hooks.example.com/cadence",
"event_types": ["time_entry.created", "time_entry.updated"]
}The URL must be HTTPS and resolve to a public host: private and local addresses are refused. The response returns the new subscription and, exactly once, its signing_secret (a whsec_ value). Store it now; it is never shown again.
{
"data": {
"id": "7c6b5a49-3827-4615-9304-2c1b0a9f8e7d",
"url": "https://hooks.example.com/cadence",
"event_types": ["time_entry.created", "time_entry.updated"],
"status": "active",
"signing_secret": "whsec_Zk9...redacted..."
}
}Manage subscriptions
integrations:read). The signing secret is never returned here.status to active, paused, or disabled (webhooks:write).webhooks:write). Subscriptions are never hard-deleted, so the audit trail is kept.Delivery payload
Cadence delivers each event as a JSON POST to your URL. The body carries the event id, type, the organisation it belongs to, when it occurred, and the changed record under data:
{
"id": "0e1d2c3b-4a59-4687-9105-2d3e4f5a6b7c",
"type": "time_entry.created",
"org_id": "abcdef01-2345-6789-abcd-ef0123456789",
"occurred_at": "2026-06-17T10:30:01Z",
"data": { /* the changed record */ }
}Verify authenticity
Every delivery carries a Cadence-Signature header so you can confirm it really came from Cadence and was not tampered with. The header has the form t=<timestamp>,v1=<hmac>, where the HMAC is SHA-256 over <timestamp>.<raw-body>keyed by your subscription's signing secret.
- Read the headerParse the
t(timestamp) andv1(signature) values fromCadence-Signature. - Recompute the HMACCompute
HMAC-SHA256(secret, t + "." + rawBody)over the exact raw request body, and compare it tov1with a constant-time comparison. - Check the timestampReject the delivery if
tis too far from now, so a captured request cannot be replayed later.
2xx quickly and do your real work asynchronously: deliveries time out after a few seconds. Because delivery is at-least-once, the same event id can arrive more than once after a retry; treat the id as an idempotency key on your side.Retries and delivery
A background worker drains pending events roughly every minute. A non-2xx response, a timeout, or an unreachable endpoint is retried on an exponential backoff (a few attempts over roughly the next several hours) before the event is marked exhausted. Repeated failures count against the subscription, and you can pause or disable it at any time. Each delivery is recorded once per attempt, so your audit and ours line up.