Skip to main content
Your webhook endpoint is a public URL. Anyone who finds it can 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.
Verify on every request, and verify against the raw request body — the exact bytes Ionic sent. If your framework parses JSON and you re-serialize it, key order and whitespace change and the signature will never match. Capture the raw body before any JSON middleware runs.

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:
The bytes after the 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

  1. Reject stale deliveries. If webhook-timestamp is more than five minutes from now, stop — this blocks replays of a captured request.
  2. Build the signed content by joining the id, timestamp, and raw body with periods:
  3. Compute base64(HMAC_SHA256(secret_bytes, signed_content)), where secret_bytes is the base64-decoded part of whsec_….
  4. Compare your result against each signature in webhook-signature using 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’s type 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.