Developer docs

Reservations

Create a reservation for a slot the availability call offered — request fields, the reservation you get back, deposits, group bookings and every refusal.

One call books a slot. Send the resource type, date and start the guest chose from Availability, the party size and the guest's contact details; you get back a reservation with a reference the guest can quote, a status, and the price and deposit that apply.

Endpoints

POST /api/venues/{venueSlug}/reservations

Request headers

Header Required Meaning
Idempotency-Key no An opaque token you choose (up to 200 characters), scoped to this venue AND to your BookDinePlay account, and honoured for 24 hours. Any API key of your account can replay a key another of your keys created, so rotating a key mid-flight is safe. All of your own keys deliberately share one namespace — they are one account — so keep minting a key per booking attempt rather than relying on separation between two of your own integrations; what is separate is another account's keys, and bookings taken for your venue through BookDinePlay's own hosted pages, which can neither replay nor block yours. A repeat with the SAME key and the SAME request body returns the ORIGINAL reservation — same reference, same cancelToken — instead of creating a second one. Once a key has produced a reservation this way, reusing it with a DIFFERENT body is refused with idempotency-key-reused, never silently replayed or silently allowed through. A key whose only attempt so far FAILED (validation, no slot free) has nothing stored under it yet — a corrected retry under that same key is simply a normal new attempt, not a mismatch. Omit the header and nothing changes: every call creates a new reservation, exactly as it always has.

Request body (Content-Type: application/json)

Field Required Meaning
resourceType yes The type of the slot, e.g. RestaurantTable
resourceId no The slot's resourceId to get exactly that resource. Omitted: the first available resource of the type at that time
date yes Venue-local yyyy-MM-dd
start yes Venue-local HH:mm, a start the availability call offered
end no Venue-local HH:mm; defaults to start plus the booking length
durationMinutes no 15–1440. Send the same value you queried availability with, or omit for the venue's default
partySize yes 1–200
games no 1–10, for resources priced per game; defaults to 1
customerName yes 2–120 characters
email yes A valid e-mail address — the confirmation goes there
phone no 3–40 characters when supplied; a blank value counts as not supplied
notes no Up to 500 characters, shown to the venue
depositOptIn no true when the guest agrees to pay a deposit; only has an effect when the venue enables deposits
extras no [{ "extraId": "…", "quantity": 1 }] — extras from the venue profile that apply to this resource type, quantity 1–100

Response 201 Created — no Location header. The token in the body is a credential (see cancelToken below); putting it in a header would put it in access logs, proxy logs and Referer chains, so nothing about the reservation is derivable from the response headers.

Field Type Meaning
id GUID Internal id
reference string BDP-XXXXXX — what the guest quotes and what a payment intent refers to. Treat it as an opaque token: bookings made before references grew to six characters carry four (BDP-XXXX) and stay valid
status string Confirmed, or Pending when a deposit is required and not yet paid; later Cancelled, Completed, NoShow
venueSlug, resourceType, resourceName string Where
date, start, end string When, venue-local
partySize, customerName Who
priceEstimate, currency decimal, string The booking's price under the resource's model
depositRequired, depositAmount boolean, decimal or null Whether the guest must pay a deposit to confirm, and how much
createdAt ISO 8601 instant When the reservation was made
groupReference, groupResources string or null, object[] or null For a group booking: the shared reference and each member (resourceId, resourceName, partyShare)
games integer or null For per-game pricing
extras, extrasTotal object[] or null, decimal or null Priced extra lines (extraId, name, unitPrice, quantity, lineTotal, currency, basis) and their sum
cancelToken string or null The guest's own credential for GET /api/reservations/{cancelToken} and the cancel route — keep it out of logs. Shared by every member of a group booking. Null only for a reservation created before this field existed
{
  "id": "cad35696-0a75-45b6-b986-8bed7eb00ac5",
  "reference": "BDP-KSQS7M",
  "status": "Confirmed",
  "venueSlug": "demo-sportsbar",
  "resourceType": "RestaurantTable",
  "resourceName": "Corner table",
  "date": "2026-10-16",
  "start": "15:00",
  "end": "17:00",
  "partySize": 4,
  "customerName": "Ada Lovelace",
  "priceEstimate": 0,
  "currency": "EUR",
  "depositRequired": false,
  "depositAmount": null,
  "createdAt": "2026-09-15T12:55:15.1526956+00:00",
  "groupReference": null,
  "groupResources": null,
  "games": null,
  "extras": null,
  "extrasTotal": null,
  "cancelToken": "dqN_AejenL2r7qXPdZuOxnLY9_BfVkTl"
}

Errors

Status When
400 A field fails validation (errors names it); date/start malformed; the venue is closed then; the duration is not one the venue offers for that type; an extra does not apply
404 No venue with that slug
409 No resource of that type is free at that time — it was taken since the availability call. Re-query and offer the next slot
409 An Idempotency-Key you sent was already used for a request with a different body — see idempotency-key-reused

curl

curl -X POST "https://api.bookdineplay.com/api/venues/your-venue/reservations" \
  -H "X-BookDinePlay-Key: bdp_pk_your_publishable_key" \
  -H "Origin: https://www.your-venue.example" \
  -H "Content-Type: application/json" \
  -d '{
    "resourceType": "RestaurantTable",
    "date": "2026-10-16",
    "start": "19:00",
    "partySize": 4,
    "customerName": "Ada Lovelace",
    "email": "ada@example.com",
    "phone": "+49 30 1234567",
    "notes": "Window seat if possible"
  }'

JavaScript

const response = await fetch('https://api.bookdineplay.com/api/venues/your-venue/reservations', {
  method: 'POST',
  headers: { 'X-BookDinePlay-Key': 'bdp_pk_your_publishable_key', 'Content-Type': 'application/json' },
  body: JSON.stringify({
    resourceType: 'RestaurantTable', date: '2026-10-16', start: '19:00', partySize: 4,
    customerName: 'Ada Lovelace', email: 'ada@example.com', phone: '+49 30 1234567',
    notes: 'Window seat if possible'
  })
});
if (response.status === 409) { /* slot just taken — re-query availability */ }
const reservation = await response.json();
console.log(reservation.reference, reservation.status);

C#

try
{
    var reservation = await client.CreateReservationAsync("your-venue", new CreateReservationRequest
    {
        ResourceType = BookableResourceType.RestaurantTable,
        Date = "2026-10-16",
        Start = "19:00",
        PartySize = 4,
        CustomerName = "Ada Lovelace",
        Email = "ada@example.com",
        Phone = "+49 30 1234567",
        Notes = "Window seat if possible"
    }, cancellationToken);
    Console.WriteLine($"{reservation.Reference} {reservation.Status}");
}
catch (BookDinePlayApiException ex) when (ex.StatusCode == HttpStatusCode.Conflict)
{
    // slot just taken — re-query availability
}

Retrying safely with Idempotency-Key — mint the key once per booking attempt and reuse it for every retry of that SAME attempt:

curl

key="resv-$(uuidgen)"
curl -X POST "https://api.bookdineplay.com/api/venues/your-venue/reservations" \
  -H "X-BookDinePlay-Key: bdp_pk_your_publishable_key" \
  -H "Origin: https://www.your-venue.example" \
  -H "Idempotency-Key: $key" \
  -H "Content-Type: application/json" \
  -d '{ "resourceType": "RestaurantTable", "date": "2026-10-16", "start": "19:00", "partySize": 4, "customerName": "Ada Lovelace", "email": "ada@example.com" }'
# On a timeout or 502/504, retry the exact same request with the SAME $key.

JavaScript

const idempotencyKey = crypto.randomUUID();
async function reserve() {
  const response = await fetch('https://api.bookdineplay.com/api/venues/your-venue/reservations', {
    method: 'POST',
    headers: {
      'X-BookDinePlay-Key': 'bdp_pk_your_publishable_key',
      'Content-Type': 'application/json',
      'Idempotency-Key': idempotencyKey
    },
    body: JSON.stringify({
      resourceType: 'RestaurantTable', date: '2026-10-16', start: '19:00', partySize: 4,
      customerName: 'Ada Lovelace', email: 'ada@example.com'
    })
  });
  return response.json();
}
// Call reserve() again on a timeout/502/504 — same idempotencyKey each time.

C#

var idempotencyKey = Guid.NewGuid().ToString();
var reservation = await client.CreateReservationAsync("your-venue", new CreateReservationRequest
{
    ResourceType = BookableResourceType.RestaurantTable,
    Date = "2026-10-16",
    Start = "19:00",
    PartySize = 4,
    CustomerName = "Ada Lovelace",
    Email = "ada@example.com"
}, idempotencyKey);
// A retry after a timeout/502/504 passes the SAME idempotencyKey and gets back the SAME reservation.

GET /api/reservations/{cancelToken}

A guest's own booking summary by their cancel token: what, when, where, party size, status, and whether the guest's own cancel button should be shown right now. No key: the token in the URL is itself the credential the confirmation e-mail carried, the same reasoning as the event registration/ticket routes. Not plan-gated — the guest booked while the venue's plan allowed it. A group booking (several resources sharing one token) is reported as a single summary: partySize is the whole party and groupResources lists every member.

Response 200 OK

Field Type Meaning
reference string The booking's own reference (a group booking's first member)
groupReference string or null The shared reference guests are shown for a group booking
venueSlug, venueName string Where
status string Confirmed, Pending, Cancelled, Completed or NoShow
resourceType, resourceName string The (first) resource
date, start, end string When, venue-local
partySize integer The whole party across every group member
groupResources object[] or null For a group booking: each member (resourceId, resourceName, partyShare)
canCancel boolean Whether the guest's own cancel button should work right now
cancelNote string or null Why not, when canCancel is false: already cancelled, resolved by the venue (a no-show or completed visit), a paid deposit with no online refund path yet, or the venue's cancellation policy no longer allows it (switched off, or past its cut-off)
{
  "reference": "BDP-KSQS7M",
  "groupReference": null,
  "venueSlug": "demo-sportsbar",
  "venueName": "The Neon Tap",
  "status": "Confirmed",
  "resourceType": "RestaurantTable",
  "resourceName": "Corner table",
  "date": "2026-10-16",
  "start": "15:00",
  "end": "17:00",
  "partySize": 4,
  "groupResources": null,
  "canCancel": true,
  "cancelNote": null
}

Errors — 404 for an unknown or malformed token.

curl

curl "https://api.bookdineplay.com/api/reservations/dqN_AejenL2r7qXPdZuOxnLY9_BfVkTl"

JavaScript

const booking = await (await fetch('https://api.bookdineplay.com/api/reservations/' + cancelToken)).json(); // sends no key

C#

using var response = await http.GetAsync($"api/reservations/{cancelToken}", cancellationToken); // sends no key

POST /api/reservations/{cancelToken}/cancel

Cancels the booking behind a cancel link and frees its slot immediately — every member of a group booking at once, since they share one token. No key: the token is the credential. Idempotent — a second call after a successful cancel answers 200 again. Not plan-gated. No request body.

Response 200 OK — reference, groupReference, status (Cancelled).

{
  "reference": "BDP-KSQS7M",
  "groupReference": null,
  "status": "Cancelled"
}

Errors — 404 for an unknown token; 409 reservation-cancellation-not-allowed once the venue's cancellation policy no longer allows an online cancel (switched off entirely, or past its cut-off — the operator controls both); 409 reservation-deposit-not-refundable for a booking whose deposit was paid online at a venue that has chosen to sort deposits out by phone rather than through a cancel link, leaving the booking untouched; and 502 reservation-refund-failed at a venue that DOES refund, when the refund itself did not go through — also leaving the booking untouched, but worth retrying rather than explaining away. Check canCancel on GET /api/reservations/{cancelToken} first to avoid the round trip either way.

curl

curl -X POST "https://api.bookdineplay.com/api/reservations/<cancelToken>/cancel"

JavaScript

const result = await client.cancelReservation(cancelToken); // sends no key

C#

var result = await client.CancelReservationAsync(cancelToken, cancellationToken); // sends no key; null when unknown

Deposits

A reservation needs a deposit when three things are true: the guest sent depositOptIn: true, the venue has deposits enabled, and the resource (or the venue's default for game resources) carries a deposit amount. Then depositRequired is true, depositAmount says how much, and status is Pending until the venue confirms the booking. Create a payment intent for that amount next. Without a deposit the reservation is Confirmed immediately.

Group bookings

When partySize exceeds one resource's capacity and the availability call offered a group slot, the reservation spans several resources: groupReference is the shared reference and groupResources lists each member with its share of the party. Extras are charged once for the whole group.

Next steps