Skip to main content

Start with the delivery log

Almost every webhook problem is answered by the delivery log. It shows, per delivery, the status, the number of attempts, the HTTP code of the last attempt (lastStatusCode) and the text of the last failure (lastError) — but never the payload.
Then find your symptom below. Each table lists the likely cause and what to check. See Anatomy of a delivery for the full delivery record and Reliability for the retry ladder.

I am not receiving any events

isActive: true means you have not disabled the subscription and the circuit breaker has not tripped. It does not prove deliveries are reaching your endpoint: while isEchoActive is true they are diverted to the capture endpoint instead of your URL, and an archived case emits nothing at all.

Deliveries show FAILED or EXHAUSTED

FAILED means the last attempt did not succeed and a retry is scheduled (nextRetryAt is set). EXHAUSTED means the delivery is final — either all 5 attempts failed, or it was cancelled because the subscription was disabled or deleted before it could be sent. Retries follow the ladder 30 s, 2 min, 10 min, 30 min — about 43 minutes end to end. Your endpoint must answer 2xx for an attempt to count as delivered. 3xx, 4xx, 5xx, a timeout and a TLS or connection failure are all failed attempts. Return 2xx as soon as the body is durably stored and do the real work asynchronously. Reliability lists every lastError string and what each one means. These three are the ones that get misread:
An EXHAUSTED delivery is not replayed. There is no redeliver endpoint. Treat it as a prompt to re-read the affected case from GET /sent-cases/{id}, GET /received-referrals/{id} or GET /status-updates. Ten deliveries in a row that exhaust their retries deactivate the subscription.
While you are fixing the endpoint, use POST /webhooks/{id}/test to queue a synthetic webhook.test delivery and confirm the fix from the delivery log rather than waiting for a real event. The subscription must be active (409 subscription_inactive otherwise).

Signature does not verify

The header is X-CaseXchange-Signature: t=<unix-seconds>,v1=<hex>. v1 is the hex HMAC-SHA256, keyed with your secret, over the string <t>.<rawBody> — the integer t from the header, a dot, and the exact request body bytes. See Verifying signatures for reference code.
Echo mode reports the signature check for you: enable it with POST /webhooks/{id}/echo/enable, trigger a test delivery, and read signatureValid, signatureDetails and rawBody from GET /webhooks/{id}/echo/captures. Comparing your computed digest with the v1= value inside signatureDetails.received (the full t=…,v1=… header we sent) over the captured rawBody, and reading signatureDetails.match, isolates a serialisation bug from a secret bug. See Testing webhooks.
For 24 hours after a rotation, echo captures also accept the previous secret for the signatureValid read-back check only. Outbound deliveries to your endpoint are always signed with the current secret alone, so a capture that says signatureValid: true right after a rotation does not mean your old secret still works.

Management calls fail

Every error uses the standard envelope — branch on error.code and keep meta.requestId for support. Managing subscriptions carries the full error table for these eleven endpoints, with the exact trigger for every code, and Error Handling covers the general pattern. Read those first; the rows below are only the ones where the code you get points somewhere other than the problem.

I get duplicates, events out of order, or my own changes

My subscription turned itself off

After 10 consecutive exhausted deliveries — that is, 10 deliveries in a row that each failed all 5 attempts — the circuit breaker deactivates the subscription. It stays listed with isActive: false. A successful delivery at any point resets the count. While the subscription is inactive, no deliveries are created for it. Events that occur during that time are not delivered later — there is no backfill — so reconcile from the API once you are back.
1

Find the failure pattern

Read GET /webhooks/{id}/deliveries and look at lastError and lastStatusCode on the EXHAUSTED records. Reliability maps every lastError value to its cause, and Deliveries show FAILED or EXHAUSTED covers the three that are easiest to misread.
2

Fix the endpoint

Ten exhausted deliveries in a row means the endpoint kept failing across full retry ladders of about 43 minutes each, not a single blip. Confirm it is publicly reachable on port 443, returns 2xx within the timeouts, does not redirect, and resolves to public addresses.
3

Re-enable the subscription

This is subject to the 5-active cap; 400 subscription_limit_reached means another subscription must be deactivated or deleted first.Reactivating also clears the consecutive-exhausted count, so the subscription starts clean: it takes another 10 exhausted deliveries in a row to trip the breaker again.
4

Confirm with a test delivery

Poll GET /webhooks/{id}/deliveries until the webhook.test record reads DELIVERED.
5

Reconcile

Re-read the cases that changed while the subscription was off from GET /status-updates, GET /sent-cases and GET /received-referrals.
Deleting the subscription instead of re-enabling it also works, but a new subscription gets a new id and a new secret, and it too starts with no backfill.

Contacting support

Email help@casexchange.com. Include the following so the team can trace the delivery end to end: Never send your API key, your signing secret or your echo token. Support does not need them and cannot read the secret back either — if you believe one is compromised, rotate it with POST /webhooks/{id}/rotate-secret or generate a new key.

Reliability

Retry ladder, delivery statuses, circuit breaker and retention.

Testing webhooks

Test deliveries and echo mode for debugging without a public endpoint.

Verifying signatures

Reference verifiers and secret rotation.

Known gaps

Changes that produce no event, and how to detect them.