DOCUMENTATION

Verify webhooks

Use this guide with the client integration workflow. Webhook subscriptions are business-isolated resources managed with integrations.manage.

Register a subscription

Create a webhook through the resource endpoint with a private secret:

{
  "name": "Client integration",
  "url": "https://client.example.com/webhooks/route",
  "events": ["*"],
  "active": true,
  "secret": "REPLACE_WITH_A_RANDOM_SHARED_SECRET"
}

* receives supported events. For a narrower subscription, inspect your business event journal and subscribe to the actual event names needed. The URL must be reachable by the installation’s webhook worker. The secret belongs on the receiving server, not in frontend code.

Envelope and headers

Deliveries contain id, business_id, event, state, created_at, and data. The state distinguishes planning from published changes; an unpublished event must not be presented as proof that a driver received work. X-Route-Event-Id repeats the envelope ID for deduplication.

X-Route-Signature has the form t=UNIX_SECONDS,v1=HEX_HMAC. The worker signs the exact UTF-8 bytes of timestamp + "." + body with HMAC-SHA256.

Node.js verification example

Your web framework must preserve the raw request body before JSON parsing. Pass its Buffer and the signature header to this function:

import { createHmac, timingSafeEqual } from 'node:crypto';

export function verifyRouteWebhook(rawBody, header, secret, nowSeconds = Date.now() / 1000) {
  const match = /^t=(\d+),v1=([a-f0-9]{64})$/.exec(header ?? '');
  if (!match || !Buffer.isBuffer(rawBody)) return false;
  const [, timestamp, digest] = match;
  // Receiver policy: reject messages more than five minutes old or in the future.
  if (Math.abs(nowSeconds - Number(timestamp)) > 300) return false;
  const expected = createHmac('sha256', secret)
    .update(timestamp).update('.').update(rawBody).digest();
  const received = Buffer.from(digest, 'hex');
  return received.length === expected.length && timingSafeEqual(received, expected);
}

The five-minute tolerance is an example receiving policy, not a server configuration requirement. Keep clocks synchronized. Do not verify a parsed-and-reserialized body: even whitespace changes invalidate the signature.

After verification, check the business ID, persist the event with a uniqueness constraint on its ID, and return a 2xx response promptly. Process downstream changes asynchronously. Duplicate deliveries should succeed without repeating side effects. Failed deliveries are retried with backoff and eventually marked failed; reconcile using the events endpoint when needed.

Out-of-order or uncertain updates

Treat a webhook as a notification of a change, then read the current business record/plan using the view your product requires. Handle live execution and plan revisions rather than assuming delivery order equals the latest state. The current API does not define a universal write idempotency key or a public delivery-replay endpoint.