Skip to content

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>, where v1 is HMAC-SHA256 of <t>.<raw body> with the webhook’s secret (shown once).
  • Replay window. Refuse a delivery whose t is 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 retryManual resend
Whenthe endpoint did not answer 2xxa person (or your script) asks for it, with POST .../deliveries/:did/resend
How manyup to five attempts in all, waiting 2, 8, 32 and 128 seconds between themone attempt, at once, not retried
immiscible-deliverythe same id on every attempt, so drop it if you already processed ita new id; the delivery list shows its resentFrom, the id it repeats
Bodythe samethe same, so its record.id (or OCSF metadata.uid) is the same
Signaturesigned afresh with a new t each attemptsigned 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.