Embed & public widget endpoints
These are the public endpoints behind the Sidedoor widgets you embed on your own website - booking, events, vouchers and the all-in-one combo. They need no bearer token: a widget is identified by its venue (or event). For the operator-facing setup - enabling widgets, theming and grabbing the embed URL - see the widgets guide.
Endpoints
Section titled “Endpoints”| Method | Path | Purpose | Auth |
|---|---|---|---|
| GET | /api/public/venues/:venueId/booking-widget-config |
Booking widget config, keys and theme | public |
| GET | /api/public/venues/:venueId/availability |
Open slots for a date and party size | public |
| GET | /api/public/venues/:venueId/floorplan |
Floors and tables for guest table-pick | public |
| POST | /api/public/venues/:venueId/book |
Create a booking (may need payment) | public |
| POST | /api/public/venues/:venueId/book/finalize |
Finalise a deposit after payment | public |
| GET | /api/public/venues/:venueId/booking/:bookingId/qr |
Fetch the booking’s QR code | public |
| GET | /api/public/venues/:venueId/booking/:bookingId/gwallet |
Google Wallet pass for the booking | public |
| GET | /api/public/venues/:venueId/booking/:bookingId/apple |
Apple Wallet pass for the booking | public |
| GET | /api/public/venues/:venueId/booking/:bookingId/calendar.ics |
Add-to-calendar .ics file for the booking |
public |
| POST | /api/public/venues/:venueId/waitlist |
Join the waitlist when a slot is full | public |
| GET | /api/public/venues/:venueId/events-widget |
Events widget listing | public |
| GET | /api/public/venues/:venueId/events-widget-config |
Events widget config and keys | public |
| GET | /api/public/events/:eventId/booking-options |
Bundles, ticket types and policy for an event | public |
| GET | /api/public/events/:eventId/promo |
Validate a promo code before booking | public |
| POST | /api/public/events/:eventId/create-intent |
Start an event booking | public |
| POST | /api/public/events/:eventId/finalize |
Finalise an event booking after payment | public |
| GET | /api/public/venues/:venueId/voucher-config |
Voucher widget config and keys | public |
| POST | /api/public/voucher-purchase/create-intent |
Start a gift-voucher purchase | public |
| POST | /api/public/voucher-purchase/finalize |
Finalise a voucher purchase, return codes | public |
| GET | /api/public/venues/:venueId/voucher/:code/gwallet |
Google Wallet pass for a purchased voucher | public |
| GET | /api/public/venues/:venueId/voucher/:code/apple |
Apple Wallet pass for a purchased voucher | public |
| GET | /api/public/venues/:venueId/guestlist-options |
Guest-list rates, surge and promo | public |
| POST | /api/public/venues/:venueId/guestlist-bookings |
Join the guest list (spends keys) | session |
| GET | /api/public/venues/:venueId/combo-widget-config |
All-in-one widget config | public |
| GET | /api/public/search |
Search venues and events across Sidedoor | public |
| GET | /api/public/venues/:venueId/experiences |
A venue’s branded experiences | public |
| GET | /api/public/venues/:venueId/reservation-tag-catalogue |
Reservation tags a guest can pick | public |
| POST | /api/public/venues/:venueId/book/quote |
Price a booking before making it | public |
| POST | /api/public/venues/:venueId/book/hold |
Hold a table during checkout | public |
| POST | /api/public/venues/:venueId/book/hold/confirm |
Convert a hold into a booking (no deposit) | public |
| POST | /api/public/venues/:venueId/book/hold/refresh |
Keep a checkout hold alive | public |
| POST | /api/public/venues/:venueId/book/hold/release |
Release a checkout hold | public |
| POST | /api/public/venues/:venueId/guestlist-hold |
Hold a guest-list spot during checkout | public |
| POST | /api/public/venues/:venueId/guestlist-hold/release |
Release a guest-list hold | public |
| POST | /api/public/events/:eventId/ticket-hold |
Hold free tickets during checkout | public |
| POST | /api/public/events/:eventId/ticket-hold/release |
Release a ticket hold | public |
Fetch a widget config
Section titled “Fetch a widget config”GET /api/public/venues/:venueId/booking-widget-config returns the venue’s
booking-widget settings, its theme, the display currency, and a Stripe
publishable_key for the hosted card step. Fetch this first when bootstrapping a
widget. Payment is handled by Sidedoor’s hosted widget - you never deal with card
details or payment credentials yourself.
curl "https://thesidedoor.co/api/public/venues/VENUE_ID/booking-widget-config"const res = await fetch( "https://thesidedoor.co/api/public/venues/VENUE_ID/booking-widget-config",);const config = await res.json();$ch = curl_init( "https://thesidedoor.co/api/public/venues/VENUE_ID/booking-widget-config");curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);$config = json_decode(curl_exec($ch), true);{ "currency": "GBP", "venue": { "id": "VENUE_ID", "name": "The Blue Room" }, "widget": { "enabled": true, "theme_mode": "detailed", "accent_color": "#C0A060" }, "publishable_key": "pk_live_..."}Discovery
Section titled “Discovery”A few endpoints aren’t tied to one widget’s flow - use them to build your own search box or to show a venue’s extras before a guest picks a slot.
GET /api/public/search searches venues and events across Sidedoor by q,
with optional kind (venue/event), type, cuisine, when and group
filters. It needs no venue id and is open to any site (CORS *).
curl "https://thesidedoor.co/api/public/search?q=cocktail&kind=venue"{ "results": [{ "id": "ven_123", "name": "The Blue Room", "type": "venue" }] }GET /api/public/venues/:venueId/experiences returns a venue’s branded
experiences (e.g. “Sunday Roast”, “Tasting Menu”) for display before a guest
picks a slot - each with a title, description, image_url and the
days/times it runs.
curl "https://thesidedoor.co/api/public/venues/VENUE_ID/experiences"{ "experiences": [ { "id": "rul_roast", "title": "Sunday Roast", "description": "Three courses, served 12-4pm.", "image_url": "https://...", "days_of_week": [0], "time_from": "12:00", "time_to": "16:00" } ]}GET /api/public/venues/:venueId/reservation-tag-catalogue returns the
reservation tags a guest can pick when booking a table (grouped by
category), for a venue that lets guests add their own. An empty
categories array just means the venue doesn’t offer any.
curl "https://thesidedoor.co/api/public/venues/VENUE_ID/reservation-tag-catalogue"{ "categories": [{ "category": "Occasion", "tags": ["Birthday", "Anniversary"] }] }Read availability
Section titled “Read availability”GET /api/public/venues/:venueId/availability returns bookable slots for a
date and party_size. Set type to tables or guestlist. It also returns
the applicable rules, bundles and, when the requested time is full, the next
available dates.
curl "https://thesidedoor.co/api/public/venues/VENUE_ID/availability?date=2026-07-18&party_size=4&type=tables"const params = new URLSearchParams({ date: "2026-07-18", party_size: "4", type: "tables",});const res = await fetch( `https://thesidedoor.co/api/public/venues/VENUE_ID/availability?${params}`,);const availability = await res.json();$q = http_build_query([ "date" => "2026-07-18", "party_size" => 4, "type" => "tables",]);$ch = curl_init( "https://thesidedoor.co/api/public/venues/VENUE_ID/availability?{$q}");curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);$availability = json_decode(curl_exec($ch), true);{ "venue": { "id": "VENUE_ID", "name": "The Blue Room" }, "date": "2026-07-18", "party_size": 4, "type": "tables", "slots": [ { "time": "19:00", "access_rule_id": "rul_dinner", "available": true }, { "time": "19:30", "access_rule_id": "rul_dinner", "available": true } ], "party_too_large": false, "next_available_dates": []}For a floorplan where the guest picks their own table, call
GET /api/public/venues/:venueId/floorplan with the same date, time and
party_size (plus the chosen rule_id); it returns { floors, tables } with
per-table availability.
Create a booking
Section titled “Create a booking”POST /api/public/venues/:venueId/book creates the booking. Pass the slot the
guest chose (access_rule_id, rule_type, date, time, party_size), any
bundle_selections, and the guest’s contact details.
The response depends on the venue’s payment policy. With no deposit you get
{ ok: true, booking_id, status }. When a deposit is required you get
{ requires_payment: true, booking_id } - Sidedoor’s hosted widget takes the
guest through the secure card step, then you finalise (below).
curl -X POST "https://thesidedoor.co/api/public/venues/VENUE_ID/book" \ -H "Content-Type: application/json" \ -d '{ "access_rule_id": "rul_dinner", "rule_type": "tables", "date": "2026-07-18", "time": "19:00", "party_size": 4, "guest_name": "Sam Guest", "guest_email": "sam@example.com", "guest_phone": "+447700900123" }'const res = await fetch( "https://thesidedoor.co/api/public/venues/VENUE_ID/book", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ access_rule_id: "rul_dinner", rule_type: "tables", date: "2026-07-18", time: "19:00", party_size: 4, guest_name: "Sam Guest", guest_email: "sam@example.com", guest_phone: "+447700900123", }), },);const result = await res.json();$ch = curl_init("https://thesidedoor.co/api/public/venues/VENUE_ID/book");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Content-Type: application/json"], CURLOPT_POSTFIELDS => json_encode([ "access_rule_id" => "rul_dinner", "rule_type" => "tables", "date" => "2026-07-18", "time" => "19:00", "party_size" => 4, "guest_name" => "Sam Guest", "guest_email" => "sam@example.com", "guest_phone" => "+447700900123", ]),]);$result = json_decode(curl_exec($ch), true);{ "requires_payment": true, "booking_id": "bkg_456"}Quote and hold a table
Section titled “Quote and hold a table”Two optional steps can sit in front of /book for a table booking.
POST /api/public/venues/:venueId/book/quote prices a selection - the same
body as /book (access_rule_id, rule_type, date, time, party_size,
bundle_selections, plus any table/area/combo pick) without guest details -
so you can show the real amount before the guest commits. It’s read-only: no
booking or hold is created.
curl -X POST "https://thesidedoor.co/api/public/venues/VENUE_ID/book/quote" \ -H "Content-Type: application/json" \ -d '{"access_rule_id":"rul_dinner","rule_type":"tables","date":"2026-07-18","time":"19:00","party_size":4}'{ "payment": { "kind": "hold", "amount_pence": 4000, "currency": "GBP" }, "table_id": "tbl_12", "combo_id": null }POST /api/public/venues/:venueId/book/hold claims a specific free table for
a short window while the guest finishes checkout, so another guest can’t take
it from under them - only on venues whose table profile has checkout holds
turned on; otherwise it returns { "held": false } and booking proceeds as
normal. Pass the same slot fields; keep it alive with
POST .../book/hold/refresh ({ hold_id }), and either convert it with
POST .../book/hold/confirm ({ hold_id } - converts straight to a booking
when no payment is due) or release it early with
POST .../book/hold/release ({ hold_id }) if the guest abandons checkout.
A paid booking instead carries the hold_id through to /book itself.
curl -X POST "https://thesidedoor.co/api/public/venues/VENUE_ID/book/hold" \ -H "Content-Type: application/json" \ -d '{"access_rule_id":"rul_dinner","date":"2026-07-18","time":"19:00","party_size":4}'{ "held": true, "hold_id": "hld_1", "held_until": "2026-07-01T18:35:00Z", "hold_minutes": 5 }POST /api/public/venues/:venueId/book/hold/confirm { "hold_id": "hld_1" }
{ "ok": true, "booking_id": "bkg_456", "status": "approved" }Finalise a deposit
Section titled “Finalise a deposit”Once the guest completes the card step in the Sidedoor widget, call
POST /api/public/venues/:venueId/book/finalize with the booking_id. It
re-checks the payment and either confirms the booking or releases it if payment
did not complete. It is idempotent - safe to retry.
curl -X POST "https://thesidedoor.co/api/public/venues/VENUE_ID/book/finalize" \ -H "Content-Type: application/json" \ -d '{"booking_id":"bkg_456"}'const res = await fetch( "https://thesidedoor.co/api/public/venues/VENUE_ID/book/finalize", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ booking_id: "bkg_456" }), },);const result = await res.json();$ch = curl_init( "https://thesidedoor.co/api/public/venues/VENUE_ID/book/finalize");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Content-Type: application/json"], CURLOPT_POSTFIELDS => json_encode(["booking_id" => "bkg_456"]),]);$result = json_decode(curl_exec($ch), true);{ "ok": true, "booking_id": "bkg_456", "status": "confirmed" }Show the booking QR
Section titled “Show the booking QR”Once confirmed, fetch the door QR with
GET /api/public/venues/:venueId/booking/:bookingId/qr. The code is
short-lived - expires_in_ms tells you when to refresh it.
curl "https://thesidedoor.co/api/public/venues/VENUE_ID/booking/bkg_456/qr"const res = await fetch( "https://thesidedoor.co/api/public/venues/VENUE_ID/booking/bkg_456/qr",);const { qr_code, expires_in_ms } = await res.json();$ch = curl_init( "https://thesidedoor.co/api/public/venues/VENUE_ID/booking/bkg_456/qr");curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);$qr = json_decode(curl_exec($ch), true);{ "qr_code": "data:image/png;base64,iVBOR...", "issued_at": 1751500000, "expires_in_ms": 60000 }Wallet passes and calendar
Section titled “Wallet passes and calendar”For a confirmed booking you can also offer a wallet pass or an add-to-calendar
file. Like the QR route, these are keyed on the booking_id (the guest’s own
capability token) so they need no bearer token:
GET /api/public/venues/:venueId/booking/:bookingId/gwallet- Google Wallet pass.GET /api/public/venues/:venueId/booking/:bookingId/apple- Apple Wallet pass.GET /api/public/venues/:venueId/booking/:bookingId/calendar.ics- an.icsfile the guest can add to their calendar.
The same wallet pair exists for a purchased voucher, keyed on the voucher code:
GET /api/public/venues/:venueId/voucher/:code/gwallet and .../apple.
Join a waitlist
Section titled “Join a waitlist”When /availability reports a slot is full (for example party_too_large with
next_available_dates), offer the waitlist instead. POST /api/public/venues/:venueId/waitlist takes the slot the guest wanted
(access_rule_id, date, time, party_size) and their contact details.
curl -X POST "https://thesidedoor.co/api/public/venues/VENUE_ID/waitlist" \ -H "Content-Type: application/json" \ -d '{ "access_rule_id": "rul_dinner", "date": "2026-07-18", "time": "19:00", "party_size": 4, "guest_name": "Sam Guest", "guest_email": "sam@example.com" }'Events widget
Section titled “Events widget”List a venue’s what’s-on with GET /api/public/venues/:venueId/events-widget
(and .../events-widget-config for keys and theme). For a chosen event, read
GET /api/public/events/:eventId/booking-options to get bundles, ticket types
and policy, then book with POST /api/public/events/:eventId/create-intent.
The create-intent body carries the selection ({ kind, ticket_type_id, quantity, party_size }) and guest details. It returns { free, booking_id },
{ requires_payment, ... } or { requires_confirmation }. When payment is
needed, the Sidedoor widget takes the guest through the card step; then call
POST /api/public/events/:eventId/finalize with the returned payment_intent_id.
To validate a promo code before booking, call
GET /api/public/events/:eventId/promo?code=CODE. It returns
{ applied: true, ... } with the discount type and amount when the code is
valid, or { applied: false, error } when it is not.
curl -X POST "https://thesidedoor.co/api/public/events/EVENT_ID/create-intent" \ -H "Content-Type: application/json" \ -d '{ "selection": { "kind": "ticket", "ticket_type_id": "tkt_ga", "quantity": 2 }, "guest_name": "Sam Guest", "guest_email": "sam@example.com" }'const res = await fetch( "https://thesidedoor.co/api/public/events/EVENT_ID/create-intent", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ selection: { kind: "ticket", ticket_type_id: "tkt_ga", quantity: 2 }, guest_name: "Sam Guest", guest_email: "sam@example.com", }), },);const result = await res.json();$ch = curl_init( "https://thesidedoor.co/api/public/events/EVENT_ID/create-intent");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Content-Type: application/json"], CURLOPT_POSTFIELDS => json_encode([ "selection" => ["kind" => "ticket", "ticket_type_id" => "tkt_ga", "quantity" => 2], "guest_name" => "Sam Guest", "guest_email" => "sam@example.com", ]),]);$result = json_decode(curl_exec($ch), true);{ "requires_payment": true, "booking_id": "evb_789", "payment_intent_id": "pi_3Nxyz" }Hold free tickets during checkout
Section titled “Hold free tickets during checkout”For a free ticket type, POST /api/public/events/:eventId/ticket-hold
claims the requested quantity of a ticket_type_id for a short window
while the guest finishes checkout, so they don’t lose them to another guest.
Release it early with POST .../ticket-hold/release ({ hold_id }) if the
guest abandons checkout; otherwise it’s released automatically on expiry.
curl -X POST "https://thesidedoor.co/api/public/events/EVENT_ID/ticket-hold" \ -H "Content-Type: application/json" \ -d '{"ticket_type_id":"tkt_ga","quantity":2}'{ "held": true, "hold_id": "hld_evt_1", "held_until": "2026-07-01T18:35:00Z", "hold_minutes": 5 }Voucher widget
Section titled “Voucher widget”Read GET /api/public/venues/:venueId/voucher-config for settings and keys.
Start a purchase with POST /api/public/voucher-purchase/create-intent (amount,
quantity, delivery and purchaser details); once the guest completes the card step
in the Sidedoor widget, call POST /api/public/voucher-purchase/finalize with the
intent_id to receive the issued voucher codes.
curl -X POST "https://thesidedoor.co/api/public/voucher-purchase/create-intent" \ -H "Content-Type: application/json" \ -d '{ "venue_id": "VENUE_ID", "amount_pence": 5000, "quantity": 1, "delivery": "recipient", "recipient_name": "Alex", "recipient_email": "alex@example.com", "purchaser_name": "Sam Guest", "purchaser_email": "sam@example.com" }'const res = await fetch( "https://thesidedoor.co/api/public/voucher-purchase/create-intent", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ venue_id: "VENUE_ID", amount_pence: 5000, quantity: 1, delivery: "recipient", recipient_name: "Alex", recipient_email: "alex@example.com", purchaser_name: "Sam Guest", purchaser_email: "sam@example.com", }), },);const result = await res.json();$ch = curl_init( "https://thesidedoor.co/api/public/voucher-purchase/create-intent");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Content-Type: application/json"], CURLOPT_POSTFIELDS => json_encode([ "venue_id" => "VENUE_ID", "amount_pence" => 5000, "quantity" => 1, "delivery" => "recipient", "recipient_name" => "Alex", "recipient_email" => "alex@example.com", "purchaser_name" => "Sam Guest", "purchaser_email" => "sam@example.com", ]),]);$result = json_decode(curl_exec($ch), true);{ "intent_id": "vin_321" }Once the card step is complete, finalise:
POST /api/public/voucher-purchase/finalize { "intent_id": "vin_321" }
{ "success": true, "delivery": "recipient", "vouchers": [ { "code": "GIFT-4KQ2-9XTP", "amount_pence": 5000, "expires_at": "2027-07-03" } ]}Guest list
Section titled “Guest list”Read GET /api/public/venues/:venueId/guestlist-options for the rate, any surge
and promo eligibility, then join with
POST /api/public/venues/:venueId/guestlist-bookings. This one needs a
session - the guest must be signed in, because it spends guest keys rather
than taking a card payment. The body is
{ rule_id, date, party_size, option_kind, promo_code }.
{ "success": true, "booking_id": "bkg_902", "status": "approved", "keys_spent": 2, "auto_approved": true }Hold a guest-list spot during checkout
Section titled “Hold a guest-list spot during checkout”POST /api/public/venues/:venueId/guestlist-hold claims a guest-list spot
for a short window while the guest finishes checkout, the same pattern as the
table checkout hold above. Pass the slot (rule_id, date, party_size);
release it early with POST .../guestlist-hold/release ({ hold_id }) if
the guest abandons checkout, otherwise it’s released automatically on
expiry. POST /guestlist-bookings carries the hold_id through to convert it.
curl -X POST "https://thesidedoor.co/api/public/venues/VENUE_ID/guestlist-hold" \ -H "Content-Type: application/json" \ -d '{"rule_id":"rul_gl","date":"2026-07-18","party_size":4}'{ "held": true, "hold_id": "hld_gl_1", "held_until": "2026-07-01T18:35:00Z", "hold_minutes": 5 }All-in-one (combo) widget
Section titled “All-in-one (combo) widget”GET /api/public/venues/:venueId/combo-widget-config returns which flows are
enabled (bookings, events, vouchers) plus the links the combo widget needs
to route into each of the flows above.
{ "enabled": { "bookings": true, "events": true, "vouchers": false }, "links": { "bookings": "...", "events": "..." }}