API-Dokumentation

Die botcmap.de API ermöglicht es, Events programmatisch anzulegen, zu verwalten und Events in der Nähe abzufragen – z.B. über Discord-Bots oder eigene Skripte.

API-Keys können unter Konto → API-Keys generiert werden.
Maschinenlesbare Spezifikation: /api/v1/openapi.json (OpenAPI 3.1)

Authentifizierung

Alle Endpoints erfordern einen API-Key im Authorization-Header:

Authorization: Bearer botcmap_sk_<dein-key>

Schreibende Endpoints (POST, PATCH) erfordern außerdem:

Content-Type: application/json

Scopes

Jeder Key hat einen oder mehrere Scopes. Beim Generieren wählbar:

ScopeEndpoints
readAlle GET-Endpoints
writeEvents anlegen, bearbeiten, veröffentlichen, stornieren, Serien anlegen
participantsTeilnehmer hinzufügen, Status ändern
communityCommunity Events einreichen

Rate-Limit

Max. 50 Events pro Key pro Tag. Jede schreibende Antwort enthält:

X-RateLimit-Limit: 50
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1746057600

Bei Überschreitung: 429 Too Many Requests.

Abgelaufene Keys

Keys mit gesetztem Ablaufdatum liefern nach dem Ablauf 401 Unauthorized: API key has expired.

Sandbox-Key

Ein schreibgeschützter Test-Key (nur read-Scope) kann über den Button „Sandbox-Key generieren“ erstellt werden – kein Event anlegen möglich, perfekt zum Ausprobieren.

Übersicht aller Endpoints

MethodePfadScopeBeschreibung
GET/api/v1/map/feedreadPlattformweiter Karten-Feed (Runden + Community-Events) für Partner-Importe
GET/api/v1/events/nearbyreadEvents in der Nähe (PLZ / Stadt / lat+lng)
GET/api/v1/events/minereadAlle eigenen Events (inkl. Entwürfe)
GET/api/v1/events/{id}readEvent-Details
POST/api/v1/eventswriteEvent anlegen
PATCH/api/v1/events/{id}writeEvent bearbeiten (Partial Update)
POST/api/v1/events/{id}/publishwriteEntwurf veröffentlichen
POST/api/v1/events/{id}/cancelwriteEvent stornieren
GET/api/v1/events/{id}/participantsreadTeilnehmerliste (nur eigene Events)
POST/api/v1/events/{id}/participantsparticipantsGast-Teilnehmer hinzufügen
PATCH/api/v1/events/{id}/participants/{userId}participantsTeilnehmerstatus ändern
GET/api/v1/events/{id}/ratingsreadBewertungen eines Events (anonymisiert)
POST/api/v1/serieswriteSerienevent anlegen
GET/api/v1/scriptsreadAlle bekannten Scripte
GET/api/v1/storytellers/{id}readÖffentliches Storyteller-Profil
POST/api/v1/community-eventscommunityCommunity Event einreichen
GET/api/v1/openapi.jsonreadOpenAPI 3.1 Spezifikation

GET /api/v1/events/nearby

Öffentliche Events in einem Radius. DSGVO: keine exakten Adressen oder GPS-Koordinaten in der Antwort.

ParameterTypPflichtDefaultBeschreibung
postal_codestringJa*PLZ des Suchzentrums
citystringJa*Stadtname als Alternative zur PLZ
latfloatJa*Breitengrad (zusammen mit lng)
lngfloatJa*Längengrad (zusammen mit lat)
radius_kmintNein50Suchradius in km, max. 200
fromdateNeinheuteStartdatum YYYY-MM-DD
todateNeinEnddatum YYYY-MM-DD
difficulty_minintNeinMin. Schwierigkeit (1–10)
difficulty_maxintNeinMax. Schwierigkeit (1–10)
beginner_onlyboolNeinfalseNur Einsteiger-Events (1 oder true)
pageintNein1Seitennummer
per_pageintNein50Ergebnisse pro Seite (max. 100)
fieldsstringNeinKommaliste gewünschter Felder, z.B. id,title,free_slots
GET /api/v1/events/nearby?city=Berlin&radius_km=30&page=1&per_page=10
Authorization: Bearer botcmap_sk_...

{
  "data": [{
    "id": "a1b2c3d4",
    "url": "https://botcmap.de/event/a1b2c3d4",
    "title": "Trouble Night",
    "date": "2026-05-10",
    "start_time": "18:00",
    "city": "Berlin",
    "postal_code": "10115",
    "difficulty_min": 3,
    "difficulty_max": 7,
    "is_beginner_friendly": false,
    "free_slots": 2,
    "max_players": 12,
    "scripts": [{ "name": "Trouble Brewing", "type": "official" }]
  }],
  "meta": { "total": 23, "page": 1, "per_page": 10, "pages": 3, "radius_km": 30, "from": "2026-04-17" }
}

GET /api/v1/map/feed

Plattformweiter Karten-Feed für Partner-Importe (z.B. brettspielabende.de): alle öffentlich sichtbaren Runden (published, nicht privat/abgesagt/versteckt) und alle freigegebenen Community-Events – jeweils mit anonymisierter Koordinate, Datum und Link. Standardmäßig nur kommende Events. Scope: read.

ParameterTypPflichtDefaultBeschreibung
fromdateNeinheuteNur Events ab diesem Datum YYYY-MM-DD
todateNeinNur Events bis zu diesem Datum
countrystringNeinAuf ein Land filtern: de, at oder ch
beginner_onlyboolNeinfalseNur Einsteiger-Events
pageintNein1Seitennummer
per_pageintNein200Ergebnisse pro Seite (max. 500)
GET /api/v1/map/feed?country=de&per_page=200
Authorization: Bearer botcmap_sk_...

{
  "data": [
    {
      "type": "round",
      "id": "a1b2c3d4",
      "url": "https://botcmap.de/event/a1b2c3d4",
      "title": "Trouble Night",
      "date": "2026-05-10",
      "start_time": "18:00",
      "city": "Berlin",
      "postal_code": "10115",
      "region": "Berlin",
      "country": "de",
      "lat": 52.51,
      "lng": 13.38,
      "is_beginner_friendly": false
    },
    {
      "type": "community",
      "id": "bounty-hunter-12e8",
      "url": "https://botcmap.de/community-event/bounty-hunter-12e8",
      "title": "Bounty Hunter Night",
      "date": "2026-05-12",
      "start_time": "19:00",
      "city": "Hamburg",
      "postal_code": "20095",
      "region": null,
      "country": "de",
      "lat": 53.55,
      "lng": 9.99,
      "is_beginner_friendly": true
    }
  ],
  "meta": { "total": 42, "page": 1, "per_page": 200, "pages": 1, "from": "2026-04-17" }
}

type unterscheidet "round" (normale Runde) von "community" (Community-Event). Die Koordinaten (lat/lng) sind anonymisiert – gleiche Genauigkeit wie die öffentliche Karte.

GET /api/v1/events/mine

Alle eigenen Events – inkl. Entwürfe, abgesagte und abgeschlossene. Scope: read.

GET /api/v1/events/mine
Authorization: Bearer botcmap_sk_...

{
  "data": [{
    "id": "a1b2c3d4",
    "url": "https://botcmap.de/event/a1b2c3d4",
    "title": "Trouble Night",
    "date": "2026-05-10",
    "start_time": "18:00",
    "city": "Berlin",
    "postal_code": "10115",
    "lat": 52.51,
    "lng": 13.38,
    "status": "published",
    "max_players": 12,
    "confirmed": 8,
    "requested": 2,
    "free_slots": 4
  }],
  "meta": { "total": 5 }
}

lat/lng sind anonymisierte Koordinaten (gleiche Genauigkeit wie die öffentliche Karte) – ideal für eigene Karten-Ansichten. Können null sein, falls keine Koordinaten hinterlegt sind.

GET /api/v1/events/{id}

Vollständige Event-Details. Eigene Entwurfs-Events sind auch lesbar. Scope: read.

GET /api/v1/events/a1b2c3d4
Authorization: Bearer botcmap_sk_...

{
  "id": "a1b2c3d4",
  "url": "https://botcmap.de/event/a1b2c3d4",
  "title": "Trouble Night",
  "description": "Eine entspannte Runde für alle.",
  "date": "2026-05-10",
  "start_time": "18:00",
  "duration_hours": 4,
  "city": "Berlin",
  "postal_code": "10115",
  "country": "Deutschland",
  "status": "published",
  "max_players": 12,
  "min_players": 5,
  "confirmed_slots": 8,
  "requested_slots": 2,
  "waitlist_slots": 1,
  "free_slots": 4,
  "difficulty_min": 3,
  "difficulty_max": 7,
  "is_beginner_friendly": false,
  "confirmation_mode": "manual",
  "scripts": [{ "name": "Trouble Brewing", "type": "official" }],
  "storyteller": "Max"
}

POST /api/v1/events — Event anlegen

Legt ein neues Event an. Erfordert freigeschalteten Storyteller-Account. Scope: write.

FeldTypPflichtBeschreibung
titlestringTitel des Events
event_datestringDatum YYYY-MM-DD
start_timestringUhrzeit HH:MM
addressstringVollständige Adresse inkl. Hausnr. (wird verschlüsselt)
postal_codestringPostleitzahl
citystringOrt
max_playersintMax. Spieleranzahl (min. 5)
publishbooltrue → sofort veröffentlicht; absent/false → Entwurf
descriptionstringBeschreibung
duration_hoursfloatDauer in Stunden (default: 4)
min_playersintMin. Spieleranzahl (default: 5)
difficulty_minintMin. Schwierigkeit 1–10
difficulty_maxintMax. Schwierigkeit 1–10
is_beginner_friendlyboolEinsteiger-freundlich
confirmation_modestring"manual" (default) oder "auto"
countrystringLand (default: "Deutschland")
POST /api/v1/events
Authorization: Bearer botcmap_sk_...
Content-Type: application/json

{
  "title": "Trouble Night",
  "event_date": "2026-06-15",
  "start_time": "19:00",
  "address": "Musterstr. 1, 10115 Berlin",
  "postal_code": "10115",
  "city": "Berlin",
  "max_players": 12,
  "difficulty_min": 3,
  "difficulty_max": 7,
  "publish": false
}

→ 201 Created
{ "id": "a1b2c3d4", "url": "https://botcmap.de/event/a1b2c3d4", "status": "draft" }

PATCH /api/v1/events/{id} — Event bearbeiten

Partial Update: nur gesendete Felder werden geändert. Adresse wird nur neu geocodiert wenn address gesendet wird. Scope: write.

FeldTypBeschreibung
titlestringNeuer Titel
event_datestringNeues Datum YYYY-MM-DD
start_timestringNeue Uhrzeit HH:MM
addressstringNeue Adresse (mit postal_code + city angeben)
descriptionstringNeue Beschreibung
max_playersintNeue max. Spielerzahl
min_playersintNeue min. Spielerzahl
difficulty_minintMin. Schwierigkeit 1–10
difficulty_maxintMax. Schwierigkeit 1–10
is_beginner_friendlyboolEinsteiger-freundlich
duration_hoursfloatDauer in Stunden

Antwort 200 OK: {"id": "...", "url": "...", "status": "published"}

POST /api/v1/events/{id}/publish — Entwurf veröffentlichen

Veröffentlicht ein eigenes Entwurfs-Event. Kein Body nötig. Scope: write.

POST /api/v1/events/a1b2c3d4/publish
Authorization: Bearer botcmap_sk_...

→ 200 OK
{ "id": "a1b2c3d4", "url": "https://botcmap.de/event/a1b2c3d4", "status": "published" }

POST /api/v1/events/{id}/cancel — Event stornieren

Setzt ein eigenes Event auf cancelled. Kein Body nötig. Nicht rückgängig machbar via API. Scope: write.

POST /api/v1/events/a1b2c3d4/cancel
Authorization: Bearer botcmap_sk_...

→ 200 OK
{ "id": "a1b2c3d4", "status": "cancelled" }

GET /api/v1/events/{id}/participants — Teilnehmerliste

Teilnehmerliste eines eigenen Events. Nur der Storyteller-Owner hat Zugriff. Scope: read.

GET /api/v1/events/a1b2c3d4/participants
Authorization: Bearer botcmap_sk_...

{
  "data": [
    {
      "user_id": 42,
      "name": "Anna",
      "status": "confirmed",
      "plus_one": false,
      "join_message": "Freue mich!",
      "requested_at": "2026-04-10"
    },
    {
      "user_id": null,
      "name": "Max (Gast)",
      "status": "confirmed",
      "plus_one": false,
      "join_message": null,
      "requested_at": null
    }
  ],
  "meta": { "total": 2 }
}

Gäste (ohne Account) haben user_id: null. Status-Werte: confirmed, requested, waitlist, cancelled, attended, no_show.

POST /api/v1/events/{id}/participants — Teilnehmer hinzufügen

Fügt einen Gast-Teilnehmer (ohne Account) zu einem eigenen Event hinzu. Scope: participants.

FeldTypPflichtBeschreibung
namestringName des Gastes
plus_oneboolKommt mit einer Begleitperson (default: false)
POST /api/v1/events/a1b2c3d4/participants
Authorization: Bearer botcmap_sk_...
Content-Type: application/json

{ "name": "Max Mustermann", "plus_one": false }

→ 201 Created
{ "participant_id": 99, "name": "Max Mustermann", "status": "confirmed", "plus_one": false }

PATCH /api/v1/events/{id}/participants/{userId} — Teilnehmerstatus ändern

Ändert den Status eines Teilnehmers (mit Account) auf einem eigenen Event. userId aus der Teilnehmerliste. Scope: participants.

FeldTypPflichtBeschreibung
statusstringconfirmed, waitlist, cancelled, attended, no_show
PATCH /api/v1/events/a1b2c3d4/participants/42
Authorization: Bearer botcmap_sk_...
Content-Type: application/json

{ "status": "confirmed" }

→ 200 OK
{ "user_id": 42, "status": "confirmed" }

GET /api/v1/events/{id}/ratings — Bewertungen

Storyteller-Bewertungen eines vergangenen Events (anonymisiert – keine Nutzernamen). Nur für veröffentlichte oder abgeschlossene Events. Scope: read.

GET /api/v1/events/a1b2c3d4/ratings
Authorization: Bearer botcmap_sk_...

{
  "event_id": "a1b2c3d4",
  "count": 5,
  "avg_orga": 4.2,
  "avg_rules_confidence": 4.5,
  "avg_atmosphere": 4.3,
  "avg_location": 4.0,
  "avg_overall": 4.25,
  "ratings": [
    { "orga": 5, "rules_confidence": 5, "atmosphere": 4, "location": 4, "date": "2026-04-01" }
  ]
}

POST /api/v1/series — Serienevent anlegen

Legt ein Serienevent mit mehreren Terminen an. Scope: write.

FeldTypPflichtBeschreibung
titlestringSerientitel
occurrencesarrayListe von {"event_date": "YYYY-MM-DD", "start_time": "HH:MM"}
addressstringAdresse (gilt für alle Termine)
postal_codestringPostleitzahl
citystringOrt
max_playersintMax. Spieleranzahl
publishbooltrue → sofort veröffentlicht
descriptionstringBeschreibung
difficulty_minintMin. Schwierigkeit
difficulty_maxintMax. Schwierigkeit
is_beginner_friendlyboolEinsteiger-freundlich
POST /api/v1/series
Authorization: Bearer botcmap_sk_...
Content-Type: application/json

{
  "title": "Montags-Runde",
  "address": "Musterstr. 1",
  "postal_code": "10115",
  "city": "Berlin",
  "max_players": 12,
  "publish": true,
  "occurrences": [
    { "event_date": "2026-05-05", "start_time": "19:00" },
    { "event_date": "2026-05-12", "start_time": "19:00" },
    { "event_date": "2026-05-19", "start_time": "19:00" }
  ]
}

→ 201 Created
{
  "series_id": 7,
  "occurrences": 3,
  "status": "published",
  "first_event": { "id": "a1b2c3d4", "url": "https://botcmap.de/event/a1b2c3d4" }
}

GET /api/v1/scripts — Scriptliste

Alle bekannten Scripte (nur aktive). Scope: read.

GET /api/v1/scripts
Authorization: Bearer botcmap_sk_...

{
  "data": [
    {
      "id": 1,
      "name": "Trouble Brewing",
      "type": "official",
      "difficulty": 2,
      "color": "green",
      "is_beginner_friendly": true,
      "recommended_min_players": 5,
      "recommended_max_players": 15,
      "url": "https://botcscripts.com/script/trouble_brewing"
    }
  ],
  "meta": { "total": 12 }
}

GET /api/v1/storytellers/{id} — Storyteller-Profil

Öffentliche Storyteller-Daten. id = User-ID. Scope: read.

GET /api/v1/storytellers/42
Authorization: Bearer botcmap_sk_...

{
  "id": 42,
  "name": "Max",
  "city": "Berlin",
  "bio": "Spiele seit 2022...",
  "follower_count": 18,
  "event_count": 34,
  "avg_rating": 4.7,
  "rating_count": 28,
  "url": "https://botcmap.de/storyteller/42"
}

POST /api/v1/community-events — Community Event einreichen

Wie das Web-Formular, aber per API. Geht durch Admin-Freigabe. Scope: community.

FeldTypPflichtBeschreibung
titlestringTitel
event_datestringDatum YYYY-MM-DD
start_timestringUhrzeit HH:MM
addressstringAdresse
postal_codestringPostleitzahl
citystringOrt
submitter_emailstringKontakt-E-Mail (nicht öffentlich)
submitter_namestringName der einreichenden Person
descriptionstringBeschreibung
location_namestringName des Veranstaltungsorts
contact_discordstringDiscord-Handle
website_urlstringURL zur Anmeldung/Info
script_infostringScript-Info (Freitext)
is_beginner_friendlyboolEinsteiger-freundlich

Antwort 201 Created: {"id": "...", "status": "pending", "message": "..."}

HTTP-Statuscodes

CodeBedeutung
200Erfolg
201Erstellt
401Kein, ungültiger oder abgelaufener API-Key
403Key gültig, aber fehlender Scope oder kein freigeschalteter Storyteller
404Event nicht gefunden oder gehört nicht zum API-Key-Inhaber
415Falscher Content-Type (erwartet application/json)
422Validierungsfehler – error-Feld enthält Details
429Rate-Limit erreicht (max. 50 Events/Key/Tag)
500Serverfehler