Skip to main content

FAQ

Quick answers to the questions we hear most often. For the full walkthrough, see Verifying Webhook Signatures.

What encryption / signature method do you use?

Webhooks are signed with HMAC-SHA256. The HMAC key is your endpoint's signing secret (the part after whsec_, base64-decoded). The signature itself is base64-encoded and sent in the svix-signature header.

To be precise, we're not encrypting the payload — the body is plain JSON over HTTPS. What the signature provides is authenticity (proof that the request came from us) and integrity (proof that the body wasn't modified in transit). TLS handles confidentiality.

What headers do you send?

Every webhook delivery includes three signature-related headers:

  • svix-id — A unique identifier for the delivery. Same across retries of the same event. Use this as your idempotency key.
  • svix-timestamp — The Unix timestamp (in seconds) for this delivery attempt.
  • svix-signature — A space-separated list of version,signature pairs (currently v1,<base64>).

In addition, you'll get the standard HTTP headers (Content-Type: application/json, User-Agent, etc.) and any custom headers you configured on the endpoint.

How do you protect against replay attacks?

Two mechanisms working together:

  1. The timestamp is signed. svix-timestamp is part of what the HMAC covers, so an attacker can't change it without invalidating the signature.
  2. You enforce a freshness window. When verifying, reject any request whose timestamp is more than 5 minutes from your server's current time. This prevents attackers from replaying an old, legitimately-signed request months later.

If you additionally want to guard against an attacker replaying a request within the 5-minute window, track the svix-id values you've already processed and reject duplicates. This is the same idempotency strategy you already need for handling our retries, so you get replay protection within the window as a free side effect.

Do I need to use the raw payload to verify?

Yes. The signature is computed over the exact bytes of the request body. If you parse the JSON and then re-serialize it to pass to your verification function, the result will almost never match — JSON libraries reorder keys, change whitespace, and normalize number formats differently from our signer.

How to get the raw body depends on your framework:

  • Express: Use express.raw({ type: 'application/json' }) on the webhook route. Your handler will get req.body as a Buffer. Do not also apply express.json() to that route.
  • FastAPI / Starlette: Call await request.body() to get the raw bytes before any Pydantic parsing.
  • Flask: request.get_data() returns the raw body.
  • Django: request.body (not request.POST) is the raw body.
  • Go net/http: io.ReadAll(r.Body), then pass the same bytes both to JSON parsing and to verification.
  • Rails: request.raw_post.

The rule of thumb: if your verification is failing on every request but your code looks correct, you're almost certainly verifying re-serialized JSON.

What status code should my endpoint return?

Any 2xx. 200 is fine. Return it as soon as you've durably accepted the event — before any slow downstream processing. Return it within 15 seconds or the delivery will be treated as a timeout.

Return a non-2xx if you want us to retry (e.g., your database is down and you genuinely can't accept the event right now). Return a 4xx if the request is unverifiable or malformed — we'll still retry, but you've correctly refused to process a bad request.

Will I ever receive the same event twice?

Yes — delivery is at-least-once. Use svix-id as an idempotency key and deduplicate on your side. See Retries and Failure Handling for details.

What happens if my endpoint is down?

We retry with an exponential backoff schedule that spans roughly 24 hours. After 8 failed attempts, we stop retrying that specific delivery. See Retries and Failure Handling for the full schedule.

Can I use HTTP instead of HTTPS?

No. Webhook URLs must use HTTPS.

Can I test webhooks without exposing a real endpoint?

For local development, tools like ngrok or Webhook Site. Register that URL as a webhook endpoint while you're developing, and swap it for your production URL when you ship.

How do I rotate the signing secret?

Call POST /api/connect/v1/webhooks/{webhook_id}/secret/rotate. The response contains the new secret. The old secret stops signing new requests immediately, so if you need a clean cutover, have your verification code accept both secrets during the transition window.