Webhook secret rotation: no downtime with a 24-hour window

After you rotate a Sume webhook signing secret, deliveries carry two signatures for 24 hours. What the header looks like and how to redeploy safely.

4 min readSume
All posts

Rotating a webhook secret without downtime means both the old and the new secret verify for a while. Sume does this for you: for 24 hours after you rotate, it signs every delivery with both secrets and sends both signatures in one header, newest first. A receiver holding either secret passes, so you redeploy on your own schedule.

The steps below come from the Sume Verifying webhooks and Webhooks docs, read 2026-09-29. They apply to job webhooks and to run webhooks, which share one signing secret.

How do I rotate a Sume webhook signing secret?

Use **Webhooks, then Rotate secret** in the dashboard, or POST /v1/webhooks/signing-secret/rotate with an API key that carries account:write. Read the current secret on the dashboard Webhooks tab or from GET /v1/webhooks/signing-secret with a key that carries account:read.

  • Before you rotate, check that your receiver accepts a header with several sume-v1= entries (see the next sections).
  • Rotate. Both API responses carry rotation.previous_valid_until, the deadline of the window; the dashboard shows it too.
  • Read the new secret and deploy it to your receiver's secret store as SUME_COM_WEBHOOK_SIGNING_SECRET.
  • Watch deliveries verify against the new secret, then stop worrying: after the window the old secret stops verifying.
curl -X POST https://api.sume.com/v1/webhooks/signing-secret/rotate \
  -H "Authorization: Bearer $SUME_API_KEY"

What does the header look like during the window?

Outside a window Sume sends exactly one signature. During a window the x-sume-webhook-signature header lists one entry per live secret, newest first, separated by commas.

From Verifying webhooks, read 2026-09-29.
MomentWhat Sume sends
No rotation in progresssume-v1=<hex>
Within 24 hours of a rotationsume-v1=<new>,sume-v1=<old>
After the windowOne signature again; the old secret no longer verifies
Fingerprint headerx-sume-webhook-secret-fingerprint names the new secret from the moment you rotate

Why does my verifier fail only during rotation?

A hand-rolled verifier that compares the whole header to one expected string fails every delivery while two entries are present. The docs say to upgrade the receiver before you rotate. The docs say verifyWebhook in @sume-com/sdk 0.2.0, which they call the current release, already handles the multi-signature header.

The fix is to split the header on commas, keep the entries that start with sume-v1=, and accept the delivery if any one matches. Compare every entry in constant time, over the raw body, and refuse to run with an empty secret: an empty HMAC key would let a forged signature pass.

import hashlib, hmac, time

def verify(raw: bytes, timestamp: str, header: str, secret: str) -> bool:
    if not secret:
        raise ValueError("empty signing secret")
    try:
        ts = int(timestamp)
    except ValueError:
        return False
    if abs(time.time() - ts) > 300:
        return False
    mac = hmac.new(secret.encode(), f"{ts}.".encode() + raw, hashlib.sha256)
    want = ("sume-v1=" + mac.hexdigest()).encode()
    ok = False
    for entry in header.split(","):
        ok |= hmac.compare_digest(entry.strip().encode(), want)
    return ok

How do I know which secret my receiver holds?

Compare fingerprints. The dashboard shows a fingerprint beside the secret, and every delivery carries the same kind of value in x-sume-webhook-secret-fingerprint; it is safe to paste into a ticket. During a window the header names the new secret, so it tells you which secret to move to, not which ones are still accepted.

What if the secret leaked?

Rotating twice inside one window retires the secret two rotations back immediately. The docs say this is what makes a leak actually stop. In practice: rotate, deploy the new secret to your receiver, then rotate again. Rotating is a signing-secret step only; if an API key leaked too, follow what to do with an exposed API key.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume