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.

4 min readSume
All posts

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.

Sume webhook signature facts from the docs, read 2026-09-30: https://docs.sume.com/workflows/webhooks
PartSume value
Timestamp headerx-sume-webhook-timestamp: 1780000000
Signature headerx-sume-webhook-signature: sume-v1=<hex_signature>
Signed string<timestamp>.<raw_body>
AlgorithmHMAC SHA-256 with your signing secret
During secret rotationOne sume-v1= entry per live secret, comma-separated, newest first
Replay windowReject 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

All Developers posts

Written by Sume