Why Verify Webhooks
Your webhook endpoint lives on the public internet. Anyone who discovers the URL — accidentally or intentionally — can send requests to it. From your server's perspective, a POST from the Connect API and a POST from an attacker look identical until you check the signature.
Verification is how you tell the difference. Before you act on a webhook, you confirm two things:
- The request was actually sent by the Connect API (authenticity).
- The request hasn't been replayed or tampered with (integrity and freshness).
What can go wrong if you skip verification
Consider what a webhook typically triggers on your side: issuing a ticket, sending an email, updating a balance, granting access. An unverified endpoint happily performs all of those actions for whoever can reach the URL.
A few concrete attack scenarios:
Forged events. An attacker posts a crafted JSON body that looks like event_attendee.ticket_success. If you trust it, you hand out tickets for free.
Replay attacks. An attacker captures a legitimate webhook (from a log, a leaked request, a misconfigured proxy) and resends it later — possibly many times. Without a freshness check, you process the same event repeatedly.
URL leaks. Webhook URLs show up in all sorts of places: error trackers, client-side code, screenshots, support tickets, Git history. Assume the URL is public and that the signature is what keeps your handler safe.
What the signature protects
Every request we send includes three headers:
svix-id— A unique ID for this delivery attempt.svix-timestamp— A Unix timestamp (in seconds) marking when we sent the request.svix-signature— An HMAC-SHA256 signature over the ID, timestamp, and raw body, keyed with your endpoint's signing secret.
By checking the signature against the raw request body and rejecting requests whose timestamp is outside a tight window, you get:
- Authenticity — Only someone holding the signing secret could have produced that signature, and the only parties that know the secret are the Connect API and you.
- Integrity — If an attacker changes a single byte of the payload, the signature no longer matches.
- Replay protection — Old captured requests fall outside the timestamp tolerance and are rejected.
Rules of thumb
- Verify every request, including during development. Turning verification off "temporarily" is how it ends up off in production.
- Verify the raw request body — the exact bytes we sent. Don't parse, re-serialize, or transform JSON before hashing, because any whitespace or key-order change will invalidate the signature.
- Treat a verification failure as a hostile request. Return
401(or just drop the connection) and log it. - Store your signing secret like any other credential: in your secrets manager, not in source control.
When you're ready to implement verification, head to Verifying Webhook Signatures.