> ## Documentation Index
> Fetch the complete documentation index at: https://docs.offthehook.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook Delivery Headers

> Every Off the Hook webhook POST includes Standard Webhooks signature headers plus an extension header. Use these to verify authenticity and route deliveries.

Every webhook POST that Off the Hook sends to your endpoint includes a fixed set of HTTP headers. Three of these — `webhook-id`, `webhook-timestamp`, and `webhook-signature` — are defined by the [Standard Webhooks specification](https://www.standardwebhooks.com) and are what the Svix verification libraries expect. The remaining headers are either standard HTTP conventions or Off the Hook extensions.

## Headers

| Header                  | Example value       | Description                                                                                     |
| ----------------------- | ------------------- | ----------------------------------------------------------------------------------------------- |
| `content-type`          | `application/json`  | Always `application/json` — the body is always a JSON object                                    |
| `webhook-id`            | `evt_8xN9kP2QbA...` | Unique event ID; stays the same across every retry of the same event — use it for deduplication |
| `webhook-timestamp`     | `1730000000`        | Unix timestamp in seconds when the webhook was created                                          |
| `webhook-signature`     | `v1,<base64>`       | HMAC-SHA256 signature; multiple space-separated values appear during secret rotation            |
| `x-oth-subscription-id` | `sub_2QkP9aB7xN...` | The subscription that triggered this delivery — useful for routing in a shared endpoint         |
| `user-agent`            | `OffTheHook/0.1`    | Identifies Off the Hook as the sender                                                           |

## Signature format

The `webhook-signature` header contains one or more space-separated signature tokens, each prefixed with the version string `v1,`:

```
webhook-signature: v1,<base64-encoded-hmac-sha256>
```

During a [secret rotation grace period](/guides/rotate-secrets), both the new and old secrets are active simultaneously. Off the Hook signs the payload with both secrets and includes both signatures in a single header value, new secret first:

```
webhook-signature: v1,<new-secret-sig> v1,<old-secret-sig>
```

Your verification code should accept a delivery if **any** of the provided signatures matches your current secret. The Svix library handles this automatically.

The signature covers the string `${webhook-id}.${webhook-timestamp}.${body}` — the event ID, timestamp, and raw request body concatenated with literal periods and HMAC-SHA256 signed with your subscription secret.

## Deduplication

Off the Hook retries deliveries when your endpoint returns `5xx`, `408`, `429`, or the connection fails entirely. Every retry of the same event uses the **same `webhook-id`** value. Store the IDs you have already successfully processed and skip any delivery that arrives with a duplicate ID, returning `2xx` so the retry stops.

## Timestamp validation

Reject any delivery where the `webhook-timestamp` is more than 5 minutes older than the current time. This defends against replay attacks where a valid captured request is re-sent to your endpoint later. The Svix verification library enforces this window automatically when you call `wh.Verify()`.

```typescript theme={null}
// Svix enforces the 5-minute window for you
const wh = new Webhook(process.env.WEBHOOK_SECRET!);
const event = wh.verify(body, headers); // throws if timestamp is stale
```

For full signature verification examples in TypeScript, Go, Python, and other languages, see [Verify Webhook Signatures](/guides/verify-signatures).
