Webhook signatures prove an inbound event came from the service you named in the docs, not from someone who found a public URL. Without that check, a webhook is an open POST that can create invoices, reset passwords, or mark orders paid. Treat the signature as part of the contract, the same way you treat auth on your own API. If the check fails, drop the request before any business logic runs.

What Signatures Protect

They stop forged events, replayed captures, and partners who paste a sample body into curl and hit your production URL by mistake. A per-partner secret plus a timestamp also limits how long a stolen payload stays useful. They do not replace authentication on the routes your app calls out. They answer one question: did this sender prove they hold the secret you issued?

Design Choices That Matter

Sign the raw body bytes, not a re-serialized JSON object. Whitespace and key order will break a check that looks fine in a unit test and fails once a real partner sends traffic. Prefer HMAC-SHA256 over a hash you invented. Publish the header name, the encoding (hex or base64), and one worked example with a known secret and body.

Common Mistakes

Teams ship the endpoint first and add the check after the first bad event. That window is enough for a forged payment webhook or a fake user.deleted callback.

Safe Rollout

If the endpoint already has traffic, start in observe mode. Count mismatches per partner, fix clock skew and encoding bugs, then reject. New endpoints should reject from the first request.

What to Tell Partners

Put the header names, the signed string, and a copy-paste example next to the webhook URL. Ask them to send the same raw body they signed, and to retry with the same event id so you can dedupe. Build the check before the first partner goes live, document the bytes you sign, and treat a sustained failure spike as an incident, not a noisy log line.