Entwicklerdokumentation

Events

Veröffentlichte Events auflisten, eines mit Anmeldefenster lesen, sich mit namentlichen Tickets anmelden – plus die schlüssellosen Ticket- und Storno-Routen.

Eine Location auf Pro oder höher veröffentlicht ihre Events – ein Dart-Ligaabend, eine Übertragung, ein wöchentliches Quiz – und Gäste melden sich über dieselbe API an, die auch die Widgets nutzen. Eine Anmeldung erzeugt ein namentliches Ticket pro Teilnehmer; jedes Ticket hat einen Code und eine Seite mit dem Code, den die Location am Einlass eincheckt. Liste, Detail und Anmeldung folgen denselben Schlüssel- und CORS-Regeln wie der Rest von /api/venues/**. Die letzten beiden Routen auf dieser Seite brauchen gar keinen Schlüssel: Der Ticketcode und das Storno-Token in der URL sind die Berechtigung, die der Gast per E-Mail erhalten hat – genau wie ein gedrucktes QR-Token unter QR-Codes und Tischsitzungen.

Events werden in der Konsole unter app.bookdineplay.com angelegt, veröffentlicht und abgesagt; eine öffentliche Schreibroute gibt es nicht. Das JavaScript-SDK rendert den ganzen Ablauf als Widget oder gibt Ihnen die Aufrufe für Ihr eigenes Markup; das .NET-SDK kapselt dieselben fünf Aufrufe.

Tarife und Sichtbarkeit

Situation Liste Detail Anmeldung
Veröffentlichtes Event, Tarif enthält Events gelistet 200 201, oder 409, wenn das Fenster geschlossen ist
Abgesagtes Event nicht gelistet 200 mit status: "Cancelled" und registration.open: false 409 registration-closed, reason: "not-open"
Entwurf oder unbekannter Slug nicht gelistet 404 404
Tarif der Location enthält keine Events (Starter) 200 mit leerem events-Array 404 403 feature-not-in-plan mit requiredPlan: "Pro"
Unbekannte Location 404 404 404

Ein Widget auf einer Starter-Location zeigt also einen leeren Zustand statt eines Fehlers, mit dem der Gast nichts anfangen kann. Die Ticket- und Storno-Routen sind nicht tarifgebunden: Ein Gast, der sich angemeldet hat, während der Tarif es erlaubte, behält seine Ticketseite und seinen Storno-Link auch nach einer Herabstufung.

Das Anmeldefenster

Jedes Event-Detail trägt ein Objekt registration, das sagt, ob eine Anmeldung jetzt angenommen würde – und wenn nicht, warum:

Feld Typ Bedeutung
mode string None (nur Information, nichts anzumelden), Free (namentliche Tickets, keine Zahlung) oder Paid (Tickets mit Preis – Premium)
price, currency decimal, string Ticketpreis eines bezahlten Events, z. B. 12.5 und EUR; sonst beide null
capacity integer oder null Maximale Ticketzahl; null bei unbegrenzt
remaining integer oder null Noch verfügbare Tickets; null bei unbegrenzt. Sinkt mit jeder Anmeldung und steigt bei einer Stornierung wieder
maxPerRegistration integer Tickets, die eine Anmeldung umfassen darf (Einstellung der Location, Standard 6). Fragen Sie höchstens min(maxPerRegistration, remaining) an
closesAt ISO-8601-Zeitpunkt oder null Wann die Location die Anmeldung vorzeitig schließt; null, wenn sie bis zum Beginn offen bleibt
open boolean true, wenn eine jetzt gesendete Anmeldung angenommen würde
reason string oder null Warum open false ist: closed (nach closesAt), sold-out (remaining ist 0), past (das Event hat begonnen), not-open (Modus None oder das Event wurde abgesagt). Null, solange offen

Dieselben vier Gründe kommen im Problem registration-closed zurück, wenn eine Anmeldung abgelehnt wird – das Fenster kann sich zwischen dem Lesen des Details und der Anmeldung schließen, behandeln Sie also beides.

Endpunkte

GET /api/venues/{venueSlug}/events

Veröffentlichte Events in Listenreihenfolge: datierte Events aufsteigend nach Datum und Beginn, laufende Events (ohne date) danach. Entwürfe und abgesagte Events erscheinen nie in der Liste. Eine Location, deren Tarif keine Events enthält, liefert eine leere Liste, keinen Fehler.

Queryupcoming (Standard true) lässt datierte Events weg, deren Tag in der Zeitzone der Location vorbei ist; upcoming=false liefert jedes veröffentlichte Event, vergangene eingeschlossen.

Antwort 200 OKevents[], jeweils:

Feld Typ Bedeutung
slug, title, excerpt string Identität und Teaser (excerpt nullable)
imageUrl, imageAlt string oder null Das Eventbild und sein Alt-Text
status string Hier Published; Cancelled beim Detail
date string oder null Lokales yyyy-MM-dd; null bei einem laufenden Event
start, end string oder null Lokales HH:mm
timeLabel string oder null Freitext, den die Location neben oder statt der Zeiten zeigt (Doors 19:30, Jeden Donnerstag ab 20:00)
locationLabel string Wo es stattfindet – das Label der Location oder ihr Name und ihre Adresse
registrationMode string None, Free oder Paid
language string en oder de – die Sprache, in der das Event geschrieben ist
{
  "events": [
    {
      "slug": "darts-league-night",
      "title": "Darts league night",
      "excerpt": "Eight boards, one winner — sign up your team.",
      "imageUrl": null,
      "imageAlt": null,
      "status": "Published",
      "date": "2027-10-16",
      "start": "20:00",
      "end": "23:00",
      "timeLabel": "Doors 19:30",
      "locationLabel": "The Neon Tap, Boxhagener Straße 42, 10245 Berlin",
      "registrationMode": "Free",
      "language": "en"
    },
    {
      "slug": "summer-cup-final",
      "title": "Summer cup final",
      "excerpt": "Watch the final on the big screens.",
      "imageUrl": null,
      "imageAlt": null,
      "status": "Published",
      "date": "2027-10-23",
      "start": "18:00",
      "end": null,
      "timeLabel": null,
      "locationLabel": "The Neon Tap, Boxhagener Straße 42, 10245 Berlin",
      "registrationMode": "Free",
      "language": "en"
    },
    {
      "slug": "pub-quiz",
      "title": "Pub quiz",
      "excerpt": "Six rounds, free entry, prizes for the top three tables.",
      "imageUrl": null,
      "imageAlt": null,
      "status": "Published",
      "date": null,
      "start": null,
      "end": null,
      "timeLabel": "Every Thursday from 20:00",
      "locationLabel": "The Neon Tap, Boxhagener Straße 42, 10245 Berlin",
      "registrationMode": "None",
      "language": "en"
    }
  ]
}

Fehler404 keine Location mit diesem Slug.

curl

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

JavaScript

const client = BookDinePlay.createClient({
  apiBaseUrl: 'https://api.bookdineplay.com', venueSlug: 'your-venue', publishableKey: 'bdp_pk_your_publishable_key'
});
const events = await client.events();                 // das Array, nur anstehende
const all = await client.events({ upcoming: false }); // auch vergangene datierte Events

C#

var events = await client.GetEventsAsync("your-venue", cancellationToken: cancellationToken);
var all = await client.GetEventsAsync("your-venue", upcoming: false, cancellationToken);

GET /api/venues/{venueSlug}/events/{eventSlug}

Ein Event mit Beschreibung und Anmeldefenster. Ein abgesagtes Event antwortet weiterhin mit 200 und status: "Cancelled", damit ein gespeicherter Link sich selbst erklärt; ein Entwurf oder ein unbekannter Slug ist 404.

Antwort 200 OK – die Listenfelder plus venueSlug, venueName, descriptionHtml und registration (Das Anmeldefenster). descriptionHtml wird aus dem Markdown der Location gerendert und serverseitig gegen eine strikte Allowlist gefiltert – Absätze, Überschriften, Hervorhebungen, Listen, Zitate, Code, Links (rel="noopener nofollow") und https-Bilder. Allowlisted Inline-HTML (<strong>, <a> …), das die Location in ihrem Markdown geschrieben hat, übersteht den Filter; nichts außerhalb dieser Allowlist bleibt erhalten – keine Scripts, Styles, Event-Handler oder rohes HTML darüber hinaus – und kann daher gefahrlos als HTML in Ihre Seite eingefügt werden.

{
  "venueSlug": "demo-sportsbar",
  "venueName": "The Neon Tap",
  "slug": "darts-league-night",
  "title": "Darts league night",
  "excerpt": "Eight boards, one winner — sign up your team.",
  "descriptionHtml": "<p>Bring your <strong>own</strong> darts or borrow a set at the bar. Registration closes at the door.</p>\n",
  "imageUrl": null,
  "imageAlt": null,
  "status": "Published",
  "date": "2027-10-16",
  "start": "20:00",
  "end": "23:00",
  "timeLabel": "Doors 19:30",
  "locationLabel": "The Neon Tap, Boxhagener Straße 42, 10245 Berlin",
  "language": "en",
  "registration": {
    "mode": "Free",
    "price": null,
    "currency": null,
    "capacity": 64,
    "remaining": 64,
    "maxPerRegistration": 6,
    "closesAt": null,
    "open": true,
    "reason": null
  }
}

Fehler404 keine solche Location oder kein veröffentlichtes Event mit diesem Slug (ein Entwurf, ein unbekannter Slug oder eine Location, deren Tarif keine Events enthält).

curl

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

JavaScript

const event = await client.event('darts-league-night'); // rejects with status 404 when unknown
if (!event.registration.open) console.log(event.registration.reason);

C#

var detail = await client.GetEventAsync("your-venue", "darts-league-night", cancellationToken); // null when unknown

POST /api/venues/{venueSlug}/events/{eventSlug}/registrations

Anmeldung zu einem kostenlosen Event. Es werden quantity Tickets erzeugt, eines pro Teilnehmername in attendees (der erste darf leer sein – er fällt auf den Käufer zurück). Der Gast erhält eine Bestätigungs-E-Mail mit den Tickets und einem Storno-Link (Was der Gast erhält).

Request-Body (Content-Type: application/json)

Feld Pflicht Bedeutung
buyerName ja 1–120 Zeichen – die Person, die sich anmeldet; zugleich der erste Teilnehmer, wenn attendees[0] leer ist
email ja Eine gültige E-Mail-Adresse – dorthin gehen die Tickets
phone nein Bis zu 40 Zeichen; ein leerer Wert gilt als nicht angegeben
notes nein Bis zu 1000 Zeichen, für die Location sichtbar
quantity ja 1–100, und höchstens registration.maxPerRegistration bzw. registration.remaining
attendees nein Ein Name pro Ticket in Ticketreihenfolge, je bis zu 120 Zeichen. Eintrag 0 darf leer sein (dann gilt der Name des Käufers); jedes weitere Ticket braucht einen Namen. Mehr Namen als quantity sind ein Fehler

Antwort 201 Created mit einem Location-Header auf die Detail-URL des Events.

Feld Typ Bedeutung
reference string BDP-E-XXXX – was der Gast angibt
status string Confirmed; Cancelled, nachdem der Gast oder die Location storniert hat
payment string NotRequired bei einem kostenlosen Event (Pending, Paid, Refunded sind bezahlten Tickets vorbehalten)
eventSlug, eventTitle string Welches Event
buyerName, email, quantity Was gesendet wurde, normalisiert
tickets[] object[] Eines pro Teilnehmer: code (12 Zeichen), attendeeName, ticketUrl – die Ticketseite mit dem QR-Code
cancelUrl string Die Storno-Seite des Gastes; das letzte Pfadsegment ist das Storno-Token
{
  "reference": "BDP-E-ESJA",
  "status": "Confirmed",
  "payment": "NotRequired",
  "eventSlug": "darts-league-night",
  "eventTitle": "Darts league night",
  "buyerName": "Ada Lovelace",
  "email": "ada@example.com",
  "quantity": 2,
  "tickets": [
    {
      "code": "X7QZUP5AC3GH",
      "attendeeName": "Ada Lovelace",
      "ticketUrl": "https://app.bookdineplay.com/ticket/X7QZUP5AC3GH"
    },
    {
      "code": "DR9HN4GDX9ZH",
      "attendeeName": "Grace Hopper",
      "ticketUrl": "https://app.bookdineplay.com/ticket/DR9HN4GDX9ZH"
    }
  ],
  "cancelUrl": "https://app.bookdineplay.com/e/dqN_AejenL2r7qXPdZuOxnLY9_BfVkTl"
}

Fehler

Status Wann
400 Ein Feld scheitert an der Validierung oder eine Anmelderegel greift. errors ordnet dem Feldnamen (Email, BuyerName, Quantity, …) seine Meldungen zu; eine verletzte Regel – mehr Tickets als maxPerRegistration oder als übrig sind, ein fehlender Teilnehmername, mehr Namen als Tickets – steht unter errors.registration, und detail wiederholt sie als einen Satz
403 Der Tarif der Location enthält keine Events – feature-not-in-plan mit requiredPlan: "Pro"
404 Keine solche Location oder kein veröffentlichtes Event mit diesem Slug
409 registration-closed: Das Fenster ist geschlossen. reason nennt den Grund (closed, sold-out, past, not-open); bei Ausverkauf kommt remaining: 0 hinzu – auch dann, wenn die letzten Plätze zwischen dem Lesen des Details und diesem Aufruf an jemand anderen gingen
409 ticket-sales-not-enabled: Das Event ist Paid, und der Online-Ticketverkauf ist noch nicht verfügbar

Eine verletzte Regel sieht so aus – dasselbe errors-Wörterbuch wie bei einem Feldfehler, sodass ein Client bei 400 nur eine Form behandelt:

{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "detail": "You can register at most 6 tickets at once.",
  "errors": {
    "registration": [
      "You can register at most 6 tickets at once."
    ]
  },
  "traceId": "0HNOK3A31C71S"
}

Regelmeldungen werden per Accept-Language lokalisiert; die Bestätigungs-E-Mail geht ebenfalls in dieser Sprache hinaus und fällt sonst auf die language des Events zurück.

curl

curl -X POST "https://api.bookdineplay.com/api/venues/your-venue/events/darts-league-night/registrations" \
  -H "X-BookDinePlay-Key: bdp_pk_your_publishable_key" \
  -H "Origin: https://www.your-venue.example" \
  -H "Content-Type: application/json" \
  -d '{ "buyerName": "Ada Lovelace", "email": "ada@example.com", "quantity": 2, "attendees": ["", "Grace Hopper"] }'

JavaScript

try {
  const registration = await client.registerForEvent('darts-league-night', {
    buyerName: 'Ada Lovelace', email: 'ada@example.com', quantity: 2, attendees: ['', 'Grace Hopper']
  });
  console.log(registration.reference, registration.tickets.map((t) => t.code), registration.cancelUrl);
} catch (err) {
  if (err.name === 'BookDinePlayError' && err.status === 409) console.log(err.reason);   // 'sold-out', ...
  if (err.name === 'BookDinePlayError' && err.status === 400) console.log(err.messages()); // the errors, flattened
}

C#

try
{
    var registration = await client.RegisterForEventAsync("your-venue", "darts-league-night",
        new EventRegistrationRequest { BuyerName = "Ada Lovelace", Email = "ada@example.com", Quantity = 2, Attendees = ["", "Grace Hopper"] },
        cancellationToken);
}
catch (BookDinePlayApiException ex) when (ex.Type == BookDinePlayApiException.RegistrationClosedType)
{
    Console.WriteLine(ex.Reason); // "closed", "sold-out", "past" or "not-open"; ex.Remaining is 0 when sold out
}
catch (BookDinePlayApiException ex) when (ex.StatusCode == HttpStatusCode.BadRequest)
{
    Console.WriteLine(string.Join(" ", ex.Messages)); // ex.Errors: field (or "registration") -> messages
}

POST /api/venues/{venueSlug}/events/{eventSlug}/tickets/checkout

Reserviert für bezahlte Events. Bis der Online-Ticketverkauf für die Location aktiviert ist, antwortet die Route mit 409 vom Typ ticket-sales-not-enabled (403 feature-not-in-plan und 404 wie oben). Einen Request-Body gibt es noch nicht, und die SDKs haben keine Methode dafür, bis die Route etwas tut.

{
  "type": "https://bookdineplay.com/docs/api/errors/ticket-sales-not-enabled",
  "title": "Ticket sales not enabled",
  "status": 409,
  "detail": "Online ticket sales are not enabled for this venue yet.",
  "traceId": "0HNOK3A31C71U"
}

curl

curl -X POST "https://api.bookdineplay.com/api/venues/your-venue/events/gala-night/tickets/checkout" \
  -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/events/gala-night/tickets/checkout', {
  method: 'POST', headers: { 'X-BookDinePlay-Key': 'bdp_pk_your_publishable_key' }
});

C#

using var response = await http.PostAsync("api/venues/your-venue/events/gala-night/tickets/checkout", null, cancellationToken);

GET /api/tickets/{code}

Alles, was die Ticketseite zeigt, per 12-stelligem Code. Kein Schlüssel: Der Code ist die Berechtigung. Die Eingabe wird normalisiert – abcd-efgh-jklm wird wie ABCDEFGHJKLM aufgelöst. Nicht tarifgebunden.

Antwort 200 OK

Feld Typ Bedeutung
code, attendeeName string Dieses Ticket
status string Valid, CheckedIn (am Einlass gescannt) oder Cancelled
checkedInAt ISO-8601-Zeitpunkt oder null Wann es gescannt wurde
reference, registrationStatus string Die zugehörige Anmeldung und ihr Status (Confirmed, Cancelled)
venueSlug, venueName string Die Location
event object Die Listenform des Events inklusive status – ein Ticket eines abgesagten Events bleibt Valid, prüfen Sie also auch event.status
ticketUrl string Die Ticketseite auf app.bookdineplay.com
qrSvg string Ein Inline-SVG mit einem QR-Code, der ticketUrl kodiert; als HTML einfügen
{
  "code": "X7QZUP5AC3GH",
  "attendeeName": "Ada Lovelace",
  "status": "Valid",
  "checkedInAt": null,
  "reference": "BDP-E-ESJA",
  "registrationStatus": "Confirmed",
  "venueSlug": "demo-sportsbar",
  "venueName": "The Neon Tap",
  "event": {
    "slug": "darts-league-night",
    "title": "Darts league night",
    "excerpt": "Eight boards, one winner — sign up your team.",
    "imageUrl": null,
    "imageAlt": null,
    "status": "Published",
    "date": "2027-10-16",
    "start": "20:00",
    "end": "23:00",
    "timeLabel": "Doors 19:30",
    "locationLabel": "The Neon Tap, Boxhagener Straße 42, 10245 Berlin",
    "registrationMode": "Free",
    "language": "en"
  },
  "ticketUrl": "https://app.bookdineplay.com/ticket/X7QZUP5AC3GH",
  "qrSvg": "<svg …>…</svg>"
}

(Attribute gekürzt)

Fehler404 bei unbekanntem oder fehlerhaftem Code.

curl

curl "https://api.bookdineplay.com/api/tickets/ABCDEFGHJKLM"

JavaScript

const ticket = await client.ticket('ABCDEFGHJKLM'); // sends no key
document.querySelector('#qr').innerHTML = ticket.qrSvg;

C#

var ticket = await client.GetTicketAsync("ABCDEFGHJKLM", cancellationToken); // sends no key; null when unknown

POST /api/event-registrations/{cancelToken}/cancel

Storniert die Anmeldung hinter einem Storno-Link und gibt ihre Plätze frei; der Gast erhält eine Bestätigungs-E-Mail. Kein Schlüssel: Das Token ist die Berechtigung. Idempotent – ein zweiter Aufruf antwortet erneut mit 200. Nicht tarifgebunden. Kein Request-Body.

Antwort 200 OKreference, status (Cancelled), eventSlug, eventTitle.

{
  "reference": "BDP-E-ESJA",
  "status": "Cancelled",
  "eventSlug": "darts-league-night",
  "eventTitle": "Darts league night"
}

Fehler404 bei unbekanntem Token; 409, sobald ein Ticket der Anmeldung eingecheckt wurde (dann kann nur noch die Location stornieren).

curl

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

JavaScript

const token = location.pathname.split('/').pop();
const result = await client.cancelEventRegistration(token); // sends no key

C#

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

Warum zwei Routen keinen Schlüssel brauchen

Jede Route unter /api/venues/** braucht einen Schlüssel, weil eine Seite der Location-Website den Aufruf macht – und diese Seite trägt den veröffentlichbaren Schlüssel der Location. Ein Gast, der einen Ticket- oder Storno-Link aus seiner E-Mail öffnet, hat keine solche Seite: Er landet auf app.bookdineplay.com oder auf einer Seite von Ihnen, die nur den Code oder das Token aus der URL kennt. Dort einen Schlüssel zu verlangen, würde entweder ein Zugangsmerkmal in eine gemailte URL zwingen oder den Link brechen. Ticketcode und Storno-Token sind ohnehin schon an die Anmeldung gebundene Berechtigungen – dieselbe Überlegung wie beim gedruckten QR-Token hinter QR-Codes und Tischsitzungen –, deshalb hängen diese beiden Routen an der API-Wurzel außerhalb des Venue-Präfixes, und beide SDKs senden dort bewusst keinen Schlüssel.

Was der Gast erhält

  • Bei der Anmeldung – eine E-Mail mit jedem Ticket (Code, QR-Code, Link zur Ticketseite) und dem Storno-Link. Gesendet in der Sprache der Anfrage (Accept-Language), sonst in der language des Events.
  • Bei der Stornierung – eine Bestätigungs-E-Mail, egal ob der Gast über den Link storniert hat oder die Location die Anmeldung in der Konsole storniert hat.
  • Wenn die Location das Event absagt – jeder bestätigte Teilnehmer wird per E-Mail informiert.

Die beiden Seiten hinter diesen Links liegen auf app.bookdineplay.com und brauchen nichts von Ihrer Website:

Seite URL Zeigt
Ticket https://app.bookdineplay.com/ticket/{code} Das Ticket mit QR-Code, Teilnehmer, Event und Location – was die Location am Einlass eincheckt
Storno https://app.bookdineplay.com/e/{cancelToken} Die Anmeldung und einen Button, der sie storniert

Eine Website mit eigenen Eventseiten kann beide selbst aus den zwei schlüssellosen Routen oben rendern; ticketUrl und cancelUrl in der Anmeldeantwort zeigen weiterhin auf die BookDinePlay-Seiten, sodass die E-Mails in jedem Fall funktionieren.

Nächste Schritte

  • JavaScript-SDK: Events – das Events-Widget und die Client-Aufrufe für eigenes Markup.
  • Fehlerregistration-closed und ticket-sales-not-enabled im Detail.
  • .NET-SDK – dieselben fünf Aufrufe vom Server aus.