For AI agents: a documentation index is available at the root level at /llms.txt and /llms-full.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
DashboardGet an API key
Get StartedEventsToolsChangelog
Get StartedEventsToolsChangelog
  • Welcome
    • Overview
    • Quickstart
  • Concepts
    • Event envelope
    • Receiving webhooks
    • Signature verification
    • Retries and backoff
    • Testing
LogoLogo
DashboardGet an API key
Concepts

Signature verification

|View as Markdown|Open in Claude|
Was this page helpful?
Previous

Receiving webhooks

Next

Retries and backoff

Built with

Pivotal signs every delivery with the endpoint’s signing secret. The Svix SDK handles verification for you; this page documents what it does so you can implement it manually if you want.

HEADERS ON EVERY REQUEST
HeaderExampleNotes
svix-idmsg_2nQv8c9aZmYHKr1...Delivery id. Different from event.id.
svix-timestamp1748286120Unix seconds. Reject if older than 5 minutes.
svix-signaturev1,EXample...,v1,EXampleSecondary...Space-separated version,signature pairs.
VERIFICATION RECIPE
manual-verify.ts
1import crypto from "node:crypto";
2
3function verify(secret: string, id: string, ts: string, sig: string, body: string) {
4 // signing secret is base64; the value after "whsec_"
5 const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
6 const toSign = `${id}.${ts}.${body}`;
7 const expected = crypto.createHmac("sha256", key).update(toSign).digest("base64");
8
9 // sig header can hold multiple signatures (rotation)
10 const ours = sig.split(" ").some((pair) => pair.split(",")[1] === expected);
11 if (!ours) throw new Error("bad signature");
12
13 if (Math.abs(Math.floor(Date.now() / 1000) - Number(ts)) > 300) {
14 throw new Error("timestamp too old");
15 }
16}
SECRET ROTATION

The Webhooks dashboard lets you rotate the signing secret with a 24-hour overlap. Deliveries during the overlap window carry two signatures so your old key keeps validating while you deploy the new one. The svix-signature header lists both, separated by a space — see the recipe above for how to handle multiple candidates.