Skip to content
V2 (Legacy) API ReferenceGet started
Webhooks

Webhooks

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:

HeaderDescription
X-Webhook-EventThe event type (e.g., message.sent, message.received)
X-Webhook-Subscription-IDYour webhook subscription ID
X-Webhook-TimestampUnix timestamp (seconds) when the webhook was sent
X-Webhook-SignatureHMAC-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

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 for the full subscription lifecycle — create, list, retrieve, update, delete, and phone-number filtering.

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 createdWebhook version
Before 2026-02-032025-01-01
2026-02-03 or later2026-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.

GuaranteeValue
Response timeout10 seconds
Retry attempts10 per endpoint
Retry backoffExponential with jitter, capped at 10 minutes
Total retry window~25 minutes
Delivery modelAt-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