Sume webhook rotation: upgrade the verifier before you click Rotate
During a rotation window Sume sends two signatures in one header. A receiver that compares the whole header for equality fails every delivery. Fix it first.

The Sume SDK docs carry a caution before the rotation steps: upgrade the receiver before you rotate. For 24 hours after a rotation, x-sume-webhook-signature holds two entries, sume-v1=<new>,sume-v1=<old>, newest first. A hand-rolled verifier that compares the header with == fails every delivery during that window.
Outside a window the header has a single entry, so the bug stays invisible until the day you rotate.
Naive versus safe
Run the sample: both verifiers agree outside a window, and only the safe one survives inside it.
import hashlib, hmac, time
def sign(secret, ts, body):
return hmac.new(secret.encode(), f"{ts}.".encode() + body, hashlib.sha256).hexdigest()
def naive_ok(header, secret, ts, body): # breaks while a rotation window is open
return header == f"sume-v1={sign(secret, ts, body)}"
def rotation_safe_ok(header, secret, ts, body):
want = sign(secret, ts, body)
return any(hmac.compare_digest(p.removeprefix("sume-v1="), want) for p in header.split(","))
ts, body = str(int(time.time())), b'{"event":"job.completed"}'
single = f"sume-v1={sign('new', ts, body)}"
double = f"sume-v1={sign('new', ts, body)},sume-v1={sign('old', ts, body)}"
for label, header in (("outside a window", single), ("inside the 24 h window", double)):
print(label, "naive:", naive_ok(header, "new", ts, body),
"safe:", rotation_safe_ok(header, "new", ts, body))Checklist before rotating
| Check | Why |
|---|---|
| Verifier accepts any sume-v1 entry | Header has two entries during the window |
| Secret read from SUME_COM_WEBHOOK_SIGNING_SECRET | One place to change |
| Empty secret refused | Fail closed rather than accept everything |
| Raw body is verified | A re-serialized body changes the signature |
Notes
verifyWebhookin@sume-com/sdk0.2.0 already handles the multi-signature header and returns false rather than throwing.- Rotation needs
account:write;rotation.previous_valid_untilshows the end of the window. - Deploy the new secret to receivers during the window, not at the same instant you press the button.
Sources
Related posts
More in Developers
- SvelteKit +server.ts endpoint for an AI video webhook: request.text()
A SvelteKit POST handler reads request.text(), verifies Sume's HMAC over timestamp.body with node:crypto and returns 401 for bad or missing signatures.
- Swap the AI image model without a redeploy: JSON config hot reload
Read the Sume image model id from a JSON file that reloads when it changes, so a gpt-image-1 shutdown fix is a one-line edit with no deploy. Python, stdlib.
- Test a Sume poll loop without waiting: inject sleep, assert delays
Unit test a job poll loop in milliseconds by injecting the fetch and the sleep. Assert that next_poll_after_seconds is obeyed and the 20-minute deadline holds.
- Text-to-speech API with curl and jq: one shell script to an MP3
Call the Sume TTS Router from a shell: submit with curl, loop on the status URL with jq until terminal, then download the audio artifact to voiceover.mp3.
Written by Sume