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 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.
- Swift: submit a Kling 3.0 job and save the MP4 to disk
One Swift file: POST /v1/videos for kling-3, poll the polling_url, follow the content redirect and write out.mp4. A 5 s clip without audio costs $0.70.
- Swift URLSession: call Sume's image API and branch on 200 or 202
Use URLSession.data(for:) with async/await, cast the response to HTTPURLResponse, and read statusCode before you touch data[0].url on Sume's /v1/images.
- Which Sume audio calls can sync-wait 30 s? A map vs 150 ms claims
Detach, timeline audio, ingest, music and TTS: which can return in a 30 s sync wait, which are async jobs, and what a 150 ms end-to-end claim leaves out.
Written by Sume