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
Events GET /api/venues/{venueSlug}/events, one event's detail, POST …/events/{eventSlug}/registrations, the paid checkout stub, and the keyless GET /api/tickets/{code} and POST /api/event-registrations/{cancelToken}/cancel
Vouchers GET …/vouchers/offers, POST …/vouchers/checkout, the keyless GET /api/vouchers/{code}, and paying a tab, a deposit or tickets with a voucher
Rate limits Only the keyless voucher lookup is limited today — the numbers to design against anyway, and what good client behaviour looks like
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.

Some routes need no key, because the URL itself carries a credential a guest received outside your site: GET /api/qr/{token} and the table-session endpoints under it (the token is printed on the table); the event routes GET /api/tickets/{code}, GET /api/registrations/{cancelToken} and POST /api/event-registrations/{cancelToken}/cancel, and the guest's own booking at GET /api/reservations/{cancelToken} and POST /api/reservations/{cancelToken}/cancel (the code and the tokens arrive by e-mail); and the gift voucher lookup GET /api/vouchers/{code} (the code is on the voucher). Eleven in all, each bound to one venue. The QR token is revocable and reprinted from the console when needed; the others are per-registration, per-booking or per-voucher credentials — Events explains why, and Vouchers what that means for a voucher code. Only the ordering routes and the tab payment depend on the venue's plan.

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, or the venue's plan lacks the feature, 404 no such venue, reservation, event, token or session, 409 the request was valid but the world has moved on (slot taken, tab already closed, payments not enabled, event registration closed or sold out), 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, and that will not stay true forever. The one limited route is the keyless voucher lookup, which has no key to count against: 30 lookups a minute per client. Rate limits has the generous numbers to design against anyway, what we do if a client is genuinely far outside them, and the client habits — debouncing, caching, backing off on 5xx — that keep any real integration well inside.

Next steps

  • Venue — the reads every integration starts with.
  • Availability — the slots a guest can book.
  • Errors — every type URL, explained.