Skip to main content

What this page covers

Every event produces one HTTPS POST 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 a 2xx status. Everything else is a failed attempt:
Answer 2xx as soon as the body is durably stored, then do the real work asynchronously. A handler that validates, writes to your database, and calls other systems before responding is the most common cause of timeouts and, eventually, a tripped circuit breaker.

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 from GET /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.
The delivery record never includes the payload, at any status and at any age. If you need to see exactly what was sent, use echo mode before the event happens — see Testing.
Example 200 response:

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 EXHAUSTED deliveries, the subscription is deactivated: isActive becomes false.
  • The subscription stays listed in GET /webhooks and GET /webhooks/{id}; it is not deleted.
  • Any delivery that ends DELIVERED resets 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.
While the subscription is inactive, no new deliveries are created for it, and 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} reports isActive: false and nobody at your firm sent PATCH {"isActive": false} or DELETE.
  • GET /webhooks/{id}/deliveries shows a run of EXHAUSTED deliveries with attempts: 5 and endpoint-side errors (HTTP 5xx, Request timed out, and so on) leading up to the moment it went inactive.
Monitor 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:
Reactivation is subject to the limit of 5 active subscriptions per firm: if five others are already active, the request fails with 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 on X-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. The GET endpoints remain the authority: Three things follow from this:
  • No backfill. A subscription only receives events that happen after it is created.
  • No replay. An EXHAUSTED delivery 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.
A periodic reconciliation against the 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.