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.

5 min readSume
All posts

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.

Signature headers and checks (read 2026-10-05)
ItemValue
Timestamp headerx-sume-webhook-timestamp
Signature headerx-sume-webhook-signature: sume-v1=<hex>
Signed text<timestamp>.<raw_body>
Replay toleranceFive minutes is a reasonable default (SDK toleranceSeconds default 300)
During rotationHeader 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

All Developers posts

Written by Sume