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.
- What status should a Sume webhook receiver return? Any 2xx
Sume counts any 2xx as delivered, so 202 or 204 is fine. A tested receiver that verifies the signature, stores the job id, answers 204 and defers the work.
- Sume webhook Redeliver errors: 409 running or no URL, 403 jobs:write
POST /v1/jobs/{id}/webhook/redeliver returns 409 while the job runs or if it had no webhook_url, 403 without jobs:write, and 404 for a job you cannot see.
- Sume webhook Redeliver gets a fresh signature: dedupe on job_id
Redeliver re-POSTs a job's real terminal event with a new timestamp and signature, so dedupe on job_id and event, never on the signature or timestamp.
Written by Sume