Rotate the Sume webhook secret without dropping events
After POST /v1/webhooks/signing-secret/rotate, Sume signs with both secrets for 24 hours. How verifyWebhook handles the two-entry header, and the deploy order.

Rotate the Sume signing secret in this order: upgrade your receiver to a verifier that accepts multi-signature headers, then rotate, then deploy the new secret within 24 hours. For that day Sume signs every delivery with both secrets and sends x-sume-webhook-signature: sume-v1=<new>,sume-v1=<old>, so a receiver holding either secret verifies.
A hand-written verifier that compares the whole header for equality fails every delivery during the window. verifyWebhook in @sume-com/sdk 0.2.0 already handles the two-entry header, per the verifying webhooks page.
A receiver with the SDK
The guard on the secret is worth keeping; a missing environment variable should stop the process, not weaken the check.
import { verifyWebhook } from "@sume-com/sdk";
const secret = process.env.SUME_COM_WEBHOOK_SIGNING_SECRET;
if (!secret) throw new Error("SUME_COM_WEBHOOK_SIGNING_SECRET is empty");
async function saveTerminalJob(jobId: string, event: unknown) {
console.log("store", jobId, event); // replace with a durable, deduped write
}
export async function POST(request: Request) {
const body = await request.text(); // raw, before any JSON.parse
const ok = await verifyWebhook({ body, headers: request.headers, secret });
if (!ok) return new Response("bad signature", { status: 401 });
const event = JSON.parse(body);
switch (event.event) {
case "job.completed":
case "job.failed":
case "job.canceled":
await saveTerminalJob(event.job_id, event); // dedupe on job_id
break;
default:
break; // unknown event type: still 2xx, no retry storm
}
return new Response(null, { status: 204 });
}Rotation timeline
| Step | What Sume does | What you do |
|---|---|---|
| Before | One sume-v1 entry | Upgrade the receiver |
| Rotate | New secret first, old second in the header | Copy the new secret |
| Next 24 hours | Two entries on every delivery | Deploy the new secret on your schedule |
| After | Old secret rejected | Nothing; check previous_valid_until on the API response |
Edge cases
- Rotating twice inside one window retires the oldest secret at once, which is how you stop a real leak.
x-sume-webhook-secret-fingerprintnames the new secret from the moment you rotate; it does not say which secrets are still accepted.verifyWebhookreturnsfalserather than throwing, so a bad delivery is one branch, not a try/catch.
Deploy order, concretely
First ship the receiver that accepts multi-entry headers; with the SDK that means version 0.2.0. Then rotate in the dashboard or with POST /v1/webhooks/signing-secret/rotate. Then update the secret in your secret store and redeploy. Rotating needs an API key with account:write; reading the secret needs account:read. Finally, confirm that deliveries verify with the new secret and let the 24-hour window close. The fingerprint header is your check at each step: it should match the fingerprint beside the new secret in the dashboard.
Keep the same receiver for run webhooks. Format, Action and Agent runs use the identical sume-v1 scheme and the same secret, so routing on event is enough to share one endpoint between runs and jobs.
Sources
Related posts
More in Developers
- Same prompt on five Sume image models in one script: about 18 cents
One loop sends a text-in-image prompt to Flux 2 Pro, Seedream 5.0 Lite, Qwen Image, Imagen 4 Fast and Recraft V4. Expected total $0.18125. Code you can run.
- Score your own audio: a word error rate script for Sume STT
Microsoft quotes 5.2% WER on FLEURS for MAI-Transcribe-2. Measure your audio: a short Python WER function, a reference file and a cent-a-minute Sume STT run.
- Seedance output pixel sizes: 480p, 720p and 1080p for every ratio
The frame size Seedance renders at 480p, 720p and 1080p for 21:9, 16:9, 4:3, 1:1, 3:4 and 9:16 on Sume, as used for pricing.
- Seedance reference inputs on Sume: images, video, audio per docs
Which reference types the Seedance ids accept on /v1/videos (images, video, audio, first and last frame), how references are priced, and a working request.
Written by Sume