curl 8.22 RFC 9421 signatures vs the Sume webhook signature
curl 8.22.0 adds experimental RFC 9421 HTTP Message Signatures. A Sume webhook is not one: it is an HMAC over timestamp.body in a sume-v1 header.

No. A Sume webhook is not an RFC 9421 HTTP Message Signature, so curl's new experimental support will not verify it. Sume signs the raw JSON body with HMAC SHA-256 over <timestamp>.<raw_body> and sends the result as sume-v1=<hex_signature>. Check it with verifyWebhook from @sume-com/sdk or a short HMAC function.
curl facts are from the 8.22.0 release post; Sume facts from Webhooks and Verifying webhooks, read 2026-09-30.
What did curl 8.22.0 add?
The release post lists "new RFC 9421 HTTP Message Signatures support (experimental)" among the six changes in the release of September 2, 2026. That is the only detail the post gives, so this page makes no claim about options or behavior beyond it.
What does a Sume delivery carry instead?
Two headers and a plain HMAC. The signed string is the timestamp, a dot, and the raw body.
| Part | Sume value |
|---|---|
| Timestamp header | x-sume-webhook-timestamp: 1780000000 |
| Signature header | x-sume-webhook-signature: sume-v1=<hex_signature> |
| Signed string | <timestamp>.<raw_body> |
| Algorithm | HMAC SHA-256 with your signing secret |
| During secret rotation | One sume-v1= entry per live secret, comma-separated, newest first |
| Replay window | Reject a timestamp outside your tolerance; five minutes is the documented default |
Why can't curl check it?
The docs describe the scheme as its own HMAC string, not a signature base built from covered components. The docs do not describe an RFC 9421 mapping for Sume deliveries, so nothing here lets a generic 9421 verifier accept them. Treat the scheme as Sume-specific.
What should a receiver use?
verifyWebhook is the check, so you do not write it: pass the raw body, the headers, and the secret from SUME_COM_WEBHOOK_SIGNING_SECRET. It is async and returns false on a malformed delivery. See HMAC vs digital signature for webhooks for why a shared-secret HMAC is a fair fit here.
import { verifyWebhook } from "@sume-com/sdk";
export async function POST(request: Request) {
const body = await request.text(); // raw, before JSON.parse
const ok = await verifyWebhook({
body,
headers: request.headers,
secret: process.env.SUME_COM_WEBHOOK_SIGNING_SECRET!,
});
if (!ok) return new Response("bad signature", { status: 401 });
return new Response(null, { status: 204 });
}Sources
Related posts
More in Developers
- Demand Gen image assets in every ratio from one reference
Loop one product reference through POST /v1/images to get the 1:1, 4:5, 9:16 and 1.91:1 Demand Gen image sizes, with notes on which pixel sizes are valid.
- Does a failed Sume video job send a webhook to callback_url?
Yes. A job that ends in failure sends job.failed with a public error to the HTTPS callback_url you set on POST /v1/videos, signed like every Sume webhook.
- Get the rendered MP4 back from a video editing API job
Descript's API lists no rendered file download without publishing. In Sume, a finished timeline_render job result carries video_url; here is the poll flow.
- Durable Object waitUntil keep-alive and a Sume job poll
Pending I/O now keeps a Durable Object alive without a client, but a Sume video job is better tracked by job id, status_url polling or a webhook.
Written by Sume