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 typeErrors 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.