Sume webhook five-minute replay window: reject stale deliveries
Sume signs timestamp.raw_body and advises a five-minute replay tolerance. Reject stale timestamps, refuse an empty secret, and keep job_id as the dedupe key.

Sume signs each webhook with HMAC-SHA256 over <timestamp>.<raw_body> and tells you to reject a delivery whose timestamp is outside your replay tolerance; five minutes is its suggested default. A publisher that posts to social platforms should apply it, because a replayed job.completed could otherwise trigger a duplicate post.
The window is about replays, not retries: Sume's retries and redeliveries carry a fresh timestamp and signature.
What are the header rules?
From Sume's webhook docs.
| Item | Value |
|---|---|
| Timestamp header | x-sume-webhook-timestamp |
| Signature header | x-sume-webhook-signature: sume-v1=<hex> |
| Signed text | <timestamp>.<raw_body> |
| Replay tolerance | Five minutes is a reasonable default (SDK toleranceSeconds default 300) |
| During rotation | Header carries one entry per live secret; accept any match |
What does a safe verifier look like?
Use the raw bytes, refuse an empty secret, compare in constant time across every entry, and check the timestamp first.
import hashlib
import hmac
import time
def verify(raw_body: bytes, timestamp: str, signature_header: str, secret: str, tolerance: int = 300) -> bool:
if not secret:
raise ValueError("refusing to verify with an empty secret")
try:
ts = int(timestamp)
except ValueError:
return False
if abs(int(time.time()) - ts) > tolerance:
return False
digest = hmac.new(secret.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256).hexdigest()
expected = f"sume-v1={digest}"
ok = False
for entry in signature_header.split(","):
if hmac.compare_digest(entry.strip(), expected):
ok = True
return ok
if __name__ == "__main__":
body, now = b'{"event":"job.completed"}', str(int(time.time()))
sig = "sume-v1=" + hmac.new(b"s3cret", now.encode() + b"." + body, hashlib.sha256).hexdigest()
print(verify(body, now, sig, "s3cret"))What about a skewed clock?
If valid deliveries fail the window, fix the receiver's clock, not the tolerance. Compare x-sume-webhook-secret-fingerprint with the dashboard value when a signature fails; the fingerprint is safe to share and the secret is not (Verifying webhooks).
Sources
Related posts
More in Developers
- Sume webhook retried 10 times and stopped: recover the job anyway
Sume tries a webhook up to 10 times, 30 seconds apart, 10s timeout each. After that the job is still terminal. Poll status_url or call redeliver to recover.
- Sume webhook Redeliver vs Send test: which one replays a real job?
Send test posts a dummy webhook.test payload to a URL you type. Redeliver re-posts a real job's terminal event with a fresh signature. API routes for both.
- Sume webhook retries: 10 attempts 30 seconds apart, size your downtime
Sume retries a failing webhook up to 10 attempts at a default 30-second spacing, about 4.5 minutes. Past that, redeliver or poll after a publisher outage.
- Sume webhook signature fails? Verify the raw body before JSON.parse
Sume signs HMAC-SHA256 over timestamp.raw_body. Re-serialized JSON changes the bytes and the check fails. Read the raw body first; a Node verifier is included.
Written by Sume