Developer docs

Venue

Read a venue's profile, business information, opening hours, menus, bookable resources and floor plan — the six GETs every integration starts with.

Six reads describe a venue completely. They are cheap, cacheable on your side, and change only when the operator edits something in the console. All of them take the venue's slug in the path, need a key (Keys and headers), and answer 404 with a problem body when no venue has that slug.

Endpoints

GET /api/venues/{venueSlug}

The venue's public profile: name, description, location, time zone, which resource types it offers, and — when the operator has set them up — paid extras and the booking durations a guest may choose per resource type.

Response 200 OK

Field Type Meaning
slug string The identifier you used in the URL
name, tagline, description string Display text, in the venue's own language
city, country string Location as shown to guests
timezone string IANA time zone every date and time on this venue is in
resourceTypes string[] The types this venue offers: RestaurantTable, BilliardTable, DartBoard, Shuffleboard, BowlingLane, EventArea
extras object[] or null Paid add-ons: id, name, description, price, currency, basis (PerPerson or PerBooking), appliesTo (resource types)
bookingDurations object[] or null Per type: type, defaultMinutes, minMinutes, maxMinutes, stepMinutes, guestSelectable, options (the selectable minutes, ready for a picker)
{
  "slug": "demo-sportsbar",
  "name": "The Neon Tap",
  "tagline": "Sports bar, kitchen & game house",
  "description": "A neighbourhood sports bar where guests come to dine, drink, watch the match, and play. Reserve a table for the big game, book a billiard table or dart board by the hour, or take the whole mezzanine for a private event.",
  "city": "Berlin",
  "country": "Germany",
  "timezone": "Europe/Berlin",
  "resourceTypes": [
    "RestaurantTable",
    "BilliardTable",
    "DartBoard"
  ],
  "extras": null,
  "bookingDurations": [
    {
      "type": "RestaurantTable",
      "defaultMinutes": 120,
      "minMinutes": 60,
      "maxMinutes": 240,
      "stepMinutes": 30,
      "guestSelectable": false,
      "options": [
        120
      ]
    },
    {
      "type": "BilliardTable",
      "defaultMinutes": 60,
      "minMinutes": 60,
      "maxMinutes": 240,
      "stepMinutes": 30,
      "guestSelectable": true,
      "options": [
        60,
        90,
        120
      ]
    },
    {
      "type": "DartBoard",
      "defaultMinutes": 60,
      "minMinutes": 60,
      "maxMinutes": 240,
      "stepMinutes": 30,
      "guestSelectable": true,
      "options": [
        60,
        90,
        120
      ]
    }
  ]
}

Lists in the example are shortened to three entries.

Errors404 no venue with that slug; key problems as on Errors.

curl

curl "https://api.bookdineplay.com/api/venues/your-venue" \
  -H "X-BookDinePlay-Key: bdp_pk_your_publishable_key" \
  -H "Origin: https://www.your-venue.example"

JavaScript

const response = await fetch('https://api.bookdineplay.com/api/venues/your-venue', {
  headers: { 'X-BookDinePlay-Key': 'bdp_pk_your_publishable_key' }
});
const venue = await response.json();
console.log(venue.name, venue.resourceTypes);

C#

var venue = await client.GetVenueAsync("your-venue", cancellationToken);
if (venue is null) { /* no venue with that slug */ }

GET /api/venues/{venueSlug}/business-info

Legal name, postal address and contact details — what an imprint, a map link or a confirmation e-mail needs.

Response 200 OK

Field Type Meaning
slug string The venue
legalName string The operating company as it appears on receipts
addressLine1, addressLine2, city, postalCode, country string (addressLine2 nullable) Postal address
phone, email, website string (website nullable) Contact details the venue publishes
timezone string Same IANA zone as on the profile
{
  "slug": "demo-sportsbar",
  "legalName": "Neon Tap Hospitality GmbH",
  "addressLine1": "Boxhagener Straße 42",
  "addressLine2": null,
  "city": "Berlin",
  "postalCode": "10245",
  "country": "Germany",
  "phone": "+49 30 5555 0142",
  "email": "hey@theneontap.example",
  "website": "https://theneontap.example",
  "timezone": "Europe/Berlin"
}

Errors404 no venue with that slug.

curl

curl "https://api.bookdineplay.com/api/venues/your-venue/business-info" \
  -H "X-BookDinePlay-Key: bdp_pk_your_publishable_key" \
  -H "Origin: https://www.your-venue.example"

JavaScript

const info = await (await fetch('https://api.bookdineplay.com/api/venues/your-venue/business-info', {
  headers: { 'X-BookDinePlay-Key': 'bdp_pk_your_publishable_key' }
})).json();
console.log(`${info.legalName}, ${info.addressLine1}, ${info.postalCode} ${info.city}`);

C#

var info = await client.GetBusinessInfoAsync("your-venue", cancellationToken);

GET /api/venues/{venueSlug}/opening-hours

The weekly schedule plus date-specific overrides (holidays, events, early closing). Availability already applies these; read them to display hours, not to compute slots.

Response 200 OK

Field Type Meaning
slug, timezone string The venue and the zone the times are in
regular object[] One entry per weekday: day (MondaySunday), isClosed, opens, closes (HH:mm, null when closed)
special object[] Date overrides: date (yyyy-MM-dd), isClosed, opens, closes, note — an entry wins over the weekday for its date

Hours that cross midnight (opens 18:00, closes 02:00) belong to the day they start on.

{
  "slug": "demo-sportsbar",
  "timezone": "Europe/Berlin",
  "regular": [
    {
      "day": "Monday",
      "isClosed": true,
      "opens": null,
      "closes": null
    },
    {
      "day": "Tuesday",
      "isClosed": false,
      "opens": "16:00",
      "closes": "23:00"
    },
    {
      "day": "Wednesday",
      "isClosed": false,
      "opens": "16:00",
      "closes": "23:00"
    }
  ],
  "special": [
    {
      "date": "2026-12-24",
      "isClosed": true,
      "opens": null,
      "closes": null,
      "note": "Christmas Eve — closed"
    },
    {
      "date": "2026-12-31",
      "isClosed": false,
      "opens": "18:00",
      "closes": "02:00",
      "note": "New Year's Eve — late night"
    }
  ]
}

Errors404 no venue with that slug.

curl

curl "https://api.bookdineplay.com/api/venues/your-venue/opening-hours" \
  -H "X-BookDinePlay-Key: bdp_pk_your_publishable_key" \
  -H "Origin: https://www.your-venue.example"

JavaScript

const hours = await (await fetch('https://api.bookdineplay.com/api/venues/your-venue/opening-hours', {
  headers: { 'X-BookDinePlay-Key': 'bdp_pk_your_publishable_key' }
})).json();
for (const day of hours.regular) {
  console.log(day.day, day.isClosed ? 'closed' : `${day.opens}–${day.closes}`);
}

C#

var hours = await client.GetOpeningHoursAsync("your-venue", cancellationToken);

GET /api/venues/{venueSlug}/menus

Every published menu with its groups and items, priced in the venue's currency. This is the display menu; what a seated guest can order from is the table session's menus (QR codes and table sessions).

Response 200 OK

Field Type Meaning
slug, currency string The venue and the ISO 4217 currency of every price below
menus[] object[] id, name, description, groups[]
groups[] object[] name, description (nullable), items[]
items[] object[] name, description, price (decimal), currency, tags (string[], e.g. dietary labels), imageUrl (nullable)
{
  "slug": "demo-sportsbar",
  "currency": "EUR",
  "menus": [
    {
      "id": "kitchen",
      "name": "Kitchen",
      "description": "Served from open until one hour before close.",
      "groups": [
        {
          "name": "Small plates",
          "description": null,
          "items": [
            {
              "name": "Loaded nachos",
              "description": "Melted cheese, jalapeños, lime crema",
              "price": 9.50,
              "currency": "EUR",
              "tags": [
                "vegetarian"
              ],
              "imageUrl": "_content/BookDinePlay.App.Shared/menu/nachos.webp"
            },
            {
              "name": "Buffalo wings",
              "description": "Blue-cheese dip, celery",
              "price": 11.00,
              "currency": "EUR",
              "tags": [],
              "imageUrl": "_content/BookDinePlay.App.Shared/menu/wings.webp"
            },
            {
              "name": "Padrón peppers",
              "description": "Sea salt, olive oil",
              "price": 7.00,
              "currency": "EUR",
              "tags": [
                "vegan",
                "gluten-free"
              ],
              "imageUrl": "_content/BookDinePlay.App.Shared/menu/padron-peppers.webp"
            }
          ]
        },
        {
          "name": "Mains",
          "description": null,
          "items": [
            {
              "name": "Smash burger",
              "description": "Double patty, house sauce, fries",
              "price": 14.50,
              "currency": "EUR",
              "tags": [],
              "imageUrl": "_content/BookDinePlay.App.Shared/menu/smash-burger.webp"
            },
            {
              "name": "BBQ ribs",
              "description": "Slow-cooked, slaw, fries",
              "price": 19.00,
              "currency": "EUR",
              "tags": [],
              "imageUrl": "_content/BookDinePlay.App.Shared/menu/bbq-ribs.webp"
            },
            {
              "name": "Halloumi flatbread",
              "description": "Grilled halloumi, harissa, greens",
              "price": 13.00,
              "currency": "EUR",
              "tags": [
                "vegetarian"
              ],
              "imageUrl": "_content/BookDinePlay.App.Shared/menu/halloumi-flatbread.webp"
            }
          ]
        },
        {
          "name": "Sweet",
          "description": null,
          "items": [
            {
              "name": "Churros",
              "description": "Cinnamon sugar, chocolate",
              "price": 7.00,
              "currency": "EUR",
              "tags": [
                "vegetarian"
              ],
              "imageUrl": "_content/BookDinePlay.App.Shared/menu/churros.webp"
            }
          ]
        }
      ]
    },
    {
      "id": "bar",
      "name": "Bar",
      "description": "Draft, cocktails and zero-proof.",
      "groups": [
        {
          "name": "Draft beer",
          "description": null,
          "items": [
            {
              "name": "House lager 0.3L",
              "description": "Crisp pilsner",
              "price": 4.50,
              "currency": "EUR",
              "tags": [],
              "imageUrl": "_content/BookDinePlay.App.Shared/menu/lager.webp"
            },
            {
              "name": "Local IPA 0.5L",
              "description": "Hoppy, citrus finish",
              "price": 6.50,
              "currency": "EUR",
              "tags": [],
              "imageUrl": "_content/BookDinePlay.App.Shared/menu/ipa.webp"
            }
          ]
        },
        {
          "name": "Cocktails",
          "description": null,
          "items": [
            {
              "name": "Old Fashioned",
              "description": "Bourbon, bitters, orange",
              "price": 11.00,
              "currency": "EUR",
              "tags": [],
              "imageUrl": "_content/BookDinePlay.App.Shared/menu/old-fashioned.webp"
            },
            {
              "name": "Spicy margarita",
              "description": "Tequila, chilli, lime",
              "price": 10.50,
              "currency": "EUR",
              "tags": [],
              "imageUrl": "_content/BookDinePlay.App.Shared/menu/margarita.webp"
            }
          ]
        },
        {
          "name": "Zero proof",
          "description": null,
          "items": [
            {
              "name": "Craft soda",
              "description": "House-made, ask for today's flavour",
              "price": 4.00,
              "currency": "EUR",
              "tags": [
                "vegan"
              ],
              "imageUrl": "_content/BookDinePlay.App.Shared/menu/craft-soda.webp"
            }
          ]
        }
      ]
    }
  ]
}

Errors404 no venue with that slug.

curl

curl "https://api.bookdineplay.com/api/venues/your-venue/menus" \
  -H "X-BookDinePlay-Key: bdp_pk_your_publishable_key" \
  -H "Origin: https://www.your-venue.example"

JavaScript

const { menus, currency } = await (await fetch('https://api.bookdineplay.com/api/venues/your-venue/menus', {
  headers: { 'X-BookDinePlay-Key': 'bdp_pk_your_publishable_key' }
})).json();
for (const menu of menus) for (const group of menu.groups) for (const item of group.items) {
  console.log(menu.name, group.name, item.name, item.price, currency);
}

C#

var menus = await client.GetMenusAsync("your-venue", cancellationToken);

GET /api/venues/{venueSlug}/resources

Every bookable resource — tables, billiard tables, dart boards, lanes, event areas — with capacity, the booking length that applies to it, and how it is priced.

Response 200 OK

Field Type Meaning
slug string The venue
resources[] object[] One per resource
id, name, description string id is what resourceId means everywhere else
type string One of the resource types
capacity integer Seats or players
slotDurationMinutes integer The effective booking length: the resource's own override when set, otherwise the venue's default for the type
slotDurationOverrideMinutes integer or null The override alone, null when the resource inherits
pricing object model (Free, PerHour, PerGame, PerSession, FixedFee, Deposit, MinimumSpend), amount, currency, depositAmount (nullable), minimumSpend (nullable), unitLabel (e.g. "per hour")
allowsFoodOrdering boolean Whether a seated guest can order from the table session
{
  "slug": "demo-sportsbar",
  "resources": [
    {
      "id": "table-2a",
      "name": "Window table",
      "type": "RestaurantTable",
      "capacity": 2,
      "description": "Seats up to 2. Reserved free of charge.",
      "slotDurationMinutes": 120,
      "pricing": {
        "model": "Free",
        "amount": 0,
        "currency": "EUR",
        "depositAmount": null,
        "minimumSpend": null,
        "unitLabel": "booking"
      },
      "allowsFoodOrdering": true,
      "slotDurationOverrideMinutes": null
    },
    {
      "id": "table-4a",
      "name": "Table 4",
      "type": "RestaurantTable",
      "capacity": 4,
      "description": "Seats up to 4. Reserved free of charge.",
      "slotDurationMinutes": 120,
      "pricing": {
        "model": "Free",
        "amount": 0,
        "currency": "EUR",
        "depositAmount": null,
        "minimumSpend": null,
        "unitLabel": "booking"
      },
      "allowsFoodOrdering": true,
      "slotDurationOverrideMinutes": null
    },
    {
      "id": "table-4b",
      "name": "Table 7",
      "type": "RestaurantTable",
      "capacity": 4,
      "description": "Seats up to 4. Reserved free of charge.",
      "slotDurationMinutes": 120,
      "pricing": {
        "model": "Free",
        "amount": 0,
        "currency": "EUR",
        "depositAmount": null,
        "minimumSpend": null,
        "unitLabel": "booking"
      },
      "allowsFoodOrdering": true,
      "slotDurationOverrideMinutes": null
    }
  ]
}

Errors404 no venue with that slug.

curl

curl "https://api.bookdineplay.com/api/venues/your-venue/resources" \
  -H "X-BookDinePlay-Key: bdp_pk_your_publishable_key" \
  -H "Origin: https://www.your-venue.example"

JavaScript

const { resources } = await (await fetch('https://api.bookdineplay.com/api/venues/your-venue/resources', {
  headers: { 'X-BookDinePlay-Key': 'bdp_pk_your_publishable_key' }
})).json();
const lanes = resources.filter(r => r.type === 'BowlingLane');

C#

var resources = await client.GetResourcesAsync("your-venue", cancellationToken);

GET /api/venues/{venueSlug}/floor-plan

Zones and placed resources with normalized geometry, for drawing a "pick your table" view. A venue without a plan answers 200 with empty lists — check tables.length before switching your UI to the floor plan.

Response 200 OK

Field Type Meaning
slug string The venue
zones[] object[] id (GUID), name, ordinal (display order)
tables[] object[] resourceId, name, type, capacity, zoneId, x, y, width, height (all 0–1, relative to the zone), shape (Rectangle or Circle), rotation (degrees)
{
  "slug": "demo-sportsbar",
  "zones": [],
  "tables": []
}

Errors404 no venue with that slug.

curl

curl "https://api.bookdineplay.com/api/venues/your-venue/floor-plan" \
  -H "X-BookDinePlay-Key: bdp_pk_your_publishable_key" \
  -H "Origin: https://www.your-venue.example"

JavaScript

const plan = await (await fetch('https://api.bookdineplay.com/api/venues/your-venue/floor-plan', {
  headers: { 'X-BookDinePlay-Key': 'bdp_pk_your_publishable_key' }
})).json();
const hasPlan = plan.tables.length > 0;

C#

var plan = await client.GetFloorPlanAsync("your-venue", cancellationToken);

Caching

None of these responses carries a cache header, because the operator can change them at any moment. A sensible integration caches them for minutes, not days, and refetches on a 404 or a schema it does not recognise. The widget itself reads the venue profile and floor plan once per page load and never caches across loads.

Next steps

  • Availability — turn a resource type and a date into bookable slots.
  • .NET SDK — the same six reads as typed methods.