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"
}
  • type is 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.
  • title is the status text; detail is the sentence to show a developer (localized by Accept-Language).
  • traceId is on every problem, and sentryId when an error was captured — quote them when you write to support.
  • A validation failure (400) adds an errors object 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 type URL, explained.