Webhook timestamp tolerance: Sume 300 s versus Stripe 5 min
Sume's verifyWebhook rejects deliveries older than 300 seconds by default. Why clock skew matters, why toleranceSeconds 0 is a trap, and how to fix drift.

Short answer
verifyWebhook in @sume-com/sdk checks the x-sume-webhook-timestamp header and rejects a delivery outside a 300 second window by default. Stripe's libraries use the same 5 minute default, and its docs say to keep your server clock accurate with NTP and never to use a tolerance of 0, because that disables the recency check. Sume's option works the same way: toleranceSeconds 0 skips the timestamp check.
Why a window exists
The signature is an HMAC-SHA256 over the timestamp, a dot and the raw body. Anyone who captures a valid delivery can replay it later, and the signature will still match. The timestamp window is what limits that: a captured request is only accepted for a few minutes.
| Item | Sume SDK | Stripe libraries |
|---|---|---|
| Default tolerance | 300 seconds | 5 minutes |
| Setting | toleranceSeconds | Library tolerance parameter |
| Tolerance 0 | Skips the timestamp check | Stripe says do not use it; it disables the check |
| Clock advice | Keep receivers on accurate time | Use NTP |
Clock skew in practice
If your receiver's clock runs more than five minutes ahead or behind, every genuine delivery fails verification and the symptom looks like a bad secret. Check this before you rotate anything. Compare the x-sume-webhook-timestamp value with your server time on a failing request, and compare the secret fingerprint header, which is safe to paste into a ticket, with the one on the dashboard.
Containers and CI runners are the usual culprits, especially long-lived dev machines that were suspended. Run a time sync service on the host and fail your health check if drift is large.
Do not widen the window to fix a bug
Raising toleranceSeconds to an hour hides drift and widens the replay window. Setting it to 0 removes the protection entirely. The better fix is accurate time on the receiver. If you must accept slow queues, verify at the edge, in the handler that receives the request, and only then hand the verified payload to a queue. Do not verify later in a worker, where the timestamp is already old and a valid delivery would look stale.
Replay protection by timestamp is not deduplication. Sume can deliver more than once, so dedupe on the request_id in the envelope and order by created_at. See dedupe on request_id.
import { verifyWebhook } from "@sume-com/sdk";
export async function POST(request: Request) {
const secret = process.env.SUME_COM_WEBHOOK_SIGNING_SECRET;
if (!secret) return new Response("not configured", { status: 500 });
const body = await request.text();
const ok = await verifyWebhook({
body,
headers: request.headers,
secret,
toleranceSeconds: 300,
});
return new Response(null, { status: ok ? 204 : 401 });
}
Rotation and the window
During a secret rotation, Sume signs with both secrets for 24 hours and sends two comma-separated signatures. Tolerance is unrelated to that window, so a rotation never needs a wider tolerance. Upgrade the receiver before you rotate, so that it accepts either signature.
A debugging order that saves time
When verification starts failing in production, work through the causes from cheapest to most disruptive. First compare the fingerprint header with the one on the dashboard; a mismatch means the receiver holds the wrong secret. Second, check the body: a framework that parses JSON before your handler has already changed the bytes, and the signature covers the raw body. Third, compare timestamps with your server clock. Only after those three should you rotate the secret.
Log the timestamp header and your own clock on every failure, never the secret and never the signature. Those two numbers settle the skew question in one line of output.
Sources
Related posts
More in Developers
- WebVTT cue text cannot contain --> : clean Sume STT segments in Python
WebVTT forbids the arrow sequence inside cue text and wants 3-digit milliseconds. A short Python script turns Sume STT sentence segments into a valid .vtt file.
- Four avatar clips a week: Python batch, one idempotency key each
HeyGen's survey ties avatars to consistent posting. Submit four Sume avatar clips a week from one handle with week-stamped keys and queue_full handling.
- What to measure for Sume API jobs: metrics, labels and alerts
A metrics plan for code that calls the Sume API: submit outcome, queue wait, time to terminal, error code and webhook gap, with low-cardinality labels.
- WhatsApp video: H.264 High profile with B-frames fails on Android
WhatsApp Cloud API says H.264 High profile with B-frames is unsupported on Android and recommends Main or Baseline. What Sume trim does and does not promise.
Written by Sume