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
Events einer Location auflisten, kostenlos anmelden oder Tickets per Stripe kaufen, eine Anmeldung nachschlagen sowie die schlüssellosen 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. Ein Paid-Event (Premium) verkauft diese Tickets über eine Stripe-Checkout-Sitzung auf dem eigenen verbundenen Konto der Location, statt sofort zu bestätigen. Liste, Detail und Anmeldung folgen denselben Schlüssel- und CORS-Regeln wie der Rest von /api/venues/**. Die letzten drei 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 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 |
refundPolicy |
object oder null | Nur bei Paid gesetzt – siehe Die Erstattungsregel. Bei jedem anderen Modus null |
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.
Die Erstattungsregel
Jedes Paid-Event trägt eine Erstattungsregel, die die Location beim Einrichten des Events festlegt – sowohl in registration.refundPolicy des Event-Details als auch in der eigenen Anmeldung eines Gasts unter GET /api/registrations/{cancelToken}:
| Feld | Typ | Bedeutung |
|---|---|---|
mode |
string | NoOnlineRefund (der Gast muss die Location kontaktieren) oder FullRefundUntil (volle Online-Erstattung bis zu einer festen Stundenzahl vor Beginn) |
hoursBeforeStart |
integer | Nur bei FullRefundUntil relevant, 0–720 |
refundableUntil |
ISO-8601-Zeitpunkt oder null | Der Zeitpunkt, nach dem keine Online-Erstattung mehr möglich ist; null bei NoOnlineRefund oder einem Event ohne festen Beginn |
Der eigene Storno-Button eines Gasts sollte nur angezeigt werden, wenn canCancel seiner Anmeldung true ist – siehe GET /api/registrations/{cancelToken}. Der Versuch, eine bezahlte Anmeldung zu stornieren, die die Regel nicht mehr erlaubt, antwortet mit 409 refund-not-available und lässt die Anmeldung unverändert; die Location kann sie jederzeit unabhängig von der Regel aus der Konsole heraus erstatten.
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"
}Akzeptiert denselben optionalen Idempotency-Key-Header wie die Reservierungsanlage: pro Anmeldeversuch einen erzeugen und bei einem Wiederholungsversuch weiterverwenden – ein vorübergehender 5xx/Timeout gibt dann die Original-Anmeldung wieder, statt eine zweite mit eigenen Tickets und Bestätigungs-E-Mail anzulegen. Siehe Idempotenz-Probleme für den einzigen Weg, wie der Key selbst abgelehnt werden kann.
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 |
409 |
idempotency-key-reused: Der gesendete Idempotency-Key wurde bereits für einen Request mit einem anderen Body verwendet |
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
Kauft Tickets für ein Paid-Event: Öffnet eine Stripe-Checkout-Sitzung auf dem eigenen verbundenen Stripe-Konto der Location und liefert die URL, an die der Gast geschickt wird. Die Anmeldung – Tickets inklusive – wird schon vor dem Aufruf bei Stripe als Zahlungs-Hold angelegt, sodass die Plätze in dem Moment reserviert sind, in dem der Gast sich verbindlich entscheidet, statt erst bei der Zahlung; der Hold – und die Checkout-Sitzung – läuft nach 30 Minuten ab, wenn der Gast nicht zahlt, und die Plätze werden automatisch freigegeben. Die Bestätigungs-E-Mail mit den Tickets wartet, bis die Zahlung tatsächlich bestätigt ist – fragen Sie nach oder lassen Sie den Gast über successUrl zurückkehren, um es zu erfahren (Was der Gast erhält). Wird mit 409 ticket-sales-not-enabled abgelehnt, sofern nicht der Tarif des Mandanten den Ticketverkauf enthält, das Stripe-Connect-Auszahlungskonto der Location Enabled ist und das Event tatsächlich Paid ist – siehe den Hinweis am Anfang dieser Seite.
Request-Body (Content-Type: application/json) – dieselben Felder wie bei der Anmeldung, plus zwei optionale Rückkehr-URLs und ein optionaler Geschenkgutschein:
| Feld | Pflicht | Bedeutung |
|---|---|---|
buyerName, email, phone, notes, quantity, attendees |
wie oben | Dieselben Regeln wie bei der Anmeldung |
successUrl |
nein | Wohin Stripe Checkout den Gast nach der Zahlung schickt. Muss https sein und entweder auf der Origin-Liste dieses API-Schlüssels stehen oder die eigene Gast-App der Plattform sein, sonst wird die Anfrage mit 400 abgelehnt (errors.successUrl); ohne Angabe wird die eingebaute Anmeldeseite /r/{cancelToken} verwendet |
cancelUrl |
nein | Wohin Checkout den Gast schickt, wenn er abbricht. Dieselbe Regel wie successUrl (errors.cancelUrl bei Verstoß) |
voucherCode |
nein | Ein Geschenkgutschein dieser Location, der die Tickets ganz oder teilweise bezahlt. Nur ein Betragsgutschein bezahlt Tickets. Gehört zu dem, was ein Idempotency-Key schützt; nie loggen |
Antwort 201 Created – eine Checkout-Sitzung, noch nicht bezahlt:
| Feld | Typ | Bedeutung |
|---|---|---|
reference |
string | BDP-E-XXXX – nur zur Anzeige |
checkoutUrl |
string | Schicken Sie den Gast hierhin – ein Redirect oder window.location.assign –, um zu bezahlen |
expiresAt |
ISO-8601-Zeitpunkt | Wann die Checkout-Sitzung und die reservierten Plätze unbezahlt ablaufen |
cancelToken |
string | Die Berechtigung für GET /api/registrations/{cancelToken} und die Storno-Route; nicht loggen |
voucherAmount |
decimal oder null | Was der Gutschein bezahlt; null ohne Gutschein |
amountDue |
decimal oder null | Was die Karte bezahlt, wenn ein Gutschein mitgegeben wurde. 0 heißt, der Gutschein hat alles bezahlt: Es wurde keine Zahlungsseite geöffnet, die Tickets sind ausgestellt, und checkoutUrl ist die Erfolgsseite |
{
"reference": "BDP-E-K7QM",
"checkoutUrl": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3…",
"expiresAt": "2027-10-16T18:30:00+00:00",
"cancelToken": "dqN_AejenL2r7qXPdZuOxnLY9_BfVkTl"
}Mit einem Gutschein wird sein Wert zusammen mit den Plätzen reserviert und abgebucht, wenn die Zahlung eintrifft; die Karte bezahlt den Rest, mindestens 0,50 € (Mit einem Gutschein bezahlen). Ein Gutschein, der alles deckt, bestätigt die Anmeldung sofort, ohne Stripe und ohne das Auszahlungskonto, das die Karte bräuchte; checkoutUrl ist dann Ihre successUrl oder die eingebaute Anmeldeseite. Eine Erstattung – durch die Location, bei einer Stornierung, die die Erstattungsregel erlaubt, oder wenn das Event abgesagt wird – bucht den Gutscheinteil auf den Gutschein zurück.
Akzeptiert ebenfalls den optionalen Idempotency-Key-Header – der schärfste Einsatz davon in dieser API: Der Platz wird reserviert, bevor Stripe überhaupt aufgerufen wird, sodass ein identischer Wiederholungsversuch (ein erneuter Versuch eines Resilience-Handlers bei 5xx/Timeout, oder ein Gast, der zweimal auf „Kaufen" tippt) dieselbe Checkout-URL wiedergibt, statt einen zweiten 30-minütigen Platz auf einem möglicherweise fast ausverkauften Event verwaist zurückzulassen. Siehe Idempotenz-Probleme.
Fehler
| Status | Wann |
|---|---|
400 |
Ein Feld scheitert an der Validierung oder eine Anmelderegel greift – dieselbe errors-Form wie bei der Anmeldung |
400 |
successUrl oder cancelUrl ist nicht https, oder der Origin steht nicht auf der Liste dieses Schlüssels – errors.successUrl / errors.cancelUrl |
404 |
Keine solche Location oder kein veröffentlichtes Event mit diesem Slug |
409 |
registration-closed: Das Fenster ist geschlossen |
409 |
ticket-sales-not-enabled: Tarif, Auszahlungskonto oder Event-Modus lehnen den Verkauf ab |
409 |
idempotency-key-reused: Der gesendete Idempotency-Key wurde bereits für einen Request mit einem anderen Body verwendet – die Checkout-Sitzung des ersten Versuchs bleibt unangetastet |
409 |
checkout-in-progress: Ein Wiederholungsversuch landete, während die Checkout-Sitzung des ersten Versuchs noch angelegt wurde – einen Moment warten und mit demselben Key erneut versuchen |
400, 404, 409 |
Mit einem Gutschein: eines der Gutschein-Probleme oder 409 mit reason below-card-minimum, wenn die Summe 0,50 € oder weniger beträgt und der Gutschein sie nicht deckt |
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" \
-H "Content-Type: application/json" \
-d '{ "buyerName": "Ada Lovelace", "email": "ada@example.com", "quantity": 2, "attendees": ["", "Grace Hopper"], "successUrl": "https://www.your-venue.example/event/gala-night?paid=1", "cancelUrl": "https://www.your-venue.example/event/gala-night?cancelled=1" }'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', 'Content-Type': 'application/json' },
body: JSON.stringify({
buyerName: 'Ada Lovelace', email: 'ada@example.com', quantity: 2, attendees: ['', 'Grace Hopper'],
successUrl: location.origin + '/event/gala-night?paid=1',
cancelUrl: location.origin + '/event/gala-night?cancelled=1'
})
});
if (response.ok) {
const checkout = await response.json();
window.location.assign(checkout.checkoutUrl);
}C#
using var response = await http.PostAsync("api/venues/your-venue/events/gala-night/tickets/checkout", JsonContent.Create(new
{
buyerName = "Ada Lovelace", email = "ada@example.com", quantity = 2, attendees = new[] { "", "Grace Hopper" },
successUrl = "https://example.com/event/gala-night?paid=1",
cancelUrl = "https://example.com/event/gala-night?cancelled=1"
}), 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 unknownGET /api/registrations/{cancelToken}
Die eigene Anmelde- und Ticketseite eines Gasts in einem Aufruf: Status, Zahlungsstand, Tickets (sobald ausgestellt) und die geltende Erstattungsregel – alles, was /r/{cancelToken} auf app.bookdineplay.com zeigt, und was ein ?session_id=-Rücksprung von Checkout abfragt, solange payment noch Pending ist. Kein Schlüssel: Das Storno-Token in der URL ist selbst die Berechtigung, dieselbe Überlegung wie bei der Ticket- und der Storno-Route. Nicht tarifgebunden.
Antwort 200 OK
| Feld | Typ | Bedeutung |
|---|---|---|
reference |
string | BDP-E-XXXX |
eventTitle, eventSlug, venueSlug |
string | Welches Event, und wo |
status |
string | Confirmed, Cancelled oder PendingPayment, solange Stripe Checkout noch läuft |
payment |
string | NotRequired (kostenlos), Pending, Paid oder Refunded |
quantity |
integer | Gekaufte Tickets |
tickets[] |
object[] | Wird mit dem Zahlungs-Hold selbst geprägt, die Codes existieren also schon, solange payment noch Pending ist – Plätze werden reserviert, sobald sich der Gast entscheidet, nicht erst bei der Zahlung. Eines pro Teilnehmer, dieselbe Form wie tickets[] bei der Anmeldung |
refundPolicy |
object | Siehe Die Erstattungsregel |
canCancel |
boolean | Ob der eigene Storno-Button des Gasts jetzt angezeigt werden sollte |
cancelNote |
string oder null | Warum nicht, wenn canCancel false ist: bereits storniert, ein Ticket eingecheckt, oder die Erstattungsregel erlaubt bei einer bezahlten Anmeldung keine Online-Erstattung mehr |
{
"reference": "BDP-E-K7QM",
"eventTitle": "Gala night",
"eventSlug": "gala-night",
"venueSlug": "demo-sportsbar",
"status": "Confirmed",
"payment": "Paid",
"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" }
],
"refundPolicy": { "mode": "FullRefundUntil", "hoursBeforeStart": 24, "refundableUntil": "2027-10-15T20:00:00+00:00" },
"canCancel": true,
"cancelNote": null
}Fehler – 404 bei unbekanntem oder fehlerhaftem Token.
curl
curl "https://api.bookdineplay.com/api/registrations/dqN_AejenL2r7qXPdZuOxnLY9_BfVkTl"JavaScript
const registration = await client.registration(cancelToken); // sendet keinen Schlüssel
if (registration.payment === 'Pending') { /* nach ein paar Sekunden erneut abfragen */ }C#
var registration = await client.GetRegistrationAsync(cancelToken, cancellationToken); // sendet keinen Schlüssel; null bei unbekanntem TokenPOST /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. Bei einer bezahlten Anmeldung erstattet dieser Aufruf sie auch – aber nur, wenn die Erstattungsregel des Events gerade eine Online-Erstattung erlaubt; sonst bleibt die Anmeldung unverändert (nichts storniert, keine Zahlung angefasst), und der Aufruf antwortet mit 409 refund-not-available. Prüfen Sie zuerst canCancel bei GET /api/registrations/{cancelToken}, um den Umweg zu vermeiden. Kein Schlüssel: Das Token ist die Berechtigung. Idempotent – ein zweiter Aufruf nach einer erfolgreichen Stornierung 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 refund-not-available bei einer bezahlten Anmeldung, die die Erstattungsregel nicht mehr online erstatten lässt; 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 drei 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-, Anmelde- 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 drei 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 oder einem abgeschlossenen bezahlten Checkout – 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. - Solange ein bezahlter Checkout noch läuft – noch nichts; der Gast ist auf
successUrl, oder auf der eingebauten Anmeldeseite, wenn die Anfrage keine genannt hat, und diese fragtGET /api/registrations/{cancelToken}ab, bispaymentnicht mehrPendingist. Die eingebaute Seite hat das Token in ihrem Pfad; eine eigenesuccessUrlwird unverändert weitergegeben – führen Sie also dencancelTokenaus der Checkout-Antwort selbst mit, wenn diese Seite abfragen soll. - 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 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 |
| Anmeldung | https://app.bookdineplay.com/r/{cancelToken} |
Status, Zahlungsstand (fragt ab, solange ausstehend), Tickets sobald ausgestellt, die Erstattungsregel und einen Storno-Button – wohin die Standard-successUrl eines Checkouts den Gast schickt |
| Storno | https://app.bookdineplay.com/e/{cancelToken} |
Die Anmeldung und einen Button, der sie storniert |
Eine Website mit eigenen Eventseiten kann alle drei selbst aus den 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-closed,ticket-sales-not-enabledundrefund-not-availableim Detail. - .NET-SDK – dieselben Aufrufe vom Server aus.