Skip to content

Webhook signature verifier

Why is my webhook signature failing?

Paste a delivery's headers, raw body and signing secret. The tool recomputes the HMAC-SHA256 exactly as Stripe, Shopify, LemonSqueezy, Polar and Dodo Payments do, shows the signed content and both digests side by side, and names the mismatch.

Runs entirely in your browser. The secret never leaves this tab.

Signature scheme

Sent by Polar, Dodo Payments, Svix-based senders.

Stays in this tab. Nothing on this page makes a network request.

What this scheme signs

webhook-id + "." + webhook-timestamp + "." + raw body, base64-encoded. The secret after whsec_ is base64 and is decoded first; Polar signs with the raw string instead, so both readings are tried.

Fill in the fields and verify, or load a working example to see a passing result first.

How each processor signs a webhook

SchemeHeaderSigned contentEncodingReplay window

Standard Webhooks

Polar, Dodo Payments, Svix-based senders

Docs

webhook-id

webhook-timestamp

webhook-signature

webhook-id + "." + webhook-timestamp + "." + raw body, base64-encoded. The secret after whsec_ is base64 and is decoded first; Polar signs with the raw string instead, so both readings are tried.base645 minutes

Stripe

Stripe

Docs

Stripe-Signature

The t= timestamp + "." + raw body, hex-encoded, keyed with the whsec_ secret as a UTF-8 string (not decoded).hex5 minutes

LemonSqueezy

LemonSqueezy

Docs

X-Signature

The raw body alone, hex-encoded, keyed with the secret as a UTF-8 string. No timestamp, so there is no replay window.hexNone

Shopify

Shopify

Docs

X-Shopify-Hmac-SHA256

The raw body alone, base64-encoded, keyed with the secret as a UTF-8 string. No timestamp.base64None

Every scheme is HMAC-SHA256 with a shared secret; they differ only in what is hashed, how the digest is encoded, and whether a timestamp is folded in. Verify the raw body in every case. The moment a framework parses the JSON for you, the bytes are gone.

Common questions

Is my signing secret sent to NumberHill?

No. The page makes no network requests after it loads. The HMAC is computed in your browser with the Web Crypto API, and the secret, headers and body live only in this tab's memory. Reload the page and they are gone.

The signature fails but my secret is right. What is the usual cause?

The body was changed before hashing. Most frameworks parse JSON on the way in, and re-serialising it reorders keys or drops whitespace, so the bytes you sign are not the bytes the sender signed. Read the raw request body (a string or buffer) and verify that, before any JSON parsing.

Why does Standard Webhooks try two keys?

The specification says the secret after whsec_ is base64 and should be decoded before use, which is what Dodo Payments does. Polar lets you type any string as the secret and signs with its raw bytes. The tool tries both and tells you which one matched, so you know which reading your own code must use.

What does the timestamp warning mean?

Stripe and Standard Webhooks include the delivery time in the signed content and expect receivers to reject anything older than about five minutes, so a captured request cannot be replayed later. A signature can therefore be cryptographically correct and still be rejected in production. The tool reports both facts separately.

Which processors does this cover?

Stripe, Shopify, LemonSqueezy, and every sender using the Standard Webhooks specification, which includes Polar, Dodo Payments and anything delivered through Svix. These are the five processors NumberHill attributes revenue from, and the schemes are the same ones its webhook endpoints verify.