Anmelden um den Slug und den veröffentlichbaren Schlüssel Ihrer Location in jedem Beispiel zu sehen.
Ihr Konto hat noch keine Location, die Beispiele behalten daher ihre Platzhalter. Abmelden
Angemeldet als · . Legen Sie in der Konsole einen veröffentlichbaren Schlüssel an und laden Sie die Seite neu, um ihn hier zu sehen. Abmelden
Angemeldet als · . Die Beispiele zeigen den veröffentlichbaren Schlüssel Ihrer Location. Abmelden
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.
Query – upcoming (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 OK – events[], 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"
}
]
}Fehler – 404 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 EventsC#
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
}
}Fehler – 404 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 unknownPOST /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)
Fehler – 404 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 unknownPOST /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 OK – reference, status (Cancelled), eventSlug, eventTitle.
{
"reference": "BDP-E-ESJA",
"status": "Cancelled",
"eventSlug": "darts-league-night",
"eventTitle": "Darts league night"
}Fehler – 404 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 keyC#
var result = await client.CancelEventRegistrationAsync(cancelToken, cancellationToken); // sends no key; null when unknownWarum 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 derlanguagedes 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.
- Fehler –
registration-closedundticket-sales-not-enabledim Detail. - .NET-SDK – dieselben fünf Aufrufe vom Server aus.