ToolsReference
Webhook signature schemes compared
Every scheme here signs with HMAC-SHA256. They differ in what is signed, how the signature is encoded, how the secret becomes the HMAC key, whether a timestamp limits replays, and how the sender retries failed deliveries.
How each request is signed
| Scheme | Headers | Signed message | Encoding | Key | Timestamp tolerance |
|---|---|---|---|---|---|
| Standard Webhooks | Webhook-Id, Webhook-Timestamp, Webhook-Signature | HMAC-SHA256(key, id + "." + timestamp + "." + body) | Base64 | Signing secret, with the whsec_ prefix removed and the rest Base64-decoded | Not fixed by the spec. The official libraries allow 5 minutes. |
| Stripe | Stripe-Signature | HMAC-SHA256(secret, t + "." + body) | Hex | Endpoint signing secret, used as text | 5 minutes, using t= timestamp |
| GitHub | X-Hub-Signature-256 | HMAC-SHA256(secret, body) | Hex | Webhook secret, used as text | None. No timestamp is signed, so a signature never expires. |
| Shopify | X-Shopify-Hmac-Sha256 | Base64(HMAC-SHA256(client secret, body)) | Base64 | App client secret, used as text | None. No timestamp is signed, so a signature never expires. |
| Slack | X-Slack-Request-Timestamp, X-Slack-Signature | HMAC-SHA256(secret, "v0:" + timestamp + ":" + body) | Hex | Signing secret, used as text | 5 minutes, using X-Slack-Request-Timestamp |
| Cascadehook | Webhook-Id, Webhook-Timestamp, Webhook-Signature | HMAC-SHA256(key, id + "." + timestamp + "." + body) | Base64 | Signing secret, with the whsec_ prefix removed and the rest Base64-decoded | 5 minutes in the reference receivers. |
Retries and response deadlines
| Sender | Automatic retries | Response deadline | Source |
|---|---|---|---|
| Standard Webhooks | Recommends exponential backoff with jitter over several days. Each sender sets its own schedule. | Recommends a 15 to 30 second timeout. | Standard Webhooks specification |
| Stripe | Up to three days with exponential backoff in live mode; three times over a few hours in a sandbox. | Not stated. Stripe asks for a quick 2xx before any slow work. | Stripe: Receive events in your webhook endpoint |
| GitHub | None. Failed deliveries must be redelivered by hand or through the REST API. | 10 seconds. | GitHub: Handling failed webhook deliveries |
| Shopify | Up to eight times over four hours. The subscription is removed if failures persist. | 5 seconds. | Shopify: Troubleshooting webhooks |
| Slack | Up to three retries: almost immediately, after 1 minute, and after 5 minutes. | 3 seconds. | Slack: Events API |
| Cascadehook | Up to 8 attempts within 72 hours, with jittered backoff from about 15 seconds to 18 hours. 408, 429, 5xx, timeouts, and network errors are retried; Retry-After can lengthen a wait. | 30 seconds per attempt. | Cascadehook: Deliveries, retries, and replay |
Checked against each provider's documentation on November 7, 2026.
What the differences mean
- GitHub and Shopify sign only the body, so a captured request keeps a valid signature indefinitely. Deduplicate on the provider's delivery or event ID to resist replays.
- Stripe, Slack, and Standard Webhooks sign a timestamp with the body, so receivers can reject old requests. Keep the server clock synced with NTP.
- Standard Webhooks also signs the message ID, which doubles as an idempotency key for the receiver.
- Standard Webhooks decodes its whsec_ secret from Base64, while Stripe uses its whsec_ secret as text. Treating one like the other is a common cause of mismatches.
- Response deadlines range from 3 to 30 seconds. Acknowledge quickly and move slow work to a queue.
Debugging a mismatch? Each scheme has a free verifier that shows which input is wrong; start from the webhook tools.