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 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
  }
}

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 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"
}

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 unknown

GET /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 Token

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. 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 key

C#

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

Warum 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 der language des 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 fragt GET /api/registrations/{cancelToken} ab, bis payment nicht mehr Pending ist. Die eingebaute Seite hat das Token in ihrem Pfad; eine eigene successUrl wird unverändert weitergegeben – führen Sie also den cancelToken aus 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-enabled und refund-not-available im Detail.
  • .NET-SDK – dieselben Aufrufe vom Server aus.