Guides
Signed webhooks for everything else
Chosen ledger events to your own endpoint, signed with HMAC-SHA256 over a timestamp, with a replay window and a delivery id.
For a tool with no card here, register a webhook (the Signed webhook card opens SIEM and webhooks, under Settings, For engineers). It is the same mechanism the SIEM export describes, in short:
- Signature.
immiscible-signature: t=<unix seconds>,v1=<hex>, wherev1is HMAC-SHA256 of<t>.<raw body>with the webhook’s secret (shown once). - Replay window. Refuse a delivery whose
tis more than 300 seconds from your clock. - Replays inside the window. Each delivery carries
immiscible-delivery: dlv_...; keep the ids you have seen for ten minutes and drop repeats. - Events. A list of ledger kinds (for example
agent_freeze,agent_incident,accounting_export), or["*"]; as OCSF 1.3 or the native record. A kind the ledger never writes is refused, with the closest real one named. - https only, never a private address.
#Retries and resends
Two different things, and your endpoint can tell them apart:
| Automatic retry | Manual resend | |
|---|---|---|
| When | the endpoint did not answer 2xx | a person (or your script) asks for it, with POST .../deliveries/:did/resend |
| How many | up to five attempts in all, waiting 2, 8, 32 and 128 seconds between them | one attempt, at once, not retried |
immiscible-delivery | the same id on every attempt, so drop it if you already processed it | a new id; the delivery list shows its resentFrom, the id it repeats |
| Body | the same | the same, so its record.id (or OCSF metadata.uid) is the same |
| Signature | signed afresh with a new t each attempt | signed afresh |
So: drop a repeated immiscible-delivery id as a duplicate; treat a new id carrying a record you have already seen as a deliberate resend, and decide by the record’s own id whether to process it again.
#Changing a webhook
PATCH .../webhooks/:id changes url, events or format and keeps the secret. POST .../webhooks/:id/rotate-secret makes a new secret, shown once; the old one stops at once, retries included, so update your verifier first or expect a few refusals. Each delivery in the list keeps the headers it was sent with (request.headers: the signature, never the secret), the SHA-256 of its body, and the status and first 1 KB of what your endpoint answered.
Verification code in TypeScript and Python is in Verifying a delivery.