Skip to content

Register to Webhook Events API (2.0.0)

The Webhook APIs is a REST API designed to manage webhooks for booking notifications. It allows partners to register, update, and delete webhook endpoints, enabling them to receive real-time booking event updates.

Receiving events

Once a callback URL is registered, Bookable POSTs a JSON BookingNotification to it for each event. Delivery is at-least-once: the same event may be delivered more than once, so consumers must be idempotent (booking events by booking id, messages by message id).

Each request carries an X-API-Key header containing a lowercase hex HMAC-SHA256 signature of the raw request body, keyed with the secretKey of the subscription it was sent for. Verify it by recomputing the HMAC over the received body and comparing.

It also carries an X-Webhook-Id header naming that subscription. If you hold more than one subscription, use it to pick the right secretKey rather than inferring it from the URL the request arrived on. A single-subscription integration can ignore the header.

Answer within 10 seconds. Timeouts, retries and redelivery per event type are described in the Handling Webhook Events API.

Subscriptions

A distributor may register several webhook subscriptions, each with its own callback URL, secret and event filter — see /webhooks/subscriptions. This lets an application acting on your behalf subscribe without disturbing your own integration.

A distributor may hold up to 10 subscriptions, paused ones included; creating an 11th answers 409 with code WHK-N-004. Subscriptions belong to one environment: one created in the sandbox does not exist in Live, and the reverse.

Event types (eventType):

  • booking.updated — a booking was created or changed (payload in bookings).
  • message.received — one or more messages for a booking (payload in messages). Emitted only for operator_to_partner messages (the venue operator sent it, delivered to the partner); filter on subject/senderEmail.
  • venue.updated — a venue you distribute changed status; today only closures are sent (payload in venues).
  • webhook.ping — a connectivity test you asked for with POST /webhooks/test; no payload. Test deliveries also carry an X-Webhook-Test: true header.

Switch on eventType and answer 2xx to types you do not handle: new ones are added over time.

Testing

POST /webhooks/test sends a test event to your subscriptions and reports what each endpoint answered. In Live it sends webhook.ping; in the sandbox it also sends sample, real and simulated business events.

Changes in 2.0.0

  • The singleton operations GET, POST, PUT and DELETE /webhooks, deprecated since 1.6.0, are removed. Use /webhooks/subscriptions.
  • POST /webhooks/test is redefined — see that operation.
  • BookingNotification documents venue.updated (with venues) and webhook.ping.
Download OpenAPI description
Languages
Servers
Live
https://api.bookabletech.com
Sandbox
https://api-sandbox.bookabletech.com

webhook

Operations related to webhook management

Operations

List your webhook subscriptions

Request

Every subscription registered for the calling distributor. Secrets are never returned — each is shown once, when the subscription is created or its secret rotated.

Security
Live or Sandbox
curl -i -X GET \
  https://api.bookabletech.com/webhooks/subscriptions \
  -H 'Authorization: Bearer <YOUR_TOKEN_HERE>'

Responses

The current subscriptions

Bodyapplication/json
dataArray of objects(WebhookSubscription)

Your subscriptions. Omitted when you have none — Bookable leaves empty lists out of every response, so treat a missing data as [].

Response
application/json
{ "data": [ { … } ] }

Create a webhook subscription

Request

Registers an additional callback URL with its own secret and event filter. The secretKey in the response is shown once — store it. Creating a subscription never affects any other.

Security
Live or Sandbox
Bodyapplication/jsonrequired
callbackUrlstring(uri)required

HTTPS URL to be called when an event occurs. Must resolve to a public address.

Example: "https://partner.example.com/webhooks/bookings"
eventTypesArray of strings or null(WebhookEventType)

Restrict this subscription to these event types. Omit for every event.

Enum"booking.updated""message.received""venue.updated"
labelstring or null

Your own name for this subscription.

curl -i -X POST \
  https://api.bookabletech.com/webhooks/subscriptions \
  -H 'Authorization: Bearer <YOUR_TOKEN_HERE>' \
  -H 'Content-Type: application/json' \
  -d '{
    "callbackUrl": "https://partner.example.com/webhooks/bookings",
    "eventTypes": [
      "booking.updated"
    ],
    "label": "string"
  }'

Responses

Subscription created

Bodyapplication/json
webhookIdstringrequired

Opaque id. Sent with every delivery in the X-Webhook-Id header.

Example: "whk_3f2b8c1e5a7d4e9b9c0a1d2e3f4a5b6c"
callbackUrlstring(uri)required

HTTPS URL this subscription's notifications are POSTed to.

Example: "https://partner.example.com/webhooks/bookings"
eventTypesArray of strings or null(WebhookEventType)

The event types this subscription receives. null or absent means every event.

Enum"booking.updated""message.received""venue.updated"
labelstring or null

Your own name for this subscription, to tell several apart.

Example: "Order management system"
activebooleanrequired

Whether this subscription currently receives deliveries.

isDefaultbooleanrequired

Legacy marker for the subscription carried over from the single-webhook model (before 1.6.0). The singleton operations it served were removed in 2.0.0; it has no effect on delivery.

createdAtstring or null(date-time)
updatedAtstring or null(date-time)
secretKeystringrequired

The HMAC-SHA256 key for this subscription's X-API-Key header. Shown once; it cannot be read back. Rotate it with POST /webhooks/subscriptions/{webhookId}/secret.

Response
application/json
{ "webhookId": "whk_3f2b8c1e5a7d4e9b9c0a1d2e3f4a5b6c", "callbackUrl": "https://partner.example.com/webhooks/bookings", "eventTypes": [ "booking.updated" ], "label": "Order management system", "active": true, "isDefault": true, "createdAt": "2019-08-24T14:15:22Z", "updatedAt": "2019-08-24T14:15:22Z", "secretKey": "string" }

Get one webhook subscription

Request

Security
Live or Sandbox
Path
webhookIdstringrequired

The subscription's opaque id, as returned at creation and sent in X-Webhook-Id.

Example: whk_3f2b8c1e5a7d4e9b9c0a1d2e3f4a5b6c
curl -i -X GET \
  https://api.bookabletech.com/webhooks/subscriptions/whk_3f2b8c1e5a7d4e9b9c0a1d2e3f4a5b6c \
  -H 'Authorization: Bearer <YOUR_TOKEN_HERE>'

Responses

The subscription

Bodyapplication/json
webhookIdstringrequired

Opaque id. Sent with every delivery in the X-Webhook-Id header.

Example: "whk_3f2b8c1e5a7d4e9b9c0a1d2e3f4a5b6c"
callbackUrlstring(uri)required

HTTPS URL this subscription's notifications are POSTed to.

Example: "https://partner.example.com/webhooks/bookings"
eventTypesArray of strings or null(WebhookEventType)

The event types this subscription receives. null or absent means every event.

Enum"booking.updated""message.received""venue.updated"
labelstring or null

Your own name for this subscription, to tell several apart.

Example: "Order management system"
activebooleanrequired

Whether this subscription currently receives deliveries.

isDefaultbooleanrequired

Legacy marker for the subscription carried over from the single-webhook model (before 1.6.0). The singleton operations it served were removed in 2.0.0; it has no effect on delivery.

createdAtstring or null(date-time)
updatedAtstring or null(date-time)
Response
application/json
{ "webhookId": "whk_3f2b8c1e5a7d4e9b9c0a1d2e3f4a5b6c", "callbackUrl": "https://partner.example.com/webhooks/bookings", "eventTypes": [ "booking.updated" ], "label": "Order management system", "active": true, "isDefault": true, "createdAt": "2019-08-24T14:15:22Z", "updatedAt": "2019-08-24T14:15:22Z" }

Update one webhook subscription

Request

Applies the supplied fields; anything omitted is left unchanged. The secret is not affected — use POST /webhooks/subscriptions/{webhookId}/secret to rotate it.

Security
Live or Sandbox
Path
webhookIdstringrequired

The subscription's opaque id, as returned at creation and sent in X-Webhook-Id.

Example: whk_3f2b8c1e5a7d4e9b9c0a1d2e3f4a5b6c
Bodyapplication/jsonrequired
callbackUrlstring or null(uri)
eventTypesArray of strings or null(WebhookEventType)
Enum"booking.updated""message.received""venue.updated"
labelstring or null
activeboolean or null

Set false to stop deliveries without deleting the subscription.

curl -i -X PUT \
  https://api.bookabletech.com/webhooks/subscriptions/whk_3f2b8c1e5a7d4e9b9c0a1d2e3f4a5b6c \
  -H 'Authorization: Bearer <YOUR_TOKEN_HERE>' \
  -H 'Content-Type: application/json' \
  -d '{
    "callbackUrl": "http://example.com",
    "eventTypes": [
      "booking.updated"
    ],
    "label": "string",
    "active": true
  }'

Responses

The updated subscription

Bodyapplication/json
webhookIdstringrequired

Opaque id. Sent with every delivery in the X-Webhook-Id header.

Example: "whk_3f2b8c1e5a7d4e9b9c0a1d2e3f4a5b6c"
callbackUrlstring(uri)required

HTTPS URL this subscription's notifications are POSTed to.

Example: "https://partner.example.com/webhooks/bookings"
eventTypesArray of strings or null(WebhookEventType)

The event types this subscription receives. null or absent means every event.

Enum"booking.updated""message.received""venue.updated"
labelstring or null

Your own name for this subscription, to tell several apart.

Example: "Order management system"
activebooleanrequired

Whether this subscription currently receives deliveries.

isDefaultbooleanrequired

Legacy marker for the subscription carried over from the single-webhook model (before 1.6.0). The singleton operations it served were removed in 2.0.0; it has no effect on delivery.

createdAtstring or null(date-time)
updatedAtstring or null(date-time)
Response
application/json
{ "webhookId": "whk_3f2b8c1e5a7d4e9b9c0a1d2e3f4a5b6c", "callbackUrl": "https://partner.example.com/webhooks/bookings", "eventTypes": [ "booking.updated" ], "label": "Order management system", "active": true, "isDefault": true, "createdAt": "2019-08-24T14:15:22Z", "updatedAt": "2019-08-24T14:15:22Z" }

Delete one webhook subscription

Request

Security
Live or Sandbox
Path
webhookIdstringrequired

The subscription's opaque id, as returned at creation and sent in X-Webhook-Id.

Example: whk_3f2b8c1e5a7d4e9b9c0a1d2e3f4a5b6c
curl -i -X DELETE \
  https://api.bookabletech.com/webhooks/subscriptions/whk_3f2b8c1e5a7d4e9b9c0a1d2e3f4a5b6c \
  -H 'Authorization: Bearer <YOUR_TOKEN_HERE>'

Responses

Subscription deleted

Response
No content

Rotate a subscription's signing secret

Request

Generates a new secretKey for this subscription and returns it once. The callback URL is unchanged and no other subscription is touched. Deliveries signed with the previous secret may still be in flight, so accept either for a short window after rotating.

Security
Live or Sandbox
Path
webhookIdstringrequired

The subscription whose secret to rotate.

curl -i -X POST \
  'https://api.bookabletech.com/webhooks/subscriptions/{webhookId}/secret' \
  -H 'Authorization: Bearer <YOUR_TOKEN_HERE>'

Responses

The new secret

Bodyapplication/json
secretKeystringrequired

The newly generated signing secret. Shown once.

Response
application/json
{ "secretKey": "string" }

Send a test event to your webhook subscriptions

Request

Sends one test event to your subscriptions now and reports what each endpoint answered: status code, response body, how long it took, and the exact payload and signature sent. Use it to check a new endpoint, a rotated secret, or how your integration handles each event type.

Every test delivery is signed with the subscription's secretKey like a real event, and carries an X-Webhook-Test: true header that a real event never does. Each subscription gets one attempt with a 10 second timeout and no retries, so a failure is reported straight back. A 200 means the test ran — check delivered on each entry of deliveries.

What can be sent where

EventLiveSandbox
webhook.ping — no business data; checks reachability, TLS and your signature handling✅✅
booking.updated, message.received, venue.updated — sample, real or simulated❌ 422✅

Live is limited to webhook.ping on purpose: a sample booking or a simulated venue closure could be mistaken for real data by a system that ignores X-Webhook-Test, and resending a real booking can reach the guest. Test how you handle business events in the sandbox. (Live is production only; Bookable's internal staging and development environments behave like the sandbox.)

Where the payload comes from (sandbox)

  • Sample — name no booking or venue. Ids are visibly fake (TEST|X0|0…0).
  • Real — bookingId (your own booking) or venueId (a venue you distribute): the current state, exactly as a real event would carry it. Nothing is written.
  • Simulated — changes (a booking), venue (a venue) or message (a message) overrides what is sent. changes is written to the sandbox booking first, standing in for the operator changing it in their table management system; venue and message are never stored.

changes: {} is a simulation that changes nothing but lastUpdate — what a payment status change looks like in production. Leaving changes out sends the booking as it is, without writing anything.

Who receives it

  • No webhookId: every active subscription. Business events respect each subscription's eventTypes filter; a subscription that filters the event out is listed with skipped: filtered. webhook.ping goes to every active subscription regardless of filter.
  • webhookId: that subscription only — even if it is paused, so you can check an endpoint before switching it on, and even if its filter would exclude the event.

Limits and refusals:

  • At most 10 test calls per minute per distributor; beyond that the call answers 429 with code RATE-R-001. Every call counts, including ones refused for a reason below.
  • bookingId must be one of your own bookings, otherwise 403 with code PMAP-N-003.
  • venueId must be a venue you distribute, otherwise 403.

Changes in 2.0.0

Previously this operation sent a sample booking.updated to your default subscription only and answered 202, or 500 when delivery failed. It now answers 200 with a report per subscription (message and eventId are still present), sends to every active subscription, sends webhook.ping in Live, and reports a failed delivery as delivered: false rather than 500.

Security
Live or Sandbox
Query
webhookIdstring

Send to this subscription only. Omit to send to every active subscription.

Example: webhookId=whk_3f2b8c1e5a7d4e9b9c0a1d2e3f4a5b6c
Bodyapplication/json

Omit for a webhook.ping.

eventTypestring

The event a test sends. webhook.ping is the only one available in Live; the others are sandbox-only. webhook.ping is a test-only event: subscriptions cannot filter on it.

Default "webhook.ping"
Enum"webhook.ping""booking.updated""message.received""venue.updated"
bookingIdstring or null

booking.updated / message.received: one of your own bookings. Without it a sample booking is used.

Example: "29|X9|275cc44dd2e2496fba44857c9257443a|B"
venueIdstring or null

venue.updated: a venue you distribute. Without it a sample venue is used.

Example: "29|X9|275cc44dd2e2496fba44857c9257443a"
changesobject or null

The operator-side change to simulate. Every field is optional; send only what changed. Omitted or null fields are left as they are.

venueobject or null

venue.updated only: overrides what the event says about the venue. The venue is not touched.

messageobject or null

message.received only: overrides the message content. Never stored or emailed.

curl -i -X POST \
  'https://api.bookabletech.com/webhooks/test?webhookId=whk_3f2b8c1e5a7d4e9b9c0a1d2e3f4a5b6c' \
  -H 'Authorization: Bearer <YOUR_TOKEN_HERE>' \
  -H 'Content-Type: application/json' \
  -d '{
    "eventType": "webhook.ping"
  }'

Responses

The test ran. Check delivered on each entry of deliveries — a 200 here does not mean your endpoint accepted the event.

Bodyapplication/json
messagestringrequired

Human-readable summary.

Example: "Test event sent to 2 subscription(s)"
eventIdstringrequired

The id the deliveries are recorded under in Bookable's webhook audit log.

Example: "3f2b8c1e-5a7d-4e9b-9c0a-1d2e3f4a5b6c"
eventTypestring(WebhookTestEventType)required

The event a test sends. webhook.ping is the only one available in Live; the others are sandbox-only. webhook.ping is a test-only event: subscriptions cannot filter on it.

Enum"webhook.ping""booking.updated""message.received""venue.updated"
payloadstring

The exact JSON body sent. Every subscription receives the same bytes. Omitted when nothing was sent (no subscription received the event).

deliveriesArray of objects(WebhookTestDelivery)

One entry per subscription the test was addressed to. Omitted when there are none — Bookable leaves empty lists out of every response, so treat a missing deliveries as [].

Response
application/json
{ "message": "Test event sent to 2 subscription(s)", "eventId": "3f2b8c1e-5a7d-4e9b-9c0a-1d2e3f4a5b6c", "eventType": "webhook.ping", "payload": "{\"eventType\":\"webhook.ping\",\"timestamp\":\"2026-10-14T10:30:00+00:00\"}", "deliveries": [ { … }, { … } ] }