Stripe tolerance 0 disables replay checks; Sume keeps a 300 s window
Stripe's default webhook tolerance is 5 minutes and 0 turns the check off. Sume's documented window is 300 seconds. Never set zero; reject stale timestamps.

Never disable the replay check. Stripe's default tolerance is five minutes and setting it to 0 turns the check off, and Sume documents the same five minutes as 300 seconds. A signature proves who sent a body; only the timestamp window stops an attacker from re-sending an old valid delivery.
Here are the two documented windows and a short guard that refuses a zero tolerance.
The two windows
| Item | Stripe | Sume |
|---|---|---|
| Default tolerance | 5 minutes | 300 seconds (5 minutes), 'a reasonable default' |
| Zero | Disables the recency check | Not offered by the docs; the sample verifier takes a toleranceSeconds parameter |
| Signed content | Timestamp plus payload | <timestamp>.<raw_body>, HMAC SHA-256 |
| Secret rotation | Roll overlap up to 24 hours | The header carries one sume-v1= entry per live secret |
| Retries | Live mode up to 3 days | 10 attempts, 30 s apart, then redeliver |
A caution about retries
Sume re-signs on redeliver with a fresh timestamp, so a legitimate late delivery still passes a 300 second window. Retries are different: they happen about 30 seconds apart, well inside the window. That is why the window can stay short without breaking delivery. You do not need a wide tolerance to cope with retries, so a zero or a very large value has no operational reason.
Guard code
Make tolerance a required positive number and fail loudly on an empty secret.
export function assertFresh(timestampSeconds, tolerance = 300, now = Date.now() / 1000) {
if (!(tolerance > 0)) {
throw new Error("tolerance must be greater than zero");
}
const ts = Number(timestampSeconds);
if (!Number.isFinite(ts)) return false;
return Math.abs(now - ts) <= tolerance;
}
export function requireSecret(secret) {
if (!secret) throw new Error("webhook secret is empty");
return secret;
}Operational notes
- Keep server clocks on NTP. A drifting clock turns a tight window into false rejections.
- Log rejections with the received timestamp, never with the secret.
- Dedupe on the event or job id as well. A fresh timestamp is not the same as a new event, since Sume redeliver sends the same job_id again.
- Both services can send during a secret rotation, so accept any matching signature entry.
Sources
Related posts
More in Developers
- Pick a caption style from the STT language_code before you burn
Run Sume STT on the detached audio, read language_code, and choose korean-ad or slam before POST /v1/video-captions. Avoids the Hangul-on-Latin 400.
- STT with no punctuation: Sume still cuts sentence segments on silence
Sume STT's sentence segmentation splits on terminal punctuation, and on silence when a run has none. What boundary_lead_ms 70 does and a Python call.
- STT words into caption words: rename word to text, stay under 60 s
Sume STT returns words as {word, start, end}; the caption job wants {text, start, end}, end above start, 60 s or less, 1200 words at most. Map and filter.
- Submit Sume jobs from Go with a bounded pool and idempotency keys
Bound a Go worker pool to your workspace in-flight headroom, send a stable Idempotency-Key per item, and stop treating a full queue as a crash. Stdlib only.
Written by Sume