---
title: Webhooks | API Docs
description: Real-time event notifications for messages, reactions, chats, and more.
---

Webhook Subscriptions allow you to receive real-time notifications when events occur on your account.

Configure webhook endpoints to receive events such as messages sent/received, delivery status changes, reactions, typing indicators, and more.

Failed deliveries (5xx, 429, network errors) are retried up to 10 times over \~25 minutes with exponential backoff. Each event includes a unique ID for deduplication.

## Webhook Headers

Each webhook request includes the following headers:

| Header                      | Description                                               |
| --------------------------- | --------------------------------------------------------- |
| `X-Webhook-Event`           | The event type (e.g., `message.sent`, `message.received`) |
| `X-Webhook-Subscription-ID` | Your webhook subscription ID                              |
| `X-Webhook-Timestamp`       | Unix timestamp (seconds) when the webhook was sent        |
| `X-Webhook-Signature`       | HMAC-SHA256 signature for verification                    |

## Verifying Webhook Signatures

All webhooks are signed using HMAC-SHA256. You should always verify the signature to ensure the webhook originated from Linq and hasn’t been tampered with.

**Signature Construction:**

The signature is computed over a concatenation of the timestamp and payload:

```
{timestamp}.{payload}
```

Where:

- `timestamp` is the value from the `X-Webhook-Timestamp` header
- `payload` is the raw JSON request body (exact bytes, not re-serialized)

**Verification Steps:**

1. Extract the `X-Webhook-Timestamp` and `X-Webhook-Signature` headers
2. Get the raw request body bytes (do not parse and re-serialize)
3. Concatenate: `"{timestamp}.{payload}"`
4. Compute HMAC-SHA256 using your signing secret as the key
5. Hex-encode the result and compare with `X-Webhook-Signature`
6. Use constant-time comparison to prevent timing attacks

**Example (Python):**

```
import hmac
import hashlib

def verify_webhook(signing_secret, payload, timestamp, signature):
    message = f"{timestamp}.{payload.decode('utf-8')}"
    expected = hmac.new(
        signing_secret.encode('utf-8'),
        message.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)
```

**Example (Node.js):**

```
const crypto = require('crypto');

function verifyWebhook(signingSecret, payload, timestamp, signature) {
  const message = `${timestamp}.${payload}`;
  const expected = crypto
    .createHmac('sha256', signingSecret)
    .update(message)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}
```

**Security Best Practices:**

- Reject webhooks with timestamps older than 5 minutes to prevent replay attacks
- Always use constant-time comparison for signature verification
- Store your signing secret securely (e.g., environment variable, secrets manager)
- Return a 2xx status code quickly, then process the webhook asynchronously

## Setting up webhooks

Create a webhook subscription to start receiving events. Each subscription targets a URL you own, filters to the events you care about, and returns a signing secret you use to verify inbound requests. See [Webhook Subscriptions](/guides/webhooks/subscriptions/index.md) for the full subscription lifecycle — create, list, retrieve, update, delete, and phone-number filtering.

## Webhook versioning

Webhook payloads are versioned using dates. Specify a version by adding `?version=YYYY-MM-DD` to your subscription URL:

```
https://your-server.com/webhook?version=2026-02-03
```

| Subscription created | Webhook version |
| -------------------- | --------------- |
| Before 2026-02-03    | `2025-01-01`    |
| 2026-02-03 or later  | `2026-02-03`    |

If no version is specified, the subscription uses the latest available version at creation time.

> **Tip:** Always specify a version explicitly to avoid unexpected payload format changes.

## Delivery guarantees

| Guarantee          | Value                                         |
| ------------------ | --------------------------------------------- |
| Response timeout   | 10 seconds                                    |
| Retry attempts     | 10 per endpoint                               |
| Retry backoff      | Exponential with jitter, capped at 10 minutes |
| Total retry window | \~25 minutes                                  |
| Delivery model     | At-least-once (duplicates possible)           |

**Retried:** HTTP 5xx, HTTP 429, connection timeout, connection refused. **Not retried:** HTTP 4xx (except 429), DNS failures, invalid hostnames.

Your endpoint should:

1. Return `200` quickly — process asynchronously if needed
2. Verify the HMAC signature using your subscription’s signing secret
3. Deduplicate using `event_id`
4. Be idempotent

## Related

- [Webhook Subscriptions](/guides/webhooks/subscriptions/index.md) — create, list, update, delete subscriptions
- [Webhook Events](/guides/webhooks/events/index.md) — full event list, shared envelope, and representative payloads
- [API Reference: Webhook Events](/api/resources/webhook_events/index.md)
- [API Reference: Webhook Subscriptions](/api/resources/webhook_subscriptions/index.md)
