All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Bookings API v8.1.0
dateTimeon bookings and availability: the real moment the reservation starts, in the venue's local time with its UTC offset (RFC 3339).dateandtimestay on the venue's trading day, so a 00:30 slot at the end of a Saturday evening has Saturday'sdatebut starts on Sunday.GET /venues/{compositeId}/availability: newtimes[].dateTime. Send it back unchanged asdateTimewhen you book.POST /venues/{compositeId}/bookingandPUT /venues/bookings/{bookingId}: newdateTime.dateandtimeare no longer required: senddateTime, or bothdateandtime. If you send both forms they must agree (BOOK-N-018). Missing both:BOOK-N-019. No UTC offset:BOOK-N-020.Booking.dateTimein responses. Selectable withfields.
GET /venues/{compositeId}/availability: newstartDateTime/endDateTimequery parameters (UTC offset required, encode+as%2B). Use them instead ofdate/startTime/endTime.endDateTimeis inclusive, needsstartDateTime, and the window can be at most 48 hours. A window that crosses midnight also returns the late-night slots of the evening before.dateis now only required whenstartDateTimeis not sent. New errorsAVAIL-N-009(the two forms disagree) andAVAIL-N-010(bad window).Venue.bookingRequirements.companyonGET /venues/{venueId}(partners mapped to the venue's operator only): your channel's company config at this operator —mode(none,single,list),company,companyNamesandrequired. Inlistmode acompanynot in the list fails with422 BOOK-N-015. If the list isrequired, a missingcompanyfails with422 BOOK-N-016.Booking.payments: the payments recorded in Bookable for the booking, oldest first (id,optionType,amountin pence,status,updatedAt). Returned byGET /venues/bookings,GET /venues/bookings/{bookingId},PATCHandPUT .../status. Not returned on create or full update. If it is missing, no payment is recorded in Bookable — that does not mean the booking is unpaid./paymentscannot be patched.GET /venues/bookingsnew filters:productName(partial, case-insensitive),productCategory(1–10 slugs fromGET /venues/category),tmsCreatedDateFrom/tmsCreatedDateTo(when the booking was made in the TMS, inclusive, UTC). The description now also explains which TMS bookings are listed and how to count bookings (pageSize=1, readmeta.totalItems).mediaon spaces (products[].spaces[]and availability spaces): the space's own photos and videos, then venue items assigned to it. Empty when it has none — use the venue'smedia.BOOK-N-017(422, Zonal): thespaceIdis not offered by the product. Use a space from this product's availability response.- Operator-only additions (venue-group callers):
Booking.tmsTablesandBooking.autoConfirmed(Collins) onGET /venues/bookings;times[].unavailableRules,excludedPackagesandexcludedMenuson availability withincludeUnavailable=truewhen operator staff act as a partner throughX-Partner-Reference. Partners never receive rule details. fieldsnow acceptsdateTimeandpaymentson bookings;depositAmount,actualGuests,actualSpend,area,tmsTablesandautoConfirmedonGET /venues/bookings;bookingRequirementsonGET /venues/{venueId}. See sparse fieldsets.
- BREAKING CHANGE:
Venue.photos(a list of URLs) is removed (8.0.0) and replaced byVenue.media, a list ofMediaItem(typephotoorvideo,url,caption,durationSeconds), cover first. Use the firsttype: photoitem where you usedphotos[0]. URLs can differ from the old ones: venues with media in Bookable's library now serve Bookable-hosted files.fields=photosbecomesfields=media. - Availability space
minimumSpendcan now have several entries: one per minimum-spend band crossed by the booking (slot start to slot start + duration), in time order, each with its ownspendType/spendValue/hireFeeValue. No total is returned.nullwhen no band applies.spaceSpendTypeis the type of the first entry.maxMinSpendonly compares the band in force at the slot start. BookingArea.idis now the Bookable space id when the booking was placed on a specific space, andnullwhen the TMS chose the area.Booking.actualGuests: on Collins it almost always copies the party size, so don't read it as measured attendance there.AVAIL-N-007/AVAIL-N-008are also returned when an operatorbooking_windowrule closes the date. NewBKRUL-N-007(amend) andBKRUL-N-008(cancel) refuse changes to a confirmed booking too close to its start —DELETEcan now fail withBKRUL-N-008. See the Partner Booking Rules API v1.6.0 entry below and the error catalog.
Register Webhook v2.0.0
- Several webhook subscriptions per partner, each with its own callback URL,
secretKeyand optionaleventTypesfilter, so an app acting for you can subscribe without touching your own integration:GET /webhooks/subscriptions— list (secrets are never returned).POST /webhooks/subscriptions— create. ReturnswebhookIdand thesecretKey, shown once.GET,PUT(partial update;active: falsepauses deliveries),DELETE /webhooks/subscriptions/{webhookId}.POST /webhooks/subscriptions/{webhookId}/secret— rotate the secret. Accept the old and new secret for a short time after rotating.
- Every delivery now carries
X-Webhook-Id(the subscription'swebhookId). Each subscription signsX-API-Keywith its ownsecretKey— use the header to pick the right one. - New
webhook.pingevent type: a test with no payload, sent only when you callPOST /webhooks/test. Test deliveries carryX-Webhook-Test: true. - New error codes
WHK-N-001toWHK-N-007. See the error catalog.
- BREAKING CHANGE:
POST /webhooks/testis no longer deprecated and works differently. It answers200(was202) with a report per subscription indeliveries[](delivered,statusCode,responseBody,durationMs,payload,signature). A failed delivery isdelivered: false, no longer a500. It goes to every active subscription (or one, with?webhookId=), one attempt, 10 second timeout, no retries.- Production sends only
webhook.ping. Other event types return422. - Sandbox can also send
booking.updated,message.receivedandvenue.updated, as a sample, from one of your real bookings or venues, or with simulated changes.
- Production sends only
- See the Webhook Guide.
Bookinglists every field abooking.updatedcarries (company,location,productName,productType,notes,source,preorders,preorderMenus).- Limits stated: at most 10 subscriptions (
409WHK-N-004); at most 10POST /webhooks/testcalls per minute (429RATE-R-001); a testbookingIdmust be your own booking (403PMAP-N-003).
- BREAKING CHANGE: the single-webhook endpoints
POST,PUTandDELETE /webhooksare removed. Use/webhooks/subscriptions. Your existing webhook is kept as a subscription (isDefault: true).
Handling Webhook Events v1.9.0 (includes v1.8.0)
- New
webhook.pingevent type: sent only when you callPOST /webhooks/test, with nobookings,messagesorvenues. Test deliveries carryX-Webhook-Test: true; a real event never does. Answer204. dateTimeonbooking.updatedbookings: the real moment the booking starts, in the venue's local time with its UTC offset.dateandtimestay on the venue's trading day. Left out if Bookable can't work out the real start.MessagePayloadnow documents every field that is sent:id(use it to dedupe),subject,senderEmail,direction(alwaysoperator_to_partner),replyToandattachments(filename,contentType; the files come by email).
- A payment status change also fires
booking.updated. Payments are not in the payload, so onlylastUpdatechanges. Treat it as a no-op. - Sandbox testing: use
POST /webhooks/testto send any event type. See the Webhook Guide. companyonbooking.updatedis now the company named on the booking. It used to carry the operator's own business name. Left out when the booking has none.- A booking with no TMS reference yet, or with no guest name or email, is now sent with
""inreference,firstName,lastNameoremail. Such bookings used to fail to send at all. booking.updatedandmessage.receiveddeliveries now time out after 45 seconds in total, retries included (was 100 seconds). An endpoint that takes longer than 45 seconds used to succeed and is now treated as failed and retried. Answer within 10 seconds, as before.- A
429Retry-Afteris honoured up to 10 seconds. - Documented: the
X-Webhook-IdandX-Webhook-Testheaders; signing with the subscription'ssecretKey; retries and redelivery per event type (message.receivedis not redelivered automatically); empty collections and fields are left out rather than sent asnull;MessagePayload.idis always present; who receivesvenue.updated; subscriptions with aneventTypeslist receive only those types.
Listings API v2.0.0
mediaonSpaceandAvailabilitySpace: the space's own photos and videos, then venue items assigned to it. Empty when it has none — use the listing'smedia.
- BREAKING CHANGE:
photos(a list of URLs) on listings is removed and replaced bymedia, a list ofMediaItem(typephotoorvideo,url,caption,durationSeconds), cover first. Use the firsttype: photoitem where you usedphotos[0]. URLs can differ from the old ones (Bookable-hosted once the venue's media is in Bookable). Anevent_ticketlisting without its own images shows its venue's. AvailabilitySpacePolicies.minimumSpendcan now have several entries, one per band crossed by the booking, in time order. No total is returned.spaceSpendTypeis the type of the first entry.- Calendar rule
endTime: anendTimebeforestartTimemeans the window ends the next day (e.g.18:00–01:00).00:00means end of day.
Partner Booking Rules API v1.6.0
- New effect
booking_window: you may not book, amend or cancel (params.action) withinparams.minHoursBeforehours of the start, or book more thanparams.maxHoursBeforehours ahead. Hours are real elapsed time (Europe/London). Amend and cancel windows only apply to confirmed bookings. The strictest matching rule wins. Errors:AVAIL-N-007/AVAIL-N-008(book),BKRUL-N-007(amend),BKRUL-N-008(cancel). - New moment
booking_cancel(DELETE /venues/bookings/{bookingId}). There is no cancelhookfilter: cancel rules are returned without one. - New effect
preorder_emailand consequenceskips_preorder_email: belowparams.thresholdguests the Collins pre-order email is not sent. The pre-order is still posted and the booking is never refused. New writable path and factbooking.sendPreorderEmail. - New
params.perItemformax_quantityandmax_quantity_party_size: cap each item on its own instead of the set's total. - New
caller.categoryIdsByOperator: your partner categories for each operator that has a rule inrules[]. This is the value of the factpartner.categoryIds— readcategoryIdsByOperator[String(rule.operatorId)].
caller.partnerCategoryIdsis kept for backward compatibility but is no longer the value ofpartner.categoryIds. Only use it if the operator's entry is missing.- The fact
venue.idis now the venue composite id ({operatorId}|{rmsSlug}|{venueId}), not the raw venue id. If you evaluate rules yourself, a raw id no longer matches. - Rule error codes can now be
BKRUL-N-001toBKRUL-N-008,BKRUL-N-100,AVAIL-N-007orAVAIL-N-008. - Expression rules that target partners in
whenare filtered for you: a rule is only returned if it can apply to you. Returned rules can contain other partners' ids when they also apply to you.
- To test your
message.receivedreceiver, usePOST /webhooks/testwitheventType: message.received. It reports what your endpoint answered. The sandboxdirectionoverride answers201even when delivery fails, and does not deliver an identical message twice in the same hour.
date_timeon Collins availability feed slots: the real moment the slot starts, in the venue's local time with its UTC offset. Send it unchanged asdateTimewhen booking. Shards written before this release don't have it until their date is regenerated. Where it is missing, usedate+time.- New
request_reasonvalueoperator_rule: an operatorroute_to_enquiryrule makes the slot a request. Your enquiry policy never removes these slots. - Operator
booking_windowrules for your channel apply to your feed: slots inside the minimum notice or past the horizon are left out. See Operator rules for your channel.
- Collins slots after midnight are now published at their real time.
dateandtimestay on the venue's trading day: a00:30slot in Saturday's shard starts on Sunday, anddate_timesays so. - Documented slot fields that were already sent:
merchant_id,service_id,room_id,room_nameandduration_requirement.
- Per-area rooms for Zonal products with two or more areas: one slot per area, with
room_id,room_nameandspace_id. Sendspace_idasspaceIdto book that area. See Per-area rooms.
- New
venue.updatedevent. It is sent when a venue closes.- The closed venues are in the
venuesarray. Each one hascompositeId,name,active(false) andshutAt(the closing date, ornullif unknown). compositeIdis the venueid(like inGET /venues). It is not a productcompositeId.- You can get this event for a venue you don't sell. Ignore venues you don't know.
- If your endpoint answers
4xx, the event is not sent again.5xxand timeouts are retried.
- The closed venues are in the
booking.updatednow includesactualGuestsandactualSpend: how many guests came and how much they spent. They are filled in after the visit.
- Your webhook handler must ignore event types it doesn't know. We will add new event types. See the Webhook Guide.
VENUE-N-009(404): the venue is closed. Returned byGET /venues/{compositeId}, availability, create booking and update booking. You can still cancel (DELETE) bookings at a closed venue. See the error catalog.AVAIL-N-008(400): the date is too far in the future for this product.
AVAIL-N-007is now also returned when the date is too soon (the product needs more notice). Sendingtype: "request"does not avoid it.GET /bookings/{bookingId}/messages:bodynow contains only the operator's text — no email template, signature or quoted replies. Automatic booking confirmations and cancellations are no longer sent as messages; you already get them asbooking.updated.
- When a booking becomes an enquiry, the enquiry policy in the response now respects the operator's booking overrides.
advance_bookings_min_days/advance_bookings_max_daysin availability feedindex.json: the minimum and maximum number of days ahead the product can be booked. Dates outside this range are not in the feed.active/shut_atinindex.json: when a venue closes,activeisfalse,shut_atis the closing date (ornullif unknown) andshardsis empty. Ifactiveis missing, treat it astrue.
- Dates that become too close to book are removed from the feed. Re-read
index.jsonevery day.
Partner Booking Rules API v1.0.0
- New
GET /booking-rules(scopevenue:read): lists the booking rules that apply to you, so you can avoid rejected bookings. Bookings are still checked by the server. - You only see rules that apply to you, and only for venues you can see.
- A booking that breaks a rule fails with
400and a code fromBKRUL-N-001toBKRUL-N-006(see the error catalog). Some rules turn the booking into an enquiry instead. - Booking updates are checked against the whole booking, so an update can fail even if you only changed contact details.
paymentrules (GET /booking-rules?hook=payment) change the amount to pay for a booking. Don't check them yourself: read the amount fromGET /venues/bookings/{bookingId}/payment-options.- Operator accounts get
403.
Bookings API v7.12.0
GET /venues/bookings: newsortByquery parameter (newBookingSortByenum) —date(default) orders by reservation date, then start time;createdDateorders by when Bookable received the booking (the booking'screatedDate), regardless of its reservation date.GET /venues/bookings: newsortDirectionquery parameter (newBookingSortDirectionenum:ASC,DESC) — defaults toDESC(latest first), unlike/venues.
GET /venues/bookings: thepartnerBookingIdandoperatorBookingIdfilters are now documented as partial, case-insensitive matches, in line withfirstName,lastName, andemail(whose descriptions now also state case-insensitivity explicitly).
Bookings API v7.11.0
GET /venues/bookings: newvenueNamequery filter — partial, case-insensitive match on the booked venue's name. Matches the venue record regardless of its active flag, so bookings at deactivated venues are still findable.Booking.venueName: new nullable field — the name of the booked venue, resolved ignoring the venue's active flag so a booking at a since-deactivated venue still reports it.nullonly when no venue record exists for the booking's composite. Also a valid top-levelfieldsvalue onGET /venues/bookingsandGET /venues/bookings/{bookingId}.Booking.actualGuests: new nullable integer — the number of guests who actually attended, as reconciled by the operator's TMS after the visit.nulluntil the TMS reports it (so normally until after the booking date, and permanently on systems that never report it).0is a real value meaning no-show, distinct fromnull.Booking.actualSpend: new nullable number — the amount the party actually spent, as reconciled by the operator's TMS after the visit.nulluntil the TMS reports it. Distinct fromdepositAmount(taken up front) and from any preorder pricing.
- BREAKING CHANGE Payment links for minimum-spend policies, added in v7.10.0, are removed:
paymentOptionson availability time-slot spacepolicies,BookingRequest.paymentOptionType, andBooking.paymentare gone. Minimum-spend payments remain available through the experimental Payment API (GET /venues/bookings/{bookingId}/payment-options,POST /venues/bookings/{bookingId}/payments). - BREAKING CHANGE
AvailabilityTimeSlot.depositRequiredandAvailabilityTimeSlot.paymentRule, added in v7.10.0, are removed, along with theResolvedPaymentRuleschema (common/DepositRule.yaml). Useproduct.depositRequiredand the product's deposit rules instead.
Register to Webhook Events API v1.4.0
Booking.actualGuests/Booking.actualSpendon thebooking.updatedpayload: new nullable fields carrying the guest count and spend reconciled by the operator's TMS after the visit.nulluntil the TMS reports them;actualGuests: 0means no-show, distinct fromnull.- A change to
actualGuestsoractualSpendis itself abooking.updatedtrigger — expect a notification after the visit with no change tostatus.
Bookings API v7.10.0
BookingRequest.company/UpdateBookingRequest.company/Booking.company: new nullable company/business name field for a booking. When omitted, falls back to the company name configured by the operator for the requesting partner/channel, if any. Currently wired through for Collins only — other RMS integrations accept but ignore it. Shared schema (Customer) change on the Listings API side too.BookingRequest.commission/UpdateBookingRequest.commission/Booking.commission: new nullable partner commission field, forwarded to Collins ascommission_amountwhen the booking's venue runs on Collins.PATCH /venues/bookings/{bookingId}:/companyand/commissionare now patchable JSON Patch paths.Menu.fixedPrice/Menu.fixedPriceRules: new fields — whether the menu is sold at a fixed price, plus an array ofFixedPriceRulefixed-price tiers (price per number of included items). NewFixedPriceRuleschema.X-Booking-Sourceheader: newembedvalue — marks a booking placed through an embedded client (e.g. the partner portal embedded in a partner's site or app).- Payment links for minimum-spend policies:
GET /venues/{compositeId}/availabilitytime slots now returnpaymentOptions(array ofPaymentOption, on each space'spolicies) — prospective payment amounts computed from that slot's minimum spend entries, one per option type (full_minimum_spend,deposit_25_minimum_spend). Pass the chosen option'stypeas the newBookingRequest.paymentOptionTypeto obtain apayment(withpaymentUrl) on the createdBooking; ignored when the space requires no payment. New shared schemasPaymentOptionType,PaymentOption,PaymentStatus,Payment(common/Payment.yaml). AvailabilityTimeSlot.depositRequired: new nullable boolean — true when a deposit/payment rule matched this exact slot (party size, date, time, duration), distinct fromproduct.depositRequiredwhich only says the product has deposit rules configured at all.AvailabilityTimeSlot.paymentRule: new nullableResolvedPaymentRule(common/DepositRule.yaml) — the single deposit/payment rule matched against this slot's actual request parameters, withamountalready resolved for the request's party size (including any adult/child split).nullwhen no rule matched.
Listings API v1.4.0
Customer.company: new nullable company/business name field on the order-level customer, applied to every item in the order. When omitted, falls back to the company name configured by the operator for the requesting partner/channel, if any. Currently wired through for Collins (table_reservation) only — other platforms accept but ignore it.OrderItemUpdate.company: new nullable guest company/business name field onPATCH /v1/orders/{orderId}items — applies to either capability.Menu.fixedPrice/Menu.fixedPriceRules: new fields — whether the menu is sold at a fixed price, plus an array ofFixedPriceRulefixed-price tiers (price per number of included items). NewFixedPriceRuleschema.X-Booking-Sourceheader: newembedvalue — marks an order placed through an embedded client (e.g. the partner portal embedded in a partner's site or app).
OrderItem.commissiondescription clarified: now wired through forevent_ticket(Tonic'spartner_commission) andtable_reservationon Collins (forwarded ascommission_amount); othertable_reservationRMS integrations accept but ignore it. Previously table_reservation behaviour was undefined.
request_reasonon availability feed shard slot entries — whytypeis"request". Known values:fully_booked,exceeds_auto_confirm,request_only; new values may be introduced, clients should handle unknown values gracefully. Omitted whentypeis"book".
Bookings API v7.8.0
MinimumSpendRuleBase.spendType/MinimumSpendOverrideBase.spendType: new nullable per-rule/per-overrideSpaceSpendType. Two-level spend type model: the policy'sspendType(MinimumSpendPolicyResponse.spendType) is the space-wide default; any rule or override may now set its ownspendTypeto differ from that default — e.g. a Mon–Tue rule withMinimumSpendand a Wed–Sun rule withHireFeePlusMinimumSpendunder the same policy. A rule/override with nospendTypeof its own inherits the policy's. The effective spend type for a rule/override is its own if set, else the policy's — and itsspendValue/hireFeeValuemust match that effective type, not necessarily the policy-level one. Shared schema (common/VenueSpaces.yaml) with the Listings API.
SpaceSpendType,MinimumSpendPolicyResponse.spendType, and thespendValue/hireFeeValuedescriptions on rules/overrides clarified to document the two-level default/override spend-type model above.
Listings API v1.2.0
MinimumSpendRuleBase.spendType/MinimumSpendOverrideBase.spendType: new nullable per-rule/per-overrideSpaceSpendType. Two-level spend type model: the policy'sspendType(MinimumSpendPolicyResponse.spendType) is the space-wide default; any rule or override may now set its ownspendTypeto differ from that default — e.g. a Mon–Tue rule withMinimumSpendand a Wed–Sun rule withHireFeePlusMinimumSpendunder the same policy. A rule/override with nospendTypeof its own inherits the policy's. The effective spend type for a rule/override is its own if set, else the policy's — and itsspendValue/hireFeeValuemust match that effective type, not necessarily the policy-level one. Shared schema (common/VenueSpaces.yaml) with the Bookings API.AvailabilitySpacePolicies.minimumSpend[].spendType: same two-level model now surfaced on the availability response itself, not just the policy-management schemas — the effectiveSpaceSpendTypefor that specificminimumSpendentry (its own if set, else the space'sspaceSpendType).spendValue/hireFeeValueon the entry are populated according to this effective type.GET /v1/listings—bookableProductNamequery parameter: filter to listings having at least one entry inbookableProducts[]whose name matches — partial, case-insensitive, with a semantic (vector similarity) fallback for near-matches on the product/ticket type name (e.g. "Sunday Roast" also surfacing "Sunday Lunch").
SpaceSpendType,MinimumSpendPolicyResponse.spendType, and thespendValue/hireFeeValuedescriptions on rules/overrides clarified to document the two-level default/override spend-type model above.GET /v1/listingsquery parametervenueNamerenamed tolistingName. Also enhanced: adds a semantic (vector similarity) fallback for near-matches, and for an event listing now matches against either the event's own title or the name of the venue it's hosted at.
depositobject on availability feed shard slot entries (amount,currency_code,amount_per) — the product's conditional deposit rule matched against the slot's date, time, party size, and duration, mirroringpaymentRules/ConditionalDepositRuleelsewhere in the API. Omitted when the product has no deposit rules, or none match the slot. The Google Feed and Listings Feed jobs now share the same deposit-matching logic.
- A generated slot whose local date/time falls in a DST spring-forward gap (a local time that never occurs) is now dropped from the feed instead of aborting generation for the whole product.
Listings API v1.1.0
- New
GET /v1/listings/{listingId}endpoint — retrieve a single listing (venue or standalone event) by itslistingId, returning the sameListingGroupshape asGET /v1/listings(groupName+items[]) but withitems[]holding exactly the one match. Lets a caller that already has alistingIdre-fetch it directly without paginating the whole catalogue. Requireslisting:read. Bookable.deposit.rules— array ofConditionalDepositRule, the full conditional deposit/payment rule schedule for a bookable unit when the RMS provides one (Collins today). Unlikedeposit.rule— a single flat amount — a bookable unit may have several of these, each varying by day of week, time window, date range and/or party size (e.g. "£5 per person on Saturdays").null/empty when the RMS does not provide conditional rules.minDuration,maxDuration,durationIntervalon availability entries — minimum/maximum selectable duration and step size (minutes), alongside the existing fixedduration.nullwhen the product has no such concept.durationadded to both the root defaults and per-entry overrides onPOST /v1/availabilitiesrequests, mirroring the existingdate/timedefault-inheritance pattern.spaceSpendTypeon space policies (newSpaceSpendTypeenum:MinimumSpend,HireFee,HireFeePlusMinimumSpend), plushireFeeValuealongsidespendValueonminimumSpendentries —spendValue/hireFeeValueare populated according to which spend type is configured. Shared schema with the Bookings API (common/VenueSpaces.yaml).
- Clarified ID terminology throughout the spec description: the 3-segment identifier (
Listing.id) is now consistently calledlistingId, kept explicitly distinct from the 4-segmentcompositeId(Bookable.compositeId). Description text only — no schema or field changes.
Bookings API v7.7.0
paymentRuleson booking type — array ofConditionalDepositRule, the full conditional deposit/payment rule schedule for a booking type when the RMS provides one (Collins today). UnlikedepositRule— a single flat amount — a booking type may have several of these, each varying by day of week, time window, date range and/or party size (e.g. "£5 per person on Saturdays").null/empty when the RMS does not provide conditional deposit rules.minDuration,maxDuration,durationIntervalonGET /venues/{compositeId}/availabilitytimes[]entries — minimum/maximum selectable duration and step size (minutes), alongside the existing fixedduration.nullwhen the product has no such concept.spaceSpendTypeon space policies in the availability response (newSpaceSpendTypeenum:MinimumSpend,HireFee,HireFeePlusMinimumSpend), plushireFeeValuealongsidespendValueonminimumSpendentries —spendValue/hireFeeValueare populated according to which spend type is configured. Shared schema with the Listings API (common/VenueSpaces.yaml).- The availability feed shard format gains the same
min_duration,max_duration,duration_intervalfields as the live availability response — Collins-backed products get real per-party-size bounds, other RMSs fall back tomin_duration = max_duration = duration_minuteswithduration_interval: null.
Listings API v1.0.0
- New venue-aggregated discovery API.
GET /v1/listingsreturns the bookable catalogue grouped by operator (ListingGroup→groupName+items[]), each item a single venue (ListingItem) carrying alocationand acapabilities[]array ofListings — one entry per capability (table_reservation,event_ticket). A venue that also hosts a ticketed event appears once, with two capability entries. - All bookable units (venue products, ticket types) are unified under a single
Bookableshape inbookableProducts[]; capability-specific fields are nullable, with context set by the parentListing.capability. - Temporal data (opening hours, event occurrences, blackout dates) is unified under
availabilityOccurrence[]— a singleCalendarRulemodel shared by both capabilities, discriminated bytype(date_range,recurring,time_based,specific_dates, and time-window variants of each). - Pre-order assets (packages, menus) are unified under a single
preorderscontainer, applicable to either capability. - Two distinct, non-interchangeable ID formats, both CompositeId-encoded:
Listing.id(3 segments —{VenueGroupId}|{RmsSlug}|{ResourceId}) vs.Bookable.compositeId(4 segments, adds{AdditionalId}), the latter used inbookableProducts[]and ascompositeIdin/v1/availabilitiesand/v1/orders. POST /v1/availabilities: batch availability check across multiplecompositeIds in one call. Modelled as a command, not a search — root-leveldate/quantity/timeact as defaults inherited by each entry. Searches run independently: a failure on onecompositeIdis reported as anerroron that entry's result only (never a404for the whole call); other results return normally.POST /v1/orders: create an order of one or more independent bookings, each resolved and booked separately — a failure on one item does not block or roll back the others. Items are unified (nocapability/oneOf); capability and platform are resolved server-side from each item'scompositeId, so a single order can span multiple venues and RMS platforms.PATCH /v1/orders/{orderId}: partial update of one or more bookings within an order, same per-item isolation model. Supports moving a booking to a different bookable product viacompositeId(same venue/RMS/event only — cross-venue move rejected with400 ORDER-N-007), updating guest details, and replacing (preorders) or incrementally changing (preorderChanges) pre-orders (mutually exclusive —400 ORDER-N-008).DELETE /v1/orders/{orderId}: cancel specific bookings viabookingIds, or every booking on the order when omitted.- Both
POST /v1/ordersandPATCH /v1/orders/{orderId}return200when every item succeeds,207whenever one or more items fail (including total failure) — a bulk success is never conflated with a partial or total failure.
Messaging API v2.0.0
- BREAKING CHANGE The Messaging API is now the operator↔partner conversation stored against a booking. Bookable relays each message between the venue operator and the integration partner over email (both parties email a per-booking conversation alias and Bookable forwards each message on). The previous operator→guest / IP→operator / administrator model is removed.
- BREAKING CHANGE
POST /bookings/{bookingId}/messagesnow returns201with the createdMessagebody (previously returned204and processed asynchronously). Sending is synchronous: the message is stored and relayed before the response returns. - BREAKING CHANGE Send request body:
messagerenamed tobody;maxLengthraised from2000to5000. - BREAKING CHANGE Removed the
recipientfield. Send direction is now derived from the caller's role — an Integration Partner sends to the venue operator (partner_to_operator); a venue group operator sends to the integration partner (operator_to_partner). - New
directionrequest field is a Sandbox-only test override: it forces the absolute direction so a partner can trigger their ownmessage.receivedwebhook (sendoperator_to_partner) without a real operator or inbound email. Ignored outside Sandbox; when set in Sandbox the email relay is skipped (webhook-only — message stored, webhook fires, no email sent). Messageschema changes:contentrenamed tobody.action(sent/received) replaced bydirection(operator_to_partner/partner_to_operator) — an absolute direction stating who sent and who received, now required.- Added
subject(nullable) — email subject line. - Added
attachments(nullable array ofMessageAttachmentwithfilenameand nullablecontentType). Attachment bytes are exchanged over email, not via this API — attachments cannot be sent throughPOSTand can only be added by replying to the relay email.
- New
MessageAttachmentschema.
Register to Webhook Events API v1.3.0
- Documented event delivery guarantees: delivery is at-least-once — the same event may arrive more than once, so consumers must be idempotent (dedupe booking events by booking
id, messages by messageid). - Documented request signing: every callback POST carries an
X-API-Keyheader holding a lowercase hex HMAC-SHA256 of the raw body, keyed with thesecretKeyfrom registration. - Documented the two
eventTypevalues —booking.updated(payload inbookings) andmessage.received(payload inmessages). - Added full callback payload schemas that were previously undocumented:
BookingNotification,Booking, andMessagePayload. MessagePayloadnow documentsid(stable, use to deduplicate),bookingId,body,subject(nullable — primary filter signal),senderName,senderEmail,direction,attachments(metadata only; bytes over email),sentAt, andreplyTo— the per-booking email alias to reply to; email it from anywhere and Bookable relays your message to the venue operator.message.receivedfires only foroperator_to_partnermessages (venue operator → partner).
POST /webhooks/testis deprecated and scheduled for removal in2.0.0. For ongoing integration testing use the sandbox self-echo flow instead — in sandbox every booking you create, amend, or cancel fires the corresponding webhook to your registered callback with no separate trigger.
Operator Site FAQs API
- The FAQ write verbs are now public (previously
x-internal). Operators can now manage a venue's FAQ list directly:PUT /venues/{venueCompositeId}/site-faqs— replace the whole array (also the bulk-import mechanism for a single venue's full list). Requiresvenue-group:update.PATCH /venues/{venueCompositeId}/site-faqs— apply an RFC 6902 JSON Patch (item-index pointers, e.g./0/answer, or/-to append). Requiresvenue-group:update.DELETE /venues/{venueCompositeId}/site-faqs— wipe the stored array (idempotent). Requiresvenue-group:delete.POST /venues/site-faqs/bulk— apply changes across many venues in one all-or-nothing transaction. Requiresvenue-group:update.
GETis now readable by operators and mapped partners (previously mapped partners only). Access is enforced by the caller's venue-group ownership of, or partner mapping to, the venue.
SiteFaq.questionandSiteFaq.answerare now nullable and no longer required.
OperatorTmsCredentials.message: new nullable string on the TMS credentials/connection response — free-text notes about the connection (e.g. a TMS-specific sync-schedule caveat).nullwhen none.
VenueSpacesschema:MinimumSpendRuleResponserenamed toMinimumSpendRuleBaseandMinimumSpendOverrideResponserenamed toMinimumSpendOverrideBase. The JSON shape is unchanged; only the schema component names differ (regenerate clients that reference these names).
- Feed window extended from 90 to 180 days.
- Incremental refresh — previously Zonal-only — now applies to all RMS types, including Collins. On each daily regeneration, a date's shard is only rewritten when its content could actually have changed: a per-RMS activity signal (new/cancelled bookings) or a config-hash change (any slot-affecting
ServicePairConfigfield) marks it dirty; otherwise the previous shard is carried forward unchanged. Every date is still force-regenerated on the first run, on any config-hash change, and on a weekly full-sweep day (Monday by default) — so no date can go stale indefinitely. Shard URLs are unaffected; a carried-forward date's content may simply be up to a week old rather than always reflecting the last 24 hours.
- New endpoint
PUT /venues/bookings/{bookingId}/status— confirm or reject an in-progress booking. SetstatustoConfirmedto accept orRejectedto decline; only bookings still awaiting a decision (PendingorInProgress) can be actioned, otherwise422. Optional free-textreasonrecorded when rejecting. Requires the newvenue-booking:update:statusscope, granted separately and not available to all partners by default (403if unauthorised). NewUpdateBookingStatusRequestschema. venue-booking:update:status: new OAuth scope (Live and Sandbox) — grants permission to confirm/reject an in-progress booking.Booking.rejectionReason: new nullable field — the reason a booking was rejected, when one was provided. Null otherwise.Booking.tmsCreatedDate: new nullable date-time on booking responses — creation timestamp reported by the originating TMS; null when unavailable. Also usable as a sort field onGET /venues/bookings.Booking.createdDateclarified as the timestamp the record was created in Bookable.Product.categories: new nullable array of all main category slugs assigned to a product (a product may have more than one).Product.categoryretained as the primary (first) slug for backwards compatibility.MinimumSpendOverride.timeRange: new optional Europe/London time window a minimum-spend override applies to, returned in venue space data. Absent means the override covers the whole date; outside the window normal rules apply.
- Availability feed regeneration changed from every 4 hours to once daily.
Product.feedUrlis now alsonullwhen the product is not currently reachable by any partner (previously only null when no feed had been generated yet).
Register to Webhook Events API v1.2.0
- Webhooks can now be tested in the sandbox environment. Register your callback URL against
https://api-sandbox.bookabletech.comand any booking you create, amend, or cancel via the sandbox API delivers a signed notification to your endpoint — the same payload andX-API-KeyHMAC-SHA256 signature as production, with no external reservation system required. The sandbox server was added to the Register to Webhook Events API. See the Webhook Guide.
BookingApi v7.3.0
- Action required Availability feed format: the single 90-day gzip file per product is replaced by a plain-JSON
index.jsonplus one gzip-compressed daily shard file ({date}.json.gz) per day with availability. The previous{productId}.json.gzfiles are no longer generated, so feed consumers need to update their download logic to the new format.Product.feedUrlnow points to the index (.../{productId}/index.json). It is plain JSON — do not decompress it.- The index lists
shards[], one entry per non-empty day, each withdateand a directurlto that day's gzip-compressed shard. Days with no availability are omitted. - Each shard contains
product_id,date, and theslotsarray. The slot object shape is unchanged.product_nameandvenue_namenow appear only in the index. - Partners can download only the dates they need instead of the full 90-day payload. See the Availability Feed guide for the new format and migration notes.
Handling Webhook Events API v1.5.0
- BREAKING CHANGE
ReservationStatusenum updated to mirror the Bookings API booking statuses (Pending,InProgress,Confirmed,Cancelled,Deleted,Lost):- Added:
in_progress(enquiry received and assigned but not yet confirmed),deleted(booking deleted from the system),lost(booking not completed before its scheduled date). - Removed:
completed,no_show. Update any handling logic that depends on these values.
- Added:
BookingApi v7.2.3
Product.sub_category: new nullable string field — admin-defined sub-category slug (e.g."outdoor","easter","christmas").nullwhen no sub-category applies.noAvailabilityCode: two new known values:NO_AVAILABLE_TIMES— RMS confirmed the request was valid but returned no bookable time slots.FILTERED_BY_CAPACITY_MIN— party size is below the minimum capacity of every available space.
RecurringRuleDataandRecurringWithTimeRuleData:daysOfWeekandmonthsare no longer individually required; either may be provided alone (when both are present the match is AND). Both fields are now nullable with nominItemsconstraint.
Messaging API v1.0.0
- New API spec at
apis/production/MessagingApi.ymlfor sending and retrieving messages associated with a booking. POST /bookings/{bookingId}/messages— send a plain-text message (async, returns204). Direction determined by caller role:- Integration Partner → message delivered to venue operator.
recipientfield ignored. - Operator → message delivered to booking guest.
recipientfield ignored. - Administrator →
recipientfield required (guestorvenue).
- Integration Partner → message delivered to venue operator.
GET /bookings/{bookingId}/messages— list all processed messages for a booking, ordered bysentAtascending. Page-based pagination viapageNumberandpageSize(default 20, max 100).- Required scopes:
venue-booking:messaging:write(send),venue-booking:messaging:read(list).
Handling Webhook Events API v1.4.0
- New
message.receivedevent type: fires when a venue operator sends a message via their TMS. - New
messagesarray field onBookingNotification: populated formessage.receivedevents,nullforbooking.updated. - New
MessagePayloadschema:bookingId,content,senderName(nullable),sentAt.
Product.feedUrl: new nullable URI field onGET /venuesandGET /venues/{venueId}responses. When non-null, provides a direct URL to the pre-generated availability feed file for that product — a gzip-compressed JSON document covering a 90-day rolling window, regenerated every 4 hours. No authentication is required to download it.
X-Is-Cross-Saleheader onPOST /venues/{compositeId}/booking: optional boolean. Whentrue, any cross-sale booking label rules configured for the operator are applied automatically to the new booking.
- BREAKING CHANGE
BookingRequest.areaIdrenamed toBookingRequest.spaceId. Type changed fromstringtointeger. Obtain valid space IDs from thespacesarray in the availability response time slots.
AvailabilityTimeSlot.areas: removed. Usespacesfrom the product data to obtain valid space IDs for booking.
AvailabilityResponse.noAvailabilityCode: new nullable string field, populated only whentimesis empty. Machine-readable code indicating why no availability was found. Known values:NO_AVAILABILITY— no available slots for the requested parameters.NO_ELIGIBLE_PRODUCTS— no products match the request.FILTERED_BY_OPERATOR_RULES— all slots were filtered out by operator availability rules.FILTERED_BY_CAPACITY— no slots satisfy the requested party size.- Clients should treat unknown values as a generic no-availability condition, as new values may be introduced without prior notice.
AvailabilityResponse.noAvailabilityAction: removed. UsenoAvailabilityReason(human-readable) and the newnoAvailabilityCode(machine-readable) instead.
AvailabilityTimeSlot.requestReason: new nullable string field, present only whentypeisrequest. Indicates why the time slot requires manual operator approval rather than instant confirmation. Possible values:fully_booked— no real-time availability for this slot, but the operator is open to receiving enquiries.exceeds_auto_confirm— availability exists but the requested party size exceeds the venue's auto-confirm threshold and requires explicit operator approval.- Clients should handle unknown values gracefully as new values may be introduced in future versions.
AvailabilityResponse.noAvailabilityReason: new nullable string field, populated only whentimesis empty. Human-readable message explaining why no availability was found for the requested parameters.Product.slug: new nullable string field — aggregation slug identifier for the product.PreorderPackage.slug: new nullable string field — aggregation slug identifier for the package.Menu.slug: new nullable string field — aggregation slug identifier for the menu.
Product.spaces: new field onGET /venuesandGET /venues/{venueId}responses. Each product now returns the list of spaces available within it, including physical data (spaceName,description,floorLocation,facilities, capacity layout fields) and space policies (minimumSpend,occasionTypes,under18s,promotedEvents,accessibility,servesFood).Product.preOrderRequiredType: enum specifying which type of preorder is required —none,package,menu, orpackageAndMenu.Product.depositRequired: boolean indicating whether a deposit is required when booking a product.AvailabilityRequest.children: optional field for the number of children in the party. Note: not all TMS providers distinguish between adults and children — when unsupported, children may be added to the total party size or ignored.AvailabilityRequestspace policy filters — new optional query parameters to filter time slots by space policy:minimumSpend: maximum minimum spend (in pence); excludes slots above this threshold.under18s: soft filter on the under-18s policy.occasionTypes: filter by supported occasion type.promotedEvents: soft filter on the promoted-events policy.accessibility: filter by wheelchair accessibility.servesFood: soft filter on the serves-food policy.includeUnavailable: whentrue, filtered-out slots are included in the response withunavailableReasonspopulated instead of being excluded.
AvailabilityTimeSlot.product: new object returned in each time slot containing the product'sid,name, andspacesarray. Space policies within this object (includingminimumSpend) are pre-filtered to the requested date and time slot.AvailabilityTimeSlot.areas: list of available areas/zones for this time slot. Populated only for TMS providers that support area-based booking (e.g. Zonal). Pass the desiredidasareaIdwhen creating a booking.AvailabilityTimeSlot.unavailableReasons: present only whenincludeUnavailable=true; contains human-readable explanations for each failing policy filter.BookingRequest.children: optional field for the number of children included in the booking.BookingRequest.areaId: optional area/zone ID to target when creating a booking. Obtain valid IDs fromAvailabilityTimeSlot.areas.Booking.depositAmount: deposit amount (float, in pence) captured from the TMS at reservation time.Booking.area: area or zone assigned to the booking, withidandnameas reported by the TMS.- New schemas:
SpaceCapacityRange,Area,BookingArea. - New shared component files:
common/VenueSpaces.yaml(space schemas and policy types) andcommon/Headers.yaml(shared header parameter definitions).
AvailabilityRequest.partySizeandBookingRequest.partySize: description clarified from "number of guests/people" to "number of adults".CorrelationIDandPartnerReferenceheader parameters refactored to referencecommon/Headers.yamlinstead of being defined inline.
AvailabilityTimeSlot.productId: useAvailabilityTimeSlot.product.idinstead.
- Sparse fieldsets support via the
fieldsquery parameter on all GET endpoints. Request only the fields you need to reduce payload size and improve response times. See Sparse Fieldsets for details. text/toonresponse format support across all Bookings API endpoints that return a body. SetAccept: text/toonto receive responses in TOON (Terse Object-Oriented Notation) — a compact format that reduces token count by 30–60% compared to JSON. Fully opt-in; omitting the header continues to return standard JSON. See TOON Format for details.VenuePreorders: addedpreordersfield toGET /venuesandGET /venues/{venueId}responses.
VenuePreordersschema: new wrapper object containingpackagesandmenusarraysProductAvailabilityRulesschema: availability rules for a product grouped by source (weekly,exceptions,rules)WeeklyRuleschema: weekly booking schedule, one entry per day of the weekRuleExceptionschema: date-specific availability exceptions imposed by the TMSAvailabilityRuleschema: operator-defined availability rules per partnerAvailabilityRuleTypeenum:date_range,recurring,time_based,specific_dates,specific_dates_with_time,date_range_with_time,recurring_with_timeRuleDataBaseand all discriminated subtypes:DateRangeRuleData,RecurringRuleData,TimeBasedRuleData,SpecificDatesRuleData,SpecificDatesWithTimeRuleData,DateRangeWithTimeRuleData,RecurringWithTimeRuleDataPreorderContainerschema: container for all preorder items (packages→PreorderPackageRequest,menus→PreorderMenuRequestV2)MenuItemOptionschema: configurable option for a menu item (fieldvalue)MenuItem: added fieldsname,price,description,diet_types,allergens,configurable_options,type,subType,displayOrderMenuItemSelection: added fieldoptions(array ofMenuItemOption)Booking.preorders.menus: added fieldsubmitted(enum:pre/post)PATCH /venues/bookings/{bookingId}: new endpoint for partial booking updates via JSON Patch (RFC 6902)JsonPatchOperationschema supportingadd,remove,replaceoperationsProduct: added fieldpreOrderRequired(boolean)
MenuItem.subGroupNamerenamed tosubTypePOST /venues/{compositeId}/bookingresponse201: changed fromBookingResponseto fullBookingschema- Terminology updated throughout: "RMS (Restaurant Management System)" → "TMS (Table Management System)"
- Security schemes consolidated: staging environments (
Staging,SandboxStaging,SandboxProduction) removed; onlyProductionandSandboxremain - Servers list reduced to
Production(https://api.bookabletech.com) andSandbox(https://api-sandbox.bookabletech.com)
Venue.packages(top-level array) — replaced byVenue.preorders.packagesVenue.menus(top-level array) — replaced byVenue.preorders.menusBookingRequest.preorderPackages— replaced byBookingRequest.preorders.packagesBookingRequest.preorderMenus— replaced byBookingRequest.preorders.menus- Sandbox Staging server
https://api-sandbox-staging.bookabletech.com
compositeIdquery filter to theGET /venues/bookingsendpoint.
BREAKING CHANGE in the following endpoints:
GET /venues/bookingsGET /venues/bookings/{bookingId}PUT /venues/bookings/{bookingId}Changed:
companyfield is nowvenueGroupNameproductTypefield is nowproductName
Removed:
cursorquery parameter fromGET /venues/bookingscursorfield from response fromGET /venues/bookings
Added: Standard pagination parameters in
GET /venues/bookings:pageNumber(integer, default: 1, min: 1) - The page index to returnpageSize(integer, default: 20, min: 1, max: 100) - Items per pageNumbermetaobject with pagination metadata:
"meta": { "currentPage": 1, "pageSize": 20, "totalItems": 26, "totalPages": 2 }Migration Guide:
- GET /venues/bookings?cursor=abc123 + GET /venues/bookings?pageNumber=1&pageSize=20GET /venues/{compositeId}/availabilityAdded: preOrderItems
venueGroupNamefield on venue object inGET /venuesandGET /venues/{venueId}endpoints.
- Remove constraints on bookingRules.
X-Booking-Sourceheader inPOST venues/bookingsis now an enumeration.
Added sorting capabilities to
GET /venuesendpoint:New Query Parameters
sortBy(optional): Field to sort results bydefault- Sort by composite ID (venue_group_id, rms_id, venue_id)name- Sort alphabetically by venue namecity- Sort by city name, then venue namearea- Sort by area, then venue namevenueGroup- Sort by venue group name (requires venueGroupName filter)relevance- Sort by semantic similarity using vector embeddings (requires embedding filters)
sortDirection(optional): Sort directionASC- Ascending (A-Z, 0-9, most to least similar)DESC- Descending (Z-A, 9-0, least to most similar)
- Operator Booking Id and Partner Booking Id support to
GET venues/bookingsbyoperatorBookingIdandpartnerBookingIdfields - Partner Booking Id support to
POST venues/bookingsbypartnerBookingIdfield
Referencefield is deprecated inGET venues/bookings; useoperatorBookingIdinstead.
- Booking Overrides support to
GET venuesandGET venues\{venueId}bybookingOverridesfield - Operator availability rule support to
GET venuesandGET venues\{venueId}byoperatorAvailabilityRulesfield
- Preorders support to
GET venues/bookingsbypreordersfield - Preorder menus support to
GET venues/bookingsbypreorderMenusfield
- Possibility to filter by
dateandtimewithin theGET /venuesendpoint.
X-Booking-Sourceheader which identifies the source or channel through which the booking was created.
X-Partner-Referenceheader is now mandatory on all the endpoints for Bookable Agent clients.
- noAvailabilityAction support to
GET venues/:venueId/availability - autoConfirmRule support to
GET venues/:venueId/availability
- Preorder menu support to
GET venuesandGET venues/:venueIdbymenusfield - Preorder menu support to
POST venues/:venueId/bookingbypreorderMenusfield - Preorder menu email triggering on submit booking
- Preorder menu support to
GET venues/bookings/:bookingIdbypreorderMenusfield
packageIdfield onPOST venues/:venueId/bookingrequest
- BREAKING CHANGE: Booking PUT request now requires
compositeId
GET /venues/products/{compositeId}endpoint
- Venue packages detail by
packages - Booking POST request now accepts
packageId - Booking POST request now accepts
preorders
- Endpoint to get a venue product detail by
compositeId
- Venue Schema Fields: Deprecated
is_activeandcreated_atfields in venue responses - Booking POST request now accepts
nullvalue fordurationfield
- Booking POST request includes admin_notes, comments & labels field
- Standardized error handling across all endpoints
- BREAKING CHANGE: Modified venue composition - products are now contained within the venue structure
- X-Partner-Reference header for Bookable Agents. The header specifies the partner reference for whom the booking is being made.
- Endpoint to update an existing booking by its unique ID
- Endpoint to cancel a booking by its unique ID
- Endpoint to retrieve a paginated list of bookings
- Endpoint to retrieve a specific booking by its unique ID
- Support for paginated venue listings
- Rename shut into bookingCutoff
- bookingRules: Insert booking rules on Venue to show information about the opening/ close time, max min number of people, and other
- Validation on require field (#TS-948)
- Add the unified api
- VenueId: Introduced the concept of composite Id to manage id into the system
- type into avalability response: Specifies how an operators time slot can be handled.
- type into revervation request
- product_name: the name of product offer from venue