Overview
A subscription is one HTTPS URL that CaseXchange posts your firm’s events to. Subscriptions are created and managed only through the Public API — there is no webhooks screen for firms in the CaseXchange dashboard. You need an API key first; see the Quickstart for how to create one from Settings > API Keys. Every active subscription receives every event for your firm. There is no per-subscription event filter: branch on theevent field in your handler and ignore names you do not handle. The full list is in the Event catalog.
All paths on this page are relative to https://api.casexchange.com/api/public/v1.
Endpoints
The three list endpoints take
page (default 1) and limit (default 20, maximum 100).
This page covers the first five endpoints and the rules they share. The delivery log is covered on Anatomy of a delivery; test, echo mode and secret rotation are covered on Testing webhooks and Verifying signatures.
Create a subscription
POST /webhooks — requires a standard or full key.
Request body
The body is strict: any field not listed above is rejected with
400 validation_error. In particular, there is no events field — you cannot choose which events a subscription receives.
whsec_ followed by 64 hex characters. It signs every delivery to this subscription.
URL rules
The URL is validated when you create the subscription, again when you change it, and re-checked at every delivery. A URL that fails any check is rejected with400 invalid_webhook_url.
https://only. Plainhttp://is rejected.- Port 443 only. An explicit
:443is accepted; any other port is rejected. - No credentials in the URL (
user:password@hostis rejected). - The hostname must resolve, and every address it resolves to must be public. Loopback, private network, link-local, carrier-grade NAT, multicast, reserved, documentation and benchmarking ranges are all rejected, for IPv4 and IPv6 alike.
- Deliveries never follow redirects. A
3xxresponse counts as a failed attempt, so point the URL at the final endpoint.
localhost, 127.0.0.1 and LAN addresses cannot be used. Use a public HTTPS tunnel, a hosted request-capture service, or echo mode, which needs no endpoint at all — see Testing webhooks.
Read subscriptions
GET /webhooks and GET /webhooks/{id} — a read_only key is enough.
data as an array of subscription objects plus a pagination object (page, limit, total, totalPages). A limit above 100 is a 400 validation_error.
The subscription object
The secret is never part of this object. Only
POST /webhooks and POST /webhooks/{id}/rotate-secret return it.
isActive: true means the subscription itself is enabled. Deliveries also require that your firm has the Public API integration enabled on its account and that the firm is active. If either is not the case, events are not delivered — and your API keys stop working at the same moment, returning 403 public_api_not_enabled or 403 api_key_firm_inactive. The subscription itself is not deactivated, so deliveries resume once access is restored. See Known gaps.Update a subscription
PATCH /webhooks/{id} — requires a standard or full key.
Send at least one field. An empty body, or any field not listed, is a
400 validation_error.
200 with the updated subscription object (no secret).
Things to know:
- Deactivating stops queued deliveries too. Deliveries that are pending or waiting for a retry are cancelled rather than sent. Events that happen while the subscription is inactive are not delivered later — there is no backfill.
- Reactivating is subject to the active cap.
{ "isActive": true }when your firm already has five active subscriptions returns400 subscription_limit_reached. Deactivate or delete another subscription first. - You cannot change
urlwhile echo mode is on. The request returns400 echo_mode_active; callPOST /webhooks/{id}/echo/disablefirst, then change the URL. - Changing
urldoes not change the secret. The same secret keeps signing deliveries to the new URL.
Delete a subscription
DELETE /webhooks/{id} — requires a standard or full key. Returns 204 with no body.
404 not_found from every endpoint, and the subscription no longer counts toward the active cap.
Limits
Five active subscriptions per firm. Creating a sixth, or reactivating one withPATCH { "isActive": true } while five are active, returns 400 subscription_limit_reached.
Only subscriptions with isActive: true count. Deactivated and deleted subscriptions do not, so you can keep a paused subscription around without it consuming a slot.
Who can manage subscriptions
Admin keys, Salesforce
cxsf_* tokens and MCP-issued keys cannot use the webhook endpoints at any tier; they receive 403 wrong_scope.
Every key sees and manages only the subscriptions of its own firm. A subscription id that belongs to another firm returns 404 not_found — it looks exactly like an id that does not exist. If you manage several firms, use a key for each firm.
Rate limits
Webhook management calls count against your key’s rate limit like any other Public API call. Limits are per API key over a 60-second sliding window:
Across all keys of one firm the aggregate cap is 1000
GET and 200 writes per minute. Responses that pass the tier check carry X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (a 403 insufficient_tier refusal does not); exceeding a limit returns 429 rate_limit_exceeded with a Retry-After header. See Error Handling for backoff guidance.
Deliveries from CaseXchange to your endpoint are not rate-limited by these numbers and do not consume your key’s budget. Only the calls you make count.
Error codes
All errors use the standard envelope described in Error Handling: branch onerror.code, log error.message and meta.requestId.
Next steps
Anatomy of a delivery
Read the delivery log, status values and error strings for a subscription.
Verifying signatures
Check
X-CaseXchange-Signature and handle secret rotation safely.Testing webhooks
Send a
webhook.test delivery or capture real deliveries with echo mode.Reliability
Retry schedule, automatic deactivation after repeated failures, and retention.

