Sume webhook fails the 300-second window: find the clock drift
A valid Sume signature still fails if your server clock is more than five minutes off. Tell drift from a bad secret with a small Python check.

A Sume webhook delivery is signed over <timestamp>.<raw_body> and carries the timestamp in x-sume-webhook-timestamp. A receiver should reject a delivery whose timestamp is outside a replay window; the Sume docs suggest five minutes, and verifyWebhook in the SDK defaults toleranceSeconds to 300. If your host clock is off by more than that, every delivery fails even though the secret is correct.
The symptom looks like a secret problem, so people rotate secrets and make things worse. The fix is to separate the two failures in your handler and log which one happened.
Tell the two failures apart
Compute the skew before you compare signatures. A positive skew means your clock is ahead of the timestamp Sume sent; negative means behind. The sample returns a distinct reason for each case.
import hashlib, hmac, time
def check(body, headers, secret, now=None, tolerance=300):
if not secret:
raise ValueError("empty signing secret")
ts = int(headers["x-sume-webhook-timestamp"])
skew = (time.time() if now is None else now) - ts # > 0: your clock is ahead
if abs(skew) > tolerance:
return f"rejected: timestamp {skew:+.0f}s from your clock (limit {tolerance}s)"
want = hmac.new(secret.encode(), f"{ts}.".encode() + body, hashlib.sha256).hexdigest()
sigs = [p.removeprefix("sume-v1=") for p in headers["x-sume-webhook-signature"].split(",")]
ok = any(hmac.compare_digest(want, s) for s in sigs)
return "ok" if ok else "rejected: signature mismatch (not a clock problem)"
secret, body, ts = "whsec_test", b"{}", 1_790_000_000
sig = hmac.new(secret.encode(), f"{ts}.".encode() + body, hashlib.sha256).hexdigest()
h = {"x-sume-webhook-timestamp": str(ts), "x-sume-webhook-signature": f"sume-v1={sig}"}
print(check(body, h, secret, now=ts + 2)) # ok
print(check(body, h, secret, now=ts + 420)) # clock 7 minutes ahead
print(check(body, h, secret, now=ts - 420)) # clock 7 minutes behindWhat to do about it
- Sync the host clock with your operating system time service, then watch the skew in your logs for a day.
- Do not set
toleranceSecondsto 0 to silence it. In the SDK, 0 skips the timestamp check, which also removes your replay protection. - If the signature mismatches with a sane clock, compare
x-sume-webhook-secret-fingerprintwith the fingerprint shown next to the secret in the dashboard. Neither side needs to send the secret itself. - After you fix the clock, redeliver missed jobs with
POST /v1/jobs/{id}/webhook/redeliver; each redelivery gets a fresh timestamp and signature.
Containers and CI
A container shares the host kernel clock, but a paused virtual machine or a laptop that slept can resume minutes behind. If drift appears only in one environment, suspect that host first. Keep the injectable now parameter in the verifier so tests can pin a clock instead of sleeping.
Sources
Related posts
More in Developers
- Sume webhook handler over 10 seconds: acknowledge first, then work
Sume gives each webhook attempt 10 seconds. Verify, answer 2xx, then process in the background and dedupe on job_id. Node sample you can run locally.
- Sume webhook rotation: upgrade the verifier before you click Rotate
During a rotation window Sume sends two signatures in one header. A receiver that compares the whole header for equality fails every delivery. Fix it first.
- SvelteKit +server.ts endpoint for an AI video webhook: request.text()
A SvelteKit POST handler reads request.text(), verifies Sume's HMAC over timestamp.body with node:crypto and returns 401 for bad or missing signatures.
- Swap the AI image model without a redeploy: JSON config hot reload
Read the Sume image model id from a JSON file that reloads when it changes, so a gpt-image-1 shutdown fix is a one-line edit with no deploy. Python, stdlib.
Written by Sume