POST to it. Before you act on an event, verify its signature — this proves the request came from Ionic and that the body wasn’t altered in transit.
Ionic signs deliveries with the open Standard Webhooks scheme: an HMAC-SHA256 over the message id, timestamp, and raw body, keyed by your endpoint’s signing secret.
What you receive
Each delivery carries three headers:
The
webhook-signature value can contain more than one signature (space-separated) — for example during secret rotation, when both the old and new secrets sign the body. Treat a match against any listed signature as valid.
Your signing secret
When you create an endpoint (or rotate its secret), Ionic returns a secret that looks like:whsec_ prefix are base64-encoded. Decode them to get the raw key for the HMAC. Store the secret somewhere only your server can read it; Ionic shows it once and never returns it again.
The algorithm
- Reject stale deliveries. If
webhook-timestampis more than five minutes from now, stop — this blocks replays of a captured request. - Build the signed content by joining the id, timestamp, and raw body with periods:
- Compute
base64(HMAC_SHA256(secret_bytes, signed_content)), wheresecret_bytesis the base64-decoded part ofwhsec_…. - Compare your result against each signature in
webhook-signatureusing a constant-time comparison. Any match means the delivery is authentic.
Verify it
Prefer a maintained Standard Webhooks library in your language over hand-rolled code where you can — it handles the timestamp window, multiple signatures, and constant-time comparison for you. The implementations above show exactly what such a library does, for languages or environments where you’d rather not add a dependency.
Getting the raw body
The signature is computed over the bytes on the wire, so you must read the body before JSON parsing. A few common frameworks:After verification
Once verified, parse the body and handle the event. The envelope’stype tells you what happened and data.object carries the resource. Because delivery is at-least-once, your handler must be idempotent — dedupe on the envelope id.
