Skip to content
Console

Webhook notifications

An organization owner or admin can have change receipts pushed to an HTTPS receiver. Webhooks are set up in the Gateway console on the Webhooks page, for one website and one installation credential.

  • The credential has website_read and event_read and no restrictions.
  • INSIDE has allowed the receiver’s origin.
  • You’ve stored the signing secret, which is shown once when the webhook is created.
{
"version": 1,
"delivery_id": "whd_...",
"webhook_id": "whk_...",
"website_id": "web_4927_123",
"event": { "cursor": 1, "type": "conversation.changed", "occurred_at": 1760313600000 }
}
  1. Compute HMAC-SHA256 over the exact bytes of X-Gateway-Timestamp + "." + raw_body, using the base64-decoded signing secret.

  2. Compare it in constant time with the hex value in X-Gateway-Signature: v1=<hex>. Verify before parsing the body.

  3. Reject stale timestamps. X-Gateway-Timestamp is in Unix seconds; occurred_at is in milliseconds.

  4. Check that X-Gateway-Delivery-ID and X-Gateway-Webhook-ID match the body.

import { createHmac, timingSafeEqual } from 'node:crypto';
export function verify(headers, rawBody, base64Secret, maxAgeSeconds = 300) {
const timestamp = headers['x-gateway-timestamp'];
const signature = (headers['x-gateway-signature'] ?? '').replace(/^v1=/, '');
if (!timestamp || Math.abs(Date.now() / 1000 - Number(timestamp)) > maxAgeSeconds) return false;
const expected = createHmac('sha256', Buffer.from(base64Secret, 'base64'))
.update(Buffer.concat([Buffer.from(`${timestamp}.`), rawBody]))
.digest();
const received = Buffer.from(signature, 'hex');
return received.length === expected.length && timingSafeEqual(received, expected);
}
  • Delivery is at least once. Return 2xx only after you’ve stored the notification, and deduplicate on delivery_id.
  • Each delivery gets up to five attempts, with a 10-second timeout and exponential backoff.
  • Notifications are kept for seven days and can be replayed from the console.

After you receive a notification, read the changed resources through the REST API with your installation credential.