Verify a Sume Format webhook in Python with an empty-secret guard
Verify x-sume-webhook-signature in Python: HMAC-SHA256 over timestamp.raw_body, a five-minute window, constant-time compare, empty secret refused.

Sume signs each terminal webhook with HMAC-SHA256 over <timestamp>.<raw_body>. The signature header looks like sume-v1=<hex>, the timestamp is Unix seconds in x-sume-webhook-timestamp, and you should reject anything outside a five-minute window. Verify against the raw bytes, not a re-serialized JSON object, and refuse to run at all if the secret is empty: an empty secret makes every signature trivially forgeable.
Verifier
import hashlib, hmac, time
def verify(secret: str, timestamp: str, signature: str, raw_body: bytes) -> bool:
if not secret:
raise ValueError("webhook secret is empty")
try:
ts = int(timestamp)
except ValueError:
return False
if abs(time.time() - ts) > 300:
return False
msg = timestamp.encode() + b"." + raw_body
expected = "sume-v1=" + hmac.new(secret.encode(), msg, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)Headers and dedupe
| Header | Use |
|---|---|
| x-sume-webhook-timestamp | Unix seconds, part of the signed string |
| x-sume-webhook-signature | sume-v1=<hex digest> |
| x-sume-webhook-secret-fingerprint | Identify which secret signed it |
Handle repeats
Deliveries retry, so dedupe on request_id or run_id before you act. Return a 2xx quickly and do the work after. The TypeScript SDK ships a verifyWebhook helper with the same rules; see SDK webhooks.
Sources
Related posts
More in Formats
- SumeMediaFile duration_ms is checked within 10% of the real file
A duration_ms in Format output must match the run's artifact ledger within 10%, or the projection fails. A null means not measured, not zero.
- Sume SDK subscribeFormatRun: 20-minute timeout vs 90-minute runs
subscribeFormatRun in @sume-com/sdk polls every 2 seconds and times out at 20 minutes by default, while a Format run can live 90. Set the timeout on purpose.
- sume-virtual-try-on Format: first frame, then Seedance 2.5
The sume-virtual-try-on Format builds a first frame with ChatGPT Image 2, then animates it with Seedance 2.5. What goes in, what comes back.
- Sume webhook retry schedule: 30s doubling, 10 tries, 1h cap
Sume Format run webhooks retry up to 10 times with min(max(30s x 2^(attempt-1), Retry-After), 1h). The arithmetic of that schedule and what to do when it ends.
Written by Sume