HMAC (Hash-based Message Authentication Code) is a cryptographic primitive that combines a hash function with a shared secret to produce a signature over some data. The signature proves two things at once: the sender knew the secret, and the data wasn't modified in transit.
In webhooks, HMAC is the most common way providers sign payloads. The sender computes HMAC(secret, body) and includes it in a header. The receiver recomputes the signature on the raw body and compares constant-time with the header value.
Why webhooks use HMAC
Three properties matter:
- Symmetric. Both sides share the same secret. No key infrastructure (PKI, certificate authorities, key servers) needed.
- Fast. HMAC-SHA256 is microseconds per request even at high throughput.
- Standard. Every language ships HMAC primitives in its standard library.
The trade-off is that the secret must be shared in advance and rotated periodically. For machine-to-machine webhook flows, this is usually acceptable.
Verification rules
Three things must be true for HMAC verification to be safe:
- Read the raw body before parsing. Parsing JSON normalizes whitespace; the signature won't match.
- Compare constant-time. Naive
==comparison is vulnerable to timing attacks; use a constant-time comparison function. - Verify a timestamp. Most providers include a timestamp in the signed string to prevent replay attacks; reject anything older than a few minutes.
Common variants in webhooks:
- HMAC-SHA256 — Stripe, GitHub, Shopify, Slack, Standard Webhooks
- HMAC-SHA1 — Twilio (legacy)
- Ed25519 — Discord interactions (asymmetric, not HMAC, but same role)
- JWT with ES256 — Plaid (asymmetric)
For the verification model in practice: Verify provider webhooks.
Related terms
- Webhook
An HTTP request one service sends to another to notify it that something happened.
Read - Payload
The body of an HTTP request — for webhooks, almost always JSON.
Read - Provider pack
Preconfigured inbound verification for a known provider — Stripe, GitHub, Clerk, Slack, or Resend.
Read - Attempt
One HTTP request within a delivery's retry chain.
Read