# Guide to Svix Webhooks: Features and Best Practices

Svix is webhook-sending infrastructure that many SaaS products use to deliver their webhooks. If you're integrating a service that runs on Svix, the webhooks you receive carry Svix's signature headers, and verifying them works the same way no matter which upstream product sent them. This guide is about receiving and verifying those deliveries reliably.

Because Svix is the sending side, the event types and payloads you get are defined by the upstream service, not by Svix. What Svix standardizes is the delivery mechanics: the headers, the signature scheme, secret rotation, and the replay window. Get those right once and every Svix-powered provider works the same way.

## What are Svix webhooks?

A Svix-delivered webhook is an HTTP POST with a JSON body and three signature headers. The signature scheme is the basis of the Standard Webhooks specification, so if you've verified Standard Webhooks deliveries before, this will look familiar. Each provider that sends through Svix gives you a signing secret (prefixed `whsec_`) in its own dashboard or Svix App Portal, which you use to verify deliveries.

## Svix webhook features

| Feature | Details |
| --- | --- |
| Signature headers | `svix-id`, `svix-timestamp`, `svix-signature` |
| Header aliases | `webhook-id`, `webhook-timestamp`, `webhook-signature` (accept both) |
| Signature scheme | base64 HMAC-SHA256 over `{id}.{timestamp}.{body}` |
| Signing key | base64-decoded portion of the secret after the `whsec_` prefix |
| Signature format | space-delimited `v1,<sig>` entries; match any (for rotation) |
| Replay protection | Reject timestamps more than 5 minutes off |
| Verification target | Raw request body |
| Event types | Defined by the upstream sender, not by Svix |
| SDKs | Official `svix` libraries (Node, Python, and more) |

## Setting up and finding your signing secret

You don't configure Svix directly; you configure webhooks in the upstream product that uses it. That product exposes your endpoint's signing secret (the `whsec_...` value) in its dashboard or in the Svix App Portal it embeds. Register your HTTPS endpoint there, subscribe to the event types the provider offers, and copy the signing secret into your environment.

Event names follow whatever convention the sender uses. A common envelope is `{"type": "<event.name>", "data": {...}}`, but that shape is a convention, not something Svix enforces, so branch on the field the provider documents.

## Securing Svix webhooks

Each delivery is signed with HMAC-SHA256 over the string `{svix-id}.{svix-timestamp}.{body}`. The key is the base64-decoded portion of your secret after the `whsec_` prefix. The `svix-signature` header can contain multiple space-delimited `v1,<signature>` entries (this is how secret rotation works), and a delivery is valid if any entry matches. Svix's libraries also reject timestamps more than five minutes from now, which is what gives the scheme its replay protection.

The official `svix` library handles all of this, including the header aliases and the multi-signature rotation case:

```javascript
const { Webhook } = require("svix");

// The whsec_-prefixed secret from the provider's dashboard / App Portal
const wh = new Webhook(process.env.WEBHOOK_SECRET);

app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  try {
    // Throws on a bad signature or a timestamp outside the 5-minute window
    const payload = wh.verify(req.body, {
      "svix-id": req.headers["svix-id"],
      "svix-timestamp": req.headers["svix-timestamp"],
      "svix-signature": req.headers["svix-signature"],
    });

    res.sendStatus(200); // acknowledge fast
    processQueue.add(payload); // process asynchronously
  } catch {
    res.sendStatus(400);
  }
});

```

The same check in Python:

```python
from svix.webhooks import Webhook, WebhookVerificationError

wh = Webhook(os.environ["WEBHOOK_SECRET"])  # whsec_-prefixed

@app.post("/webhook")
def webhook():
    try:
        payload = wh.verify(request.get_data(), dict(request.headers))
    except WebhookVerificationError:
        return "", 400

    # enqueue payload for async processing
    return "", 200

```

If you verify manually instead of using the library, remember three things: base64-decode the secret after `whsec_` before using it as the HMAC key, verify against the raw body, and accept the `webhook-id` / `webhook-timestamp` / `webhook-signature` aliases some senders use in place of the `svix-` prefix.

## Svix webhook limitations and pain points

### Event types and payloads are the sender's, not Svix's

The Problem: Svix standardizes delivery, not content. There's no universal catalog of event types; each provider defines its own, and the `{type, data}` envelope is a convention you can't assume is present.

Why It Happens: Svix is infrastructure the sender builds on. The domain events belong to the sender.

Workarounds:

* Read the specific provider's event reference and branch on the field they document.
* Validate each parsed payload against the schema you expect rather than assuming a shape.

How Hookdeck Can Help: Hookdeck gives you a consistent inspection and routing layer across every provider you receive from, Svix-powered or not, so you're not reasoning about each sender's quirks in your handler.

### Secret rotation produces multiple signatures

The Problem: During rotation, `svix-signature` carries more than one `v1,<sig>` entry. Verification code that reads only the first signature will reject valid deliveries signed with the other secret.

Why It Happens: Overlapping signatures are what let a sender rotate a secret without dropping in-flight deliveries.

Workarounds:

* Match against any entry in the header, not just the first.
* Use the official `svix` library, which handles this for you.

How Hookdeck Can Help: Hookdeck verifies Svix signatures at the edge, including the rotation case, so your application only ever sees pre-verified events.

### The five-minute timestamp window

The Problem: Deliveries with a `svix-timestamp` more than five minutes off are rejected as replay protection. If your handler is slow or briefly backed up, deliveries can age out before you verify them.

Why It Happens: The freshness window is what stops a captured signed payload from being replayed indefinitely.

Workarounds:

* Acknowledge fast and process asynchronously so verification happens within the window.
* Keep NTP in sync on your servers; clock skew eats into the tolerance.

How Hookdeck Can Help: Hookdeck verifies each delivery once at the edge and durably queues it, so a downstream backlog no longer risks deliveries aging out of the freshness window.

## Best practices

### Use the official Svix library

It handles the base64 secret decoding, the `{id}.{timestamp}.{body}` construction, the multi-signature rotation case, the header aliases, and the timestamp tolerance. Manual verification has to get all five right.

### Verify against the raw body

The signature is computed over the exact bytes sent. Capture the raw body before any JSON middleware parses it.

### Accept both header sets

Some senders emit `webhook-id` / `webhook-timestamp` / `webhook-signature` instead of the `svix-` prefix. Accept either so you're not caught out by a Professional/Enterprise-tier sender.

### Dedupe on the message ID

Use `svix-id` as an idempotency key so retries and replays don't double-process. See our [guide to webhook idempotency](/webhooks/guides/implement-webhook-idempotency).

### Acknowledge fast, process asynchronously

Return 200 as soon as the signature verifies, then hand the event to a queue. See [why to process webhooks asynchronously](/webhooks/guides/why-implement-asynchronous-processing-webhooks).

## Conclusion

Svix standardizes the delivery side of webhooks for the many providers built on it: consistent `svix-id` / `svix-timestamp` / `svix-signature` headers, an HMAC-SHA256 scheme over `id.timestamp.body`, overlapping signatures for rotation, and a five-minute replay window. Learn it once and every Svix-powered sender verifies the same way, though the event types and payloads always belong to the upstream service.

The receiving-side responsibilities are still yours: verify against the raw body, handle rotation and the header aliases, stay inside the freshness window, and dedupe on `svix-id`. For teams integrating several providers at once, [Hookdeck](https://hookdeck.com) verifies Svix signatures, deduplicates, and durably queues events at the edge, giving you one reliable ingestion layer across every source.

[Get started with Hookdeck](https://dashboard.hookdeck.com/signup) for free and handle Svix-delivered webhooks reliably in minutes.