Sume webhook signature fails: check the secret fingerprint first
When a Sume webhook signature will not verify, compare the 12-character secret fingerprint header before you debug the HMAC. Includes a Python verifier.

If a Sume webhook signature will not verify, first compare the x-sume-webhook-secret-fingerprint header with the fingerprint shown next to your signing secret in the dashboard. The header is 12 hex characters. If the two differ, your receiver holds a different secret from the one that signed the delivery, and no amount of HMAC debugging will fix that. Neither side ever has to send the secret itself.
Where the fingerprint appears
Each delivery carries three headers: a timestamp, a signature of the form sume-v1=<hex>, and the fingerprint. The signature is HMAC-SHA256 over <timestamp>.<raw_body> with your workspace's signing secret, computed on the raw bytes before parsing. The same fingerprint is on the run receipt as webhook_delivery.signing_secret_fingerprint.
| Item | Rule |
|---|---|
| Signature header | x-sume-webhook-signature: sume-v1=<hex hmac-sha256> |
| Signed text | <timestamp>.<raw_body> |
| Timestamp window | Reject outside five minutes |
| Fingerprint header | x-sume-webhook-secret-fingerprint, 12 hex characters |
| Secret source | Dashboard Webhooks tab, or GET /v1/webhooks/signing-secret with account:read |
| Env name used by Sume | SUME_COM_WEBHOOK_SIGNING_SECRET |
A verifier that refuses an empty secret
The function below checks the window, refuses an empty secret, and compares against every comma-separated signature entry, which keeps it working while a secret is being rotated. The demo at the bottom signs a sample body and verifies it.
import hashlib, hmac, time
def verify(secret, ts, raw, header):
if not secret:
raise ValueError("empty webhook secret")
if abs(time.time() - int(ts)) > 300:
return False
mac = hmac.new(secret.encode(), ts.encode() + b"." + raw, hashlib.sha256)
want = "sume-v1=" + mac.hexdigest()
return any(hmac.compare_digest(p.strip(), want) for p in header.split(","))
raw = b'{"event":"format.run.terminal"}'
ts = str(int(time.time()))
sig = "sume-v1=" + hmac.new(b"s3cret", ts.encode() + b"." + raw, hashlib.sha256).hexdigest()
print(verify("s3cret", ts, raw, sig))Steps when it fails
Work from the cheapest check to the dearest.
- Compare the fingerprint header with the dashboard fingerprint. A mismatch means the wrong secret.
- Confirm you verify the raw request bytes, not a re-serialized JSON body.
- Check your server clock against the five-minute window.
- Check that the environment variable is set and not empty before the process starts.
What Sume does not do
Sume does not send the secret in any delivery, and the fingerprint is not a secret you can verify with; it only tells you which secret was used. A failed verification on your side should return a non-2xx answer, and Sume will then count the attempt as failed and retry within its documented limits.
Secret rotation and retries
During a signing-secret rotation the signature header carries one entry for each live secret, newest first, separated by commas, and the receiver should accept the delivery if any entry verifies. The verifier above does that. Without it, a receiver that reads the header as a single value will fail during the rotation window even though nothing is wrong with the secret.
Sume treats any non-2xx answer, and any answer that takes more than 10 seconds, as a failed attempt. It tries up to 10 times, with a backoff that grows from 30 seconds with jitter up to one hour, or longer if you send a Retry-After on a 429 or 503. A delivery outcome never changes the run, so a verification bug on your side leaves the run completed and the delivery status failed or exhausted. You can ask for a new delivery with the redeliver endpoint once the receiver is fixed.
Sources
Related posts
More in Formats
- Which ready-made Sume Formats can I call today? 27 slugs
Sume's first-party Format catalog answers at the sume handle with 27 slugs, from UGC and product demos to try-on, product splashes and recreate. List and call.
- Which Sume Format for a UGC ad? Read io, then call by name
Sume's catalog lists UGC-style Formats such as sume-close-camera-ugc and sume-mobile-app-ugc. Read each Format's io profile, then run it with a spend cap.
- Which Sume Formats return images, not video? 9 of the 27 slugs
Of the 27 Formats by Sume, 18 declare video output and 9 declare image output. The slug lists, how to read the io profile, and why to check before you call.
- Ready-made Formats for product video: the Sume Format catalog
Sume ships ready-made Formats for product and UGC-style video and images, each callable from your backend with one HTTP request at the reserved sume handle.
Written by Sume