Integration authentication and webhook signatures

Use API keys or timestamped HMAC and distinguish inbound and outbound secrets.

Inbound integration requests

Configure the integration's auth method. API_KEY requires x-api-key with an active, unexpired integration key. HMAC_SIGNATURE requires x-signature and x-timestamp, using the integration secret.

The inspected guard signs timestamp + "." + raw JSON body, with HMAC-SHA256 and a lowercase hex digest, without a sha256= prefix. Timestamp is Unix seconds; the guard accepts at most 300 seconds difference from the server clock. This time check is not a per-event deduplication guarantee.

Signing example (Node.js)

Serialize once, sign that same string, and send it without reformatting. Supply URL and secret through server-side configuration.

import { createHmac } from 'node:crypto';
const body = JSON.stringify({ event: 'example.event', data: { reference: 'TEST-1' } });
const timestamp = Math.floor(Date.now() / 1000).toString();
const signature = createHmac('sha256', process.env.INTEGRATION_SECRET)
  .update(timestamp + '.' + body).digest('hex');
const response = await fetch(process.env.INBOUND_WEBHOOK_URL, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json',
    'x-timestamp': timestamp, 'x-signature': signature },
  body,
});
if (!response.ok) throw new Error('HTTP ' + response.status);

The example event is illustrative; select an event supported by your configured integration and workflow. Copy the actual inbound URL from configuration. The inspected router path is /api/v1/webhooks/:businessId/:integrationType/inbound.

Verify outbound delivery

When a webhook secret is configured, the inspected integration delivery service sends the same x-signature/x-timestamp scheme, using the webhook's secret, not the integration's inbound secret. Capture the raw request body before parsing. Compute the expected hex digest and compare with a constant-time function after checking equal byte lengths. Enforce your timestamp tolerance and handle duplicate business effects safely. If no webhook secret is configured, do not treat a request as signed.

Delivery behavior and errors

The inspected integration delivery service uses a 10-second HTTP timeout and defaults to three retries after thrown transport errors, with 1s, 5s, and 15s delays. Its non-2xx HTTP responses are recorded as errors but do not themselves throw for retry. Other senders or deployed versions may differ; inspect delivery records and test your actual path. Do not advertise guaranteed delivery.

Missing/invalid credentials or expired timestamps produce authentication errors; inactive/missing integrations can produce forbidden errors. Synchronize clocks, preserve exact bytes, and check which secret belongs to which direction.