verifyWebhook toleranceSeconds 0 turns off the Sume replay check
In @sume-com/sdk, toleranceSeconds defaults to 300 and 0 skips the timestamp check. A runnable test shows an hour-old signed delivery passing only at 0.

In verifyWebhook from @sume-com/sdk, toleranceSeconds defaults to 300, and a value of 0 skips the timestamp check entirely. That makes 0 handy in a test and dangerous in production: a captured delivery with a valid signature would verify forever. Leave the option unset in your receiver, or set it to a number of seconds you can defend, and keep 0 for fixtures only.
What the option does
The signed text is the timestamp, a dot and the raw body, hashed with HMAC-SHA256. The helper enforces the replay window before it computes the HMAC, and it returns false instead of throwing.
| Field | Notes |
|---|---|
| body | The raw body: string, ArrayBuffer or typed array |
| headers | Headers, Map or plain object, case-insensitive |
| secret | Your Sume webhook signing secret |
| toleranceSeconds | Replay window, default 300, 0 skips the timestamp check |
A test you can run
This script signs a body with a timestamp from an hour ago, using a dummy secret. It prints the verdict for the default window, an explicit 300, and 0. Expect false, false and true.
import { createHmac } from "node:crypto";
import { verifyWebhook } from "@sume-com/sdk";
const secret = "a".repeat(64); // test value only
const body = JSON.stringify({ event: "job.completed", job_id: "job_1" });
const ts = Math.floor(Date.now() / 1000) - 3600; // signed an hour ago
const sig = createHmac("sha256", secret).update(`${ts}.${body}`).digest("hex");
const headers = {
"x-sume-webhook-timestamp": String(ts),
"x-sume-webhook-signature": `sume-v1=${sig}`,
};
for (const toleranceSeconds of [undefined, 300, 0]) {
const ok = await verifyWebhook({ body, headers, secret, toleranceSeconds });
console.log(`tolerance ${toleranceSeconds ?? "default"}: ${ok}`);
}Choosing a window
A replay window and a dedupe table do different jobs, and a receiver should have both.
- Keep the default of 300 seconds unless your clocks drift. Fix the drift with NTP before you widen the window.
- Do not use 0 in a staging environment that shares a secret with production.
- Dedupe on job_id or request_id as well. The timestamp check stops old replays, not a fast duplicate.
- A receiver that is down for longer than the window rejects the late retry, so Sume's redeliver endpoint is the recovery path, not a wider tolerance.
Why the window exists
A valid signature proves that Sume signed a body at some moment. It does not prove the delivery is new. Without a window, anyone who once captured a request, for instance from a proxy log, could send it again and your receiver would accept it. The timestamp header is part of the signed text, so an attacker cannot move it forward without breaking the signature. That is why the window check and the HMAC belong together.
If your fixtures need a fixed timestamp, sign them with the current time at test start, as the script above does, instead of switching the check off.
Sources
Related posts
More in Developers
- Sume video models by aspect ratio: who takes 9:16, 21:9 and 1:1
Seedance takes 21:9 through 9:16, Kling only 16:9, 9:16 and 1:1, Gemini Omni only 16:9 and 9:16. Full matrix of Sume video models as of 2026-10-08.
- AI video API with audio reference input: which Sume models accept it
Seedance (all four), Wan 3.0, MiniMax H3 and H3 Max accept audio references on Sume. Kling, Gemini Omni, Grok Imagine and the motion models do not.
- Video frames caps at 24 stills: a 15 s hook needs fps 1.6, not 2
Video frames returns at most 24 stills and fps up to 2. At 2 fps only 12 s are covered, so a 15 s hook needs fps 1.6; the Python below checks any length.
- One env line picks the video model: $1.25 vs $5.78 per 10 s clip
Read the Sume video model id from one env var. For a 10 s 720p 9:16 clip, gemini-omni-flash-1.1 bills $1.25 and seedance-2.5 bills $5.78.
Written by Sume