Skip to main content
incaseof.law pushes events to a partner-controlled HTTPS endpoint whenever a claim’s lifecycle moves to a new stage. This lets you keep your local mirror of claim status in sync without polling. There are two event types: Your endpoint receives every type — there is no per-type subscription. New types may be added by extending the type enum (the envelope shape stays stable): acknowledge an event whose type you do not know with a 2xx and ignore it.

Setup

Webhooks are configured by an incaseof.law administrator on your behalf:
  1. The admin opens Settings → Partners → Webhooks and selects your organization.
  2. They paste your HTTPS endpoint URL and click Create webhook.
  3. incaseof.law generates a signing secret and displays it once. The admin forwards it to you over a secure channel.
  4. Store the secret in your config — you’ll use it to verify every incoming request (see Signature verification below).
The endpoint URL must use HTTPS. Plain HTTP is rejected at config time.

Request format

Every delivery is a POST to your configured URL with a JSON body.

Headers

Envelope

Every event uses the same envelope. The data field is shaped by type.

Signature verification

Each request includes an X-IcoLaw-Signature header of the form:
Where:
The secret is the value the admin shared with you at setup time.

Rules

  • Reject any request where |now - t| > 5 minutes (replay protection).
  • After a secret rotation only the new secret is valid — there is no overlap window (see Secret rotation).
  • Use a constant-time comparison (crypto.timingSafeEqual / hmac.compare_digest).
  • Verify against the raw request body bytes, not a re-serialised version — JSON key order matters for the HMAC.

Reference implementations


Secret rotation

Either party can request a secret rotation through the admin team. When rotated:
  1. A new signing secret is generated and shown to the admin once; the admin forwards it to you.
  2. From that moment every delivery is signed with the new secret only. There is no overlap window in which the previous secret still verifies.
  3. Deliveries your handler rejects until you have deployed the new secret are retried on the retry schedule — the last attempt comes about 7 h 12 min after the first (30 s + 2 min + 10 min + 1 h + 6 h). Agree a time for the rotation with the admin and deploy the new secret within that time, and nothing is lost.

Response & retry policy

Return any 2xx status code within 10 seconds to acknowledge the event. Anything else — non-2xx, timeout, connection error — is treated as a failure and triggers the next retry. Deliveries are sent by a job that runs every 30 seconds, so each time below can be up to 30 seconds later. After the 6th attempt fails, the delivery is marked failed and is visible to the incaseof.law team. It is not sent again — failed deliveries are not re-fired. Reconcile once a day through GET /openapi/claims (compare collection_stage with your mirror), so a missed event cannot leave your data behind.

While your webhook is disabled

No events are queued for a disabled webhook: stage changes in that time are not delivered later, and deliveries still pending when it was disabled are marked failed. After it is re-enabled, reconcile through the API.
Your handler must respond within 10 seconds. If you need to do heavy work, return 2xx immediately and process the event asynchronously (e.g. push the body to a queue and return 202).

Best practices

Deduplicate

The same X-IcoLaw-Delivery UUID (and the same body id) will arrive multiple times if any attempt fails and is retried. Persist the IDs you’ve already processed and short-circuit duplicates — pick one of the two and use it consistently:

Reconcile out-of-order events

Webhooks are eventually consistent. A retried older event can arrive after a newer one. Always reconcile by data.claim_id + data.changed_at rather than assuming arrival order:

Don’t trust the payload for sensitive fields

The webhook payload deliberately contains no debtor PII (no name, no email, no address, no claim amount). If your downstream logic needs those fields, call GET /openapi/claims/{id} with your API token after receiving the event. This keeps the webhook signal-only and avoids exposing debtor data on whichever network your receiver sits on.

Verify before you parse

Verify the signature before doing anything else with the body — including JSON parsing. Reject with 401 Unauthorized on signature mismatch.

What’s NOT included


Next steps

  • See the Collection Stage Events reference for the payload shape and example bodies.
  • Test the integration end-to-end by asking your admin to use the “Send test event” button in the webhooks tab — it fires a synthetic event with a fixed claim_id (00000000-…-000000000000) so you can easily filter test traffic in your handler.