Accounting APIv1

Webhooks

Webhooks push events to an endpoint you host, instead of you polling the API. Register an endpoint at Developer → Webhooks (/settings/webhooks) — provide your HTTPS URL and choose which event types to receive.

The delivery headers

Each delivery is an HTTP POST with a JSON body and these headers:

| Header | Meaning | | --- | --- | | X-Webhook-Signature | The HMAC signature (see below). | | X-Webhook-Timestamp | The Unix timestamp the signature was computed over. | | X-Webhook-Event | The event type (the catalog event key). | | X-Webhook-Id | The delivery id — use it as a dedupe key. |

Verifying the signature

Deliveries are signed Stripe-style: an HMAC-SHA256 over ${timestamp}.${rawBody} keyed by the endpoint's signing secret. The X-Webhook-Signature header value has the form t=<timestamp>,v1=<sig>. To verify, recompute the HMAC over the X-Webhook-Timestamp and the exact raw request body you received, and compare it to the v1= value:

import { createHmac, timingSafeEqual } from 'node:crypto';
 
function verify(rawBody, signatureHeader, timestampHeader, signingSecret) {
  const parts = Object.fromEntries(signatureHeader.split(',').map((p) => p.split('=')));
  const expected = createHmac('sha256', signingSecret)
    .update(`${timestampHeader}.${rawBody}`)
    .digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1 ?? '');
  return a.length === b.length && timingSafeEqual(a, b);
}
Verify before you trust

Always verify the signature against the raw, unparsed body before acting on a delivery — and reject deliveries whose X-Webhook-Timestamp is too old to limit replay. The signing secret is shown on the Webhooks settings page; treat it like an API-key secret.

Retries and redelivery

A delivery that does not get a 2xx is retried automatically on an exponential backoff. After the maximum number of attempts, the delivery is marked dead (dead-lettered). You can inspect every attempt — and manually redeliver any delivery — from the Webhooks page in the app.

Respond 2xx quickly and do your heavy work asynchronously, so a slow handler does not cause a retry.