What this page covers
Every event produces one HTTPSPOST per active subscription. This page describes what happens to that POST when your endpoint is slow, down, or answers with the wrong status: how many times CaseXchange retries, how long it waits, what the delivery log shows you, and when a failing endpoint gets its subscription switched off.
For the request itself (headers, body, envelope fields) see Deliveries. For checking the signature see Verify Signatures.
What counts as a successful attempt
An attempt is successful when your endpoint answers with a2xx status. Everything else is a failed attempt:
The retry ladder
A delivery gets 5 attempts in total. After a failed attempt the next one waits progressively longer:
After the fifth failure the delivery is marked
EXHAUSTED and is never retried. There is no replay or redeliver endpoint, so an exhausted delivery has to be recovered by reconciling against the GET endpoints (see Webhooks are a signal below).
Every retry resends the same delivery with the same X-CaseXchange-Delivery header, so your endpoint can deduplicate across attempts. Each attempt is signed with the secret that is current at that moment: if you rotate the secret while a delivery is on the ladder, its later attempts are signed with the new secret. See Verify Signatures for what that means for a rotation.
Delivery statuses
Each delivery moves through these statuses. You can read them fromGET /webhooks/{id}/deliveries.
The delivery record
GET /webhooks/{id}/deliveries returns the delivery log for one subscription, newest first, paginated with page (default 1) and limit (default 20, max 100). It needs a read_only key or higher.
lastError reference
lastError is null for a DELIVERED delivery and for a PENDING one that has not been attempted yet.
The circuit breaker
If your endpoint keeps failing, CaseXchange stops sending to it rather than queueing retries forever.- After 10 consecutive
EXHAUSTEDdeliveries, the subscription is deactivated:isActivebecomesfalse. - The subscription stays listed in
GET /webhooksandGET /webhooks/{id}; it is not deleted. - Any delivery that ends
DELIVEREDresets the consecutive count to zero. Ten exhausted deliveries with a single success between them do not trip the breaker. - Deliveries cancelled because you disabled or deleted the subscription do not count toward the breaker.
POST /webhooks/{id}/test is refused with 409 subscription_inactive. Events that occur during that time are not delivered later: there is no replay.
How to tell the breaker tripped
There is no separate flag. You know it was the breaker when:GET /webhooks/{id}reportsisActive: falseand nobody at your firm sentPATCH {"isActive": false}orDELETE.GET /webhooks/{id}/deliveriesshows a run ofEXHAUSTEDdeliveries withattempts: 5and endpoint-side errors (HTTP 5xx,Request timed out, and so on) leading up to the moment it went inactive.
isActive on your subscriptions as part of your health checks. If isActive is true and deliveries are still not arriving, the breaker is not the cause; see Troubleshooting.
Re-enabling after a trip
Fix the endpoint first. Then reactivate the subscription:400 subscription_limit_reached. Reactivating also resets the consecutive-exhausted count, so the subscription starts with a clean slate.
Send POST /webhooks/{id}/test after reactivating and watch GET /webhooks/{id}/deliveries for a DELIVERED result before you rely on the subscription again.
Disabling or deleting a subscription
PATCH /webhooks/{id} with {"isActive": false} and DELETE /webhooks/{id} both stop deliveries that are already queued, not just future ones. Pending deliveries and deliveries waiting for a retry are cancelled instead of sent: they move to EXHAUSTED with lastError set to Subscription is no longer active (deleted or disabled) — delivery cancelled.
Those cancelled deliveries do not count toward the circuit breaker. If you disable a subscription for maintenance and later reactivate it, reconcile with the GET endpoints for anything that happened in between.
At-least-once delivery and concurrency
CaseXchange delivers at least once. A retry can arrive after the same attempt already reached you (for example when your endpoint stored the body but timed out before responding), so deduplicate onX-CaseXchange-Delivery.
Deliveries to one subscription are not serialised. Two events that happen close together are sent in parallel, and a retried delivery can trail a later one by up to about 43 minutes. Expect concurrent POSTs to your endpoint, do not assume order, and apply the delivery with the greatest occurredAt. Deliveries covers ordering and duplicate handling in detail.
Retention
Stored payloads are never exposed through the API at any point during their retention; the periods above describe internal storage only. Pull the delivery log into your own systems if you need history beyond 180 days.
Webhooks are a signal
Treat a delivery as a prompt to fetch current state, not as the state itself. TheGET endpoints remain the authority:
GET /status-updatesfor status history — see Status Updates examples.GET /sent-casesandGET /received-referralsfor the current state of each referral — see Sent Cases examples and Received Referrals examples.GET /referralsfor referral records.
- No backfill. A subscription only receives events that happen after it is created.
- No replay. An
EXHAUSTEDdelivery cannot be resent, and there is no endpoint to request one. - Not every write produces an event. Some paths into CaseXchange, such as historical CSV imports, create or update cases without emitting anything — see Known Gaps.
GET endpoints covers exhausted deliveries, breaker trips, and the gaps in one pass — for example, pull GET /status-updates hourly with dateFrom set to your last run, and page GET /sent-cases?sort=updatedAt&order=desc until you reach records you have already seen.
Next steps
Deliveries
Headers, the envelope, ordering, and deduplication.
Verify Signatures
Check
X-CaseXchange-Signature and handle secret rotation.Testing
Test events and echo mode for inspecting what was sent.
Troubleshooting
Nothing arriving, signature failures, and unexpected
isActive: false.
