Operations related to webhook management
- Send a test event to your webhook subscriptions
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.
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.
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 inbookings).message.received— one or more messages for a booking (payload inmessages). Emitted only foroperator_to_partnermessages (the venue operator sent it, delivered to the partner); filter onsubject/senderEmail.venue.updated— a venue you distribute changed status; today only closures are sent (payload invenues).webhook.ping— a connectivity test you asked for withPOST /webhooks/test; no payload. Test deliveries also carry anX-Webhook-Test: trueheader.
Switch on eventType and answer 2xx to types you do not handle: new ones are added over time.
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.
- Livehttps://api.bookabletech.com/webhooks/subscriptions
- Sandboxhttps://api-sandbox.bookabletech.com/webhooks/subscriptions
- curl
- JavaScript
- Node.js
- Python
- Java
- C#
- PHP
- Go
- Ruby
- R
- Payload
curl -i -X GET \
https://api.bookabletech.com/webhooks/subscriptions \
-H 'Authorization: Bearer <YOUR_TOKEN_HERE>'{ "data": [ { … } ] }
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.
HTTPS URL to be called when an event occurs. Must resolve to a public address.
Restrict this subscription to these event types. Omit for every event.
- Livehttps://api.bookabletech.com/webhooks/subscriptions
- Sandboxhttps://api-sandbox.bookabletech.com/webhooks/subscriptions
- curl
- JavaScript
- Node.js
- Python
- Java
- C#
- PHP
- Go
- Ruby
- R
- Payload
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"
}'Subscription created
Opaque id. Sent with every delivery in the X-Webhook-Id header.
HTTPS URL this subscription's notifications are POSTed to.
The event types this subscription receives. null or absent means every event.
Your own name for this subscription, to tell several apart.
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.
{ "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" }
- Livehttps://api.bookabletech.com/webhooks/subscriptions/{webhookId}
- Sandboxhttps://api-sandbox.bookabletech.com/webhooks/subscriptions/{webhookId}
- curl
- JavaScript
- Node.js
- Python
- Java
- C#
- PHP
- Go
- Ruby
- R
- Payload
curl -i -X GET \
https://api.bookabletech.com/webhooks/subscriptions/whk_3f2b8c1e5a7d4e9b9c0a1d2e3f4a5b6c \
-H 'Authorization: Bearer <YOUR_TOKEN_HERE>'The subscription
Opaque id. Sent with every delivery in the X-Webhook-Id header.
HTTPS URL this subscription's notifications are POSTed to.
The event types this subscription receives. null or absent means every event.
Your own name for this subscription, to tell several apart.
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.
{ "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" }
Request
Applies the supplied fields; anything omitted is left unchanged. The secret is not affected — use POST /webhooks/subscriptions/{webhookId}/secret to rotate it.
- Livehttps://api.bookabletech.com/webhooks/subscriptions/{webhookId}
- Sandboxhttps://api-sandbox.bookabletech.com/webhooks/subscriptions/{webhookId}
- curl
- JavaScript
- Node.js
- Python
- Java
- C#
- PHP
- Go
- Ruby
- R
- Payload
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
}'The updated subscription
Opaque id. Sent with every delivery in the X-Webhook-Id header.
HTTPS URL this subscription's notifications are POSTed to.
The event types this subscription receives. null or absent means every event.
Your own name for this subscription, to tell several apart.
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.
{ "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" }
- Livehttps://api.bookabletech.com/webhooks/subscriptions/{webhookId}
- Sandboxhttps://api-sandbox.bookabletech.com/webhooks/subscriptions/{webhookId}
- curl
- JavaScript
- Node.js
- Python
- Java
- C#
- PHP
- Go
- Ruby
- R
- Payload
curl -i -X DELETE \
https://api.bookabletech.com/webhooks/subscriptions/whk_3f2b8c1e5a7d4e9b9c0a1d2e3f4a5b6c \
-H 'Authorization: Bearer <YOUR_TOKEN_HERE>'- Livehttps://api.bookabletech.com/webhooks/subscriptions/{webhookId}/secret
- Sandboxhttps://api-sandbox.bookabletech.com/webhooks/subscriptions/{webhookId}/secret
- curl
- JavaScript
- Node.js
- Python
- Java
- C#
- PHP
- Go
- Ruby
- R
- Payload
curl -i -X POST \
'https://api.bookabletech.com/webhooks/subscriptions/{webhookId}/secret' \
-H 'Authorization: Bearer <YOUR_TOKEN_HERE>'{ "secretKey": "string" }
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.
| Event | Live | Sandbox |
|---|---|---|
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.)
- Sample — name no booking or venue. Ids are visibly fake (
TEST|X0|0…0). - Real —
bookingId(your own booking) orvenueId(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) ormessage(a message) overrides what is sent.changesis written to the sandbox booking first, standing in for the operator changing it in their table management system;venueandmessageare 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.
- No
webhookId: every active subscription. Business events respect each subscription'seventTypesfilter; a subscription that filters the event out is listed withskipped: filtered.webhook.pinggoes 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
429with codeRATE-R-001. Every call counts, including ones refused for a reason below. bookingIdmust be one of your own bookings, otherwise403with codePMAP-N-003.venueIdmust be a venue you distribute, otherwise403.
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.
Omit for a webhook.ping.
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.
booking.updated / message.received: one of your own bookings. Without it a sample booking is used.
venue.updated: a venue you distribute. Without it a sample venue is used.
The operator-side change to simulate. Every field is optional; send only what changed. Omitted or null fields are left as they are.
venue.updated only: overrides what the event says about the venue. The venue is not touched.
- Livehttps://api.bookabletech.com/webhooks/test
- Sandboxhttps://api-sandbox.bookabletech.com/webhooks/test
- curl
- JavaScript
- Node.js
- Python
- Java
- C#
- PHP
- Go
- Ruby
- R
- Payload
- ping
- sampleBooking
- sampleMessage
- sampleVenue
- myBookingAsItIsNow
- messageWithSubject
- noShow
- actuals
- operatorCancelled
- moved
- paymentStatusChanged
- guestDetailsEdited
- preordersAdded
- venueClosed
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"
}'The test ran. Check delivered on each entry of deliveries — a 200 here does not mean your endpoint accepted the event.
The id the deliveries are recorded under in Bookable's webhook audit log.
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.
The exact JSON body sent. Every subscription receives the same bytes. Omitted when nothing was sent (no subscription received the event).
- deliveredToAll
- oneFilteredOut
- endpointTimedOut
- endpointRejected
{ "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": [ { … }, { … } ] }