Receiving webhooks
How Karma delivers events to your endpoint — the envelope, signing, retries and ordering.
Karma pushes events to an HTTPS endpoint you control. Subscribe once, then handle events as they happen instead of polling for changes.
The envelope
Every event arrives as a POST with the same outer shape. Only data differs
between event types — its schema is documented on each event's page.
{
"id": "9f1c…",
"event": "order.created",
"resource": "order",
"resourceId": "3b7e…",
"timestamp": "2026-08-02T09:41:17.000Z",
"apiVersion": "v1",
"data": { }
}| Field | Description |
|---|---|
id | Unique id for this delivery group. Stable across retries — use it to deduplicate. |
event | Event name, e.g. order.created. |
resource / resourceId | The entity the event is about. |
timestamp | When the event occurred, ISO 8601 UTC. |
apiVersion | Envelope version. Currently always v1. |
data | Event-specific payload. |
Custom fields and metadata
HTTP webhooks include the object's API-readable custom fields in two forms:
customFields: values with their field ID, stable key, label, group, type, display value, version and status.metadata.customFields: the same values keyed by the field's stable key. Other existing metadata is preserved.
For inventory, booking and order events these properties live directly under
data. Tab events that already contain a tab reference put them under
data.tab; other tab events put them under data. Existing location, tab,
item, purchase, table and guest reference blocks are enriched too,
including items[].item. A booking with a purchaseId also gets a purchase
reference. A staff user reference is not a guest profile.
Only fields enabled for API reading are included, respecting inherited
restrictions and the subscription's API-key permissions and location access.
Company-owned subscriptions without an API key receive API-readable fields
for their matched location. Unset fields are omitted; explicitly cleared
values remain null. Deleted or inaccessible records have empty field
projections. Decimal and money values retain the API's exact representation.
Values are captured when outbound dispatch is prepared, which can be later than the event timestamp. They are not sale-time snapshots. Once saved for a delivery, the values stay unchanged across retries, even if the fields are edited afterwards. The signature covers the entire payload, including fields. These subscriber-specific values are not added to browser realtime broadcasts.
This enrichment does not introduce new event types or emit an event merely because a custom field was edited. In particular, there is currently no standalone public purchase webhook; purchase references are enriched within existing events.
Verifying deliveries
Each request carries an X-Webhook-Signature header: an HMAC-SHA256 of the raw
request body, keyed with your subscription's signing secret.
Verify before you trust
Compute the HMAC over the raw body bytes, before any JSON parsing, and compare using a constant-time comparison. Reject any request whose signature does not match.
Responding
Respond 2xx within 30 seconds to acknowledge delivery. Anything else —
including a timeout — is treated as a failure and retried.
Acknowledge first, process afterwards: do the minimum work needed to persist
the event, return 2xx, and handle the rest asynchronously. Slow handlers are
the most common cause of duplicate deliveries.
Retries, ordering and duplicates
- Retries — failed deliveries are retried with backoff. Your endpoint must be idempotent.
- Duplicates — at-least-once delivery. The same
idmay arrive more than once; deduplicate on it. - Ordering — not guaranteed. Use
timestampto order events, and don't assumeorder.updatedarrives afterorder.created.
Subscribing
Create and manage subscriptions through the Webhooks endpoints in the API reference. A subscription is a URL, the set of events it should receive, and the location scope it applies to.