Sign in to see your venue's slug and publishable key in every sample.
Your account has no venue yet, so the samples keep their placeholders. Sign out
Signed in as · . Create a publishable key in the console and reload to see it here. Sign out
Signed in as · . The samples show your venue's publishable key. Sign out
Developer docs
Booking flow
What the widget does end to end — the four API calls it makes and in which order, every step a guest sees, the optional deposit, and the confirmation.
The widget is a client of the public API and nothing more: it keeps no state on our side, and every screen a guest sees is the result of one of four calls. This guide walks through those calls in the order they happen, what the widget shows and sends at each step, and what your page can and cannot observe. If you build your own interface instead of embedding the widget, this is the sequence to reproduce.
The four calls, in order
| When | Call | Why |
|---|---|---|
Immediately after renderBookingWidget |
GET /api/venues/{venueSlug} |
The venue profile: its bookingDurations (which lengths a guest may pick) and extras (paid add-ons to offer) |
| When the guest asks for times | GET /api/venues/{venueSlug}/availability |
The day's slots for the chosen type, date, party size and length |
| With the first availability lookup | GET /api/venues/{venueSlug}/floor-plan |
The venue's table map, if it published one — fetched once, never blocking |
| When the guest confirms | POST /api/venues/{venueSlug}/reservations |
The booking itself |
Every call carries the publishableKey you passed as X-BookDinePlay-Key, and the API answers only when the page's origin is on that key's list (Origins). The widget never creates a payment intent — see Deposits.
Step by step
The widget renders five steps under a progress bar. While availability loads the step shows a loading state; while the reservation is in flight the button reads "Confirming..." and is disabled; the venue and floor-plan calls show nothing. A failed request shows a red banner above the current step and leaves the guest where they were.
1. What to book
One card per type you passed in resourceTypes. Choosing one also selects the venue's default booking length for that type, so a length picked for darts never carries over to a restaurant table. With a single type it is pre-selected, but the step still shows.
2. Date and party size
A date (today or later), a party size from 1 to 12 (2 by default) and — only when the venue lets guests choose a length for that type and offers more than one — a "How long?" selector pre-set to the venue's default. "See available times" validates the date and sends the availability call.
3. Available times
The slots the API returned, available ones only, each with its time range, the resource's name and a "from" price when the slot carries one. A day with nothing free shows "No open times for this date and party size." and a hint to try another date or a smaller party. When the venue has published a floor plan with placed resources of that type and at least one slot is free, the guest instead picks a time and then a table on the floor; "List view" switches back to the list. Picking a slot and "Continue" — or tapping a table on the floor plan — goes to step 4.
4. Contact details and the optional deposit
Name and email are required to enable the button; phone and notes are optional in the form. Where they apply, the step also shows a games-per-player selector (1–10) for a slot priced per game and the venue's extras for that resource type with a quantity each. Last, a checkbox — "Secure my spot with a deposit" — unticked by default. "Confirm reservation" sends the reservation.
5. Confirmation
"Reservation confirmed" — or "Reservation received" when the reservation came back Pending — with the reference (BDP-XXXX), the venue, what and when was booked, the party size, the name, the price estimate and, when one applies, the deposit amount. The API has emailed the guest the reference and status by then. "Make another booking" resets to step 1; the venue profile and the floor plan are kept, not fetched again.
What the widget sends
The availability query string, built from steps 1 and 2:
| Parameter | Value |
|---|---|
date |
The chosen date, venue-local yyyy-MM-dd |
partySize |
The chosen party size |
resourceType |
The chosen type |
durationMinutes |
Only when the guest picked a length; omitted otherwise so the venue's default applies to every resource |
The reservation body, built from the chosen slot and step 4. Every field is always present; what does not apply is null:
| Field | Value |
|---|---|
resourceType |
The chosen type |
resourceId |
The chosen slot's resourceId, so exactly that resource is booked |
date |
The chosen date |
start |
The slot's start |
end |
The slot's end |
partySize |
The chosen party size |
games |
Games per player for a slot priced per game; null otherwise |
durationMinutes |
The picked length when the picker was shown; null otherwise |
extras |
[{ "extraId": "…", "quantity": 1 }] for every extra with a quantity above zero; null when none |
customerName |
The name, trimmed |
email |
The email, trimmed |
phone |
The phone, trimmed; optional — a blank value counts as not supplied, 3–40 characters when given |
notes |
The notes, trimmed; empty string when blank |
depositOptIn |
true when the checkbox was ticked |
{
"resourceType": "RestaurantTable",
"resourceId": "table-4a",
"date": "2026-10-16",
"start": "19:00",
"end": "21:00",
"partySize": 4,
"games": null,
"durationMinutes": null,
"extras": null,
"customerName": "Ada Lovelace",
"email": "ada@example.com",
"phone": "+49 30 1234567",
"notes": "",
"depositOptIn": false
}What your page can observe
renderBookingWidget returns the widget instance, or null when it refused to render (When it refuses to render). That is the whole surface: the widget emits no DOM events, takes no callbacks, and the instance's fields are not part of the contract. To react to a completed booking on your side — analytics, a thank-you page, your own CRM — build your own flow on the same four calls with the pure helpers under window.BookDinePlay.helpers (Advanced: helpers and version).
Deposits
Ticking the checkbox sends depositOptIn: true. A deposit is then required only when the venue has deposits enabled and the resource — or the venue's default for game resources — carries a deposit amount; all three, or none (Deposits). When it is required, the reservation comes back Pending with depositRequired: true and a depositAmount; the widget shows "Reservation received" and the deposit line, and the guest's email shows the status Pending. The widget does not collect the money: the venue confirms the booking in its console, or a server-side integration creates a payment intent for depositAmount. Without a deposit — the checkbox unticked, or the venue not set up for one — the reservation is Confirmed immediately.
When a call fails
- Venue profile or floor plan — the widget carries on without them: no extras, no length picker, the list view instead of the floor.
- Availability — "We could not load available times. Please check your connection and try again." The guest stays on step 2.
- Reservation — "We could not confirm your reservation. Please review your details and try again." The guest stays on step 4. This is the same banner for a validation error (400), a slot taken in the meantime (409) and a refused key (401/403); the browser's network tab shows the problem
type— Errors explains each one. - A superseded request — when the guest clicks again before an answer arrives, the earlier request is aborted and its answer ignored.
Next steps
- Azure Static Web Apps — put the widget on a live page, from nothing to a custom domain.
- Reservations — every field of the booking the widget makes, and the 409 it can get.
- JavaScript SDK — the options, isolation and CSP rules for the page that embeds it.