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
API reference
Base URL, headers, dates and time zones, the error format, and one page per endpoint group of the public BookDinePlay API.
Everything the widget, the SDKs and the WordPress plugin do, they do through this API. It is plain HTTPS and JSON at https://api.bookdineplay.com, one venue per URL, and the same rules on every endpoint: a key on every /api/venues/** call, venue-local dates and times, and RFC 7807 problem bodies when something is refused. This page has the rules; the pages below have the endpoints.
| Page | Endpoints |
|---|---|
| Venue | GET /api/venues/{venueSlug} and its business-info, opening-hours, menus, resources and floor-plan |
| Availability | GET /api/venues/{venueSlug}/availability |
| Reservations | POST /api/venues/{venueSlug}/reservations |
| Payment intents | POST /api/venues/{venueSlug}/payment-intents |
| QR codes and table sessions | GET /api/qr/{token}, the table session, orders and closing the tab |
| Messaging | POST /api/venues/{venueSlug}/messages |
| Errors | Every problem type the API emits, what caused it and what fixes it |
Base URL and versioning
The base URL is https://api.bookdineplay.com. There is no version segment in the path: changes are additive (new optional fields, new endpoints) and never remove or rename what is documented here. Every response is application/json (application/problem+json for errors) in UTF-8. The samples on these pages use that base URL; when you are signed in, they also show your own venue slug and publishable key instead of placeholders.
Keys and headers
Every /api/venues/** request presents a key — there is no anonymous tier — as X-BookDinePlay-Key: <key> or Authorization: Bearer <key>, never in the query string. A publishable key (bdp_pk_…) is for browsers: the request must carry an Origin header on the key's allowlist. A secret key (bdp_sk_…) is for servers: the request must not carry an Origin. Authentication has the full rules; Errors has every refusal.
GET /api/qr/{token} and the table-session endpoints under it need no key: the token in the URL is already a venue-scoped, revocable credential printed on the table.
Two optional headers apply everywhere: Accept-Language: de returns a German detail in problem bodies (English is the default); title is never localized — it is the framework's status text — and Content-Type: application/json is required on every POST.
Dates, times and time zones
The API speaks the venue's local time, never UTC-with-an-offset for anything a guest chooses:
| Value | Format | Example |
|---|---|---|
| A date | yyyy-MM-dd |
2026-10-16 |
| A time of day | HH:mm, 24-hour |
19:30 |
| The venue's time zone | IANA identifier, on the venue and on opening hours | Europe/Berlin |
An instant (createdAt, issuedAt, sentAt) |
ISO 8601 with offset | 2026-10-16T17:31:04.2810000+00:00 |
A slot's start of 19:30 on 2026-10-16 means 19:30 in Europe/Berlin on that date. Convert on your side only if you need an instant; never send an offset in a date or time field — the API rejects it as invalid.
Enumerations (resourceType, status, pricingModel, basis) are strings, exactly as spelled in the tables on the endpoint pages, and are case-sensitive.
Errors
Anything the API refuses is an RFC 7807 problem body with the matching HTTP status:
{
"type": "https://bookdineplay.com/docs/api/errors/origin-not-allowed",
"title": "Forbidden",
"status": 403,
"detail": "This origin is not on the API key's list of allowed origins.",
"traceId": "0HNOJ52B2L9BD"
}typeis a URL. For a key problem it points at the section of Errors that explains it; for everything else it is the generic RFC 9110 section link.titleis the status text;detailis the sentence to show a developer (localized byAccept-Language).traceIdis on every problem, andsentryIdwhen an error was captured — quote them when you write to support.- A validation failure (400) adds an
errorsobject with the field names as keys:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"PartySize": [
"The field PartySize must be between 1 and 200."
],
"CustomerName": [
"The CustomerName field is required."
]
},
"traceId": "0HNOJ52B2L9BA"
}Statuses you will see: 400 invalid input, 401 no or unknown key, 403 the key is fine but not for this request, 404 no such venue, reservation, token or session, 409 the request was valid but the world has moved on (slot taken, tab already closed, payments not enabled), 5xx ours — retry with backoff and keep the traceId.
CORS and preflight
The API answers a browser's OPTIONS preflight for any /api/venues/** path with GET, POST, OPTIONS, the request headers X-BookDinePlay-Key, Authorization, Content-Type, Accept-Language, and Access-Control-Max-Age: 600 — the same answer for every origin and every path under the prefix, because a preflight carries no key to check. Every /api/venues/** route, POST /api/venues/{venueSlug}/messages included, opts out of the framework's blanket CORS middleware; there is no Access-Control-Allow-Origin: *. On the real request that follows, that header is set by the key check itself — a publishable key only when its Origin is on that key's own allowlist (Origins) — which is the actual gate, not the preflight.
Rate limits
There is no enforced per-key rate limit today. Be a good neighbour: cache venue, menu and opening-hours responses on your side (they change rarely), and query availability for the date a guest is looking at rather than for a whole month. A limit, when one arrives, will be announced in advance and signalled with 429 and a Retry-After header.
Next steps
- Venue — the reads every integration starts with.
- Availability — the slots a guest can book.
- Errors — every
typeURL, explained.