Delivery behaviour
Read from the vendor's own documentation, last checked 2026-08-06. Where the vendor states no number, this table carries none.
| Response budget | not published by the vendor Stripe publishes no figure. Its docs say only to return 2xx before any slow logic. 20s is the community working number |
|---|---|
| Retry | Exponential backoff for up to 3 days in live mode. Sandbox: 3 attempts over a few hours |
| Gives up | Endpoint disabled after continued failure, with an email first. Events stay in the Events API for 30 days |
| Source | https://docs.stripe.com/webhooks |
Signature
Verified against a working verifier proven by a test suite, not read from documentation. Last verified 2026-08-20.
| Algorithm | HMAC-SHA256 |
|---|---|
| Signed payload | {timestamp}.{body} |
| Encoding | hex |
| Header | stripe-signature = t=X,v1=hex |
| Tolerance | 5 min |
Verify it
The raw request body, byte for byte, before any JSON parsing. Every scheme on this page breaks the moment a framework re-serializes the payload.
const crypto = require("node:crypto");
// stripe-signature: "t=1700000000,v1=abc..." — HMAC-SHA256 over "{t}.{body}"
const pairs = Object.fromEntries(
req.headers["stripe-signature"].split(",").map((p) => p.split("=")),
);
const expected = crypto
.createHmac("sha256", endpointSecret) // whsec_... from the endpoint's page
.update(`${pairs.t}.${rawBody}`)
.digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(pairs.t)) < 300;
const valid =
fresh &&
crypto.timingSafeEqual(Buffer.from(pairs.v1), Buffer.from(expected));Or let the SDK do it: stripe.webhooks.constructEvent(rawBody, sig, secret) — it needs the raw body, so mount express.raw() on the webhook route before any JSON parser.
What bites
Stripe never publishes a response deadline. Its docs say only to return 2xx before any slow logic; 20 seconds is the number the community works to, and handlers that write to a slow database synchronously flirt with it on every event.
After 3 days of failures the event leaves the retry queue silently. Continued failure gets the endpoint disabled, with an email first, and the events stay pullable from the Events API for 30 days.
Read more
AnyHook sits in front of endpoints that receive from Stripe: it answers inside the budget above, retries on its own schedule when your server is down, and keeps every event replayable. Change one URL, keep your code.