For the complete documentation index, see llms.txt. This page is also available as Markdown.

Verify a webhook signature

Anyone can find your webhook URL and POST a fake payload to it. Signature verification is how your server tells a genuine InsurGrid delivery apart from an imposter — before you trust a single field in the body.

New term — signature verification: recomputing a cryptographic fingerprint of an incoming request on your side, using a secret only you and the sender know, and checking it matches the one the sender attached. A match proves the request came from the sender and wasn't altered in transit.

This is the default auth scheme for every InsurGrid webhook (see Register your endpoint & choose an auth type if you've configured a different one — the rest of this page doesn't apply to those).

How it works

Every delivery carries an x-signature header: an HMAC-SHA256 of the raw request body, computed with your signing secret (from the API section — see Authentication & tokens), hex-encoded.

New term — HMAC-SHA256: a hashing algorithm that mixes a secret key into the fingerprint it produces. Recomputing the same hash of the same bytes with the same secret always yields the same output — so if your recomputed value matches the header, the request is genuine.

The check has three parts:

  1. Get the exact raw bytes InsurGrid sent — not the parsed-and-re-serialized JSON.

  2. Compute HMAC-SHA256(raw_body, signing_secret), hex-encoded.

  3. Compare it to the x-signature header, using a timing-safe comparison. If it doesn't match, reject the request with 401.

⚠️ The raw-bytes footgun

The single most common first-time bug: computing the HMAC over JSON.stringify(parsedBody) (or Python's json.dumps(parsed)) instead of the bytes as received.

Parsing JSON and re-serializing it does not reliably reproduce the original bytes — key order, whitespace, and number formatting can all differ. Even a one-byte difference produces a completely different hash, so the check fails even though the request is perfectly genuine.

The fix: capture the raw body before your framework parses it, and hash those bytes — never the parsed object.

Verify it · server

Node (Express):

Python (Flask):

Notice both handlers read the raw body before parsing JSON, verify first, and only trust req.body / request.get_json() after the check passes.

Why timing-safe comparison

A plain expected === signature string comparison returns as soon as it finds the first mismatched character — which means it takes slightly longer to reject a guess that gets more characters right. An attacker who can measure that timing difference can, in theory, guess a valid signature one byte at a time. crypto.timingSafeEqual (Node) and hmac.compare_digest (Python) take the same amount of time regardless of where the mismatch is, closing that side channel. Always use them for this comparison instead of === or ==.

Reject on mismatch

If the signature doesn't match, return 401 and stop — don't process the body, don't log it as a near-miss with full contents, and don't fall back to trusting the request anyway. A mismatch means either the secret is wrong on one side (check you copied the current one) or the request isn't from InsurGrid.

Using a different scheme instead

If signature verification doesn't fit your setup, you can configure your endpoint to authenticate deliveries a different way — HMAC-SHA256 with your own secret, Basic auth, a bearer token, or a static API-key header. See Register your endpoint & choose an auth type for all five options and when each makes sense. A static credential is simpler to check but, unlike an HMAC, it doesn't prove the body wasn't altered in transit.