Webhook Subscriptions
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:
timestampis the value from theX-Webhook-Timestampheaderpayloadis the raw JSON request body (exact bytes, not re-serialized)
Verification Steps:
- Extract the
X-Webhook-TimestampandX-Webhook-Signatureheaders - Get the raw request body bytes (do not parse and re-serialize)
- Concatenate:
"{timestamp}.{payload}" - Compute HMAC-SHA256 using your signing secret as the key
- Hex-encode the result and compare with
X-Webhook-Signature - 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