Whatβs on each delivery
Every request carries three headers:
Your signing secret (prefixed
whsec_) is shown in plaintext once β when you create a
webhook endpoint or rotate its secret. Store it securely; you cannot retrieve it again.
Verify with a Svix library (recommended)
The library handles the construction, the multi-signature format, timing-safe comparison, and timestamp (replay) checks for you.Svix maintains official verification libraries for these and more languages. Install with your
package manager β
npm i svix, pip install svix, go get github.com/svix/svix-webhooks/go,
gem install svix, composer require svix/svix, or Maven/Gradle (com.svix:svix) β and see the
full list.Verify manually
If thereβs no Svix library for your stack, reproduce the signature yourself:1
Build the signed content
Join the id, timestamp, and the raw body with dots:
signed_content = svix_id + "." + svix_timestamp + "." + raw_body2
Derive the key
Drop the
whsec_ prefix from your signing secret and base64-decode the remainder. Those bytes
are your HMAC key.3
Sign
Compute
base64(HMAC-SHA256(key, signed_content)).4
Compare
The
svix-signature header is a space-separated list of v1,<signature> entries (an endpoint can
have more than one valid secret during a rotation). Compare your value against each entryβs
signature β the part after v1, β using a constant-time comparison. Accept if any matches.5
Check the timestamp
Reject deliveries whose
svix-timestamp is more than a few minutes from your current time, to
guard against replay.Deduplicating events
The bodyβsevent_id equals the svix-id without its msg_ prefix. Use either to deduplicate β
a delivery may be retried, and the same event_id arrives more than once. Treat your handler as
idempotent: record processed event_ids and ignore repeats.