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) and gated where appropriate by a bot check (Turnstile). 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/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 |
| 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/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 |
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, and the publishable keys the widget needs
(Stripe publishable key and Turnstile site key). Fetch this first when
bootstrapping a widget.
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);{ "venue": { "id": "VENUE_ID", "name": "The Blue Room" }, "widget": { "enabled": true, "theme_mode": "detailed", "accent_color": "#C0A060" }, "publishable_key": "pk_live_...", "turnstile_site_key": "0x4AAA..."}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, the guest’s contact details and - for guest (non-logged-in)
callers - a turnstile_token.
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, payment_client_secret, booking_id } - confirm the
payment_client_secret with Stripe on the client, then 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", "turnstile_token": "TURNSTILE_TOKEN" }'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", turnstile_token: "TURNSTILE_TOKEN", }), },);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", "turnstile_token" => "TURNSTILE_TOKEN", ]),]);$result = json_decode(curl_exec($ch), true);{ "requires_payment": true, "booking_id": "bkg_456", "payment_client_secret": "pi_3Nabc_secret_xyz"}Finalise a deposit
Section titled “Finalise a deposit”After the guest confirms the payment_client_secret with Stripe, call
POST /api/public/venues/:venueId/book/finalize with the booking_id. It
re-reads the payment intent 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 }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 }), guest details and - for guests - a turnstile_token.
It returns { free, booking_id }, { requires_payment, ... } or
{ requires_confirmation }. When payment was needed, complete it and call
POST /api/public/events/:eventId/finalize with the payment_intent_id.
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", "turnstile_token": "TURNSTILE_TOKEN" }'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", turnstile_token: "TURNSTILE_TOKEN", }), },);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", "turnstile_token" => "TURNSTILE_TOKEN", ]),]);$result = json_decode(curl_exec($ch), true);{ "requires_payment": true, "booking_id": "evb_789", "payment_intent_id": "pi_3Nxyz" }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); after payment, 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", "client_secret": "pi_3Nvou_secret_abc" }After confirming payment, 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 }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": "..." }}