Referenz
| Feld | Wert |
|---|---|
| Version | 1 |
| Geprüft | 2026-08-30 |
| Status | Beta |
| OpenAPI | /openapi/public-v1.yaml |
| Basis-URL | https://api.kursnaut.ccl-dev.com |
Diese Seite beschreibt den ausgelieferten Vertrag. Abweichungen gehören zuerst in die OpenAPI-Datei, dann hierher und in den Changelog.
Authentifizierung
Abschnitt betitelt „Authentifizierung“| Eigenschaft | Wert |
|---|---|
| Schema | Authorization: Bearer <key> |
| Key-Präfix | orb_api_ |
| Herkunft | Dashboard Integrationen, nur Owner und Admin |
| Workspace | ausschließlich aus dem Key, kein Query-Parameter |
Ungültige, fehlende, zu lange, widerrufene oder rotierte Keys ergeben
401 / invalid_api_key. Die Fehlermeldung enthält keine Hashes und keine
internen IDs.
| Scope | Endpunkt | Personenbezogene Daten |
|---|---|---|
courses:read |
GET /api/public/v1/courses, GET /api/public/v1/courses/{courseId} |
nein |
bookings:read |
GET /api/public/v1/bookings |
nein |
participants:read |
derselbe Bookings-Endpunkt | participant.name, participant.email |
participants:read ohne bookings:read lässt sich nicht anlegen. Ein Key mit
nur courses:read erhält auf /bookings 403 / missing_scope.
GET /api/public/v1/courses
Abschnitt betitelt „GET /api/public/v1/courses“Liefert veröffentlichte Kursdurchführungen (courseRun) mit geplanten
Sitzungen des Key-Workspaces, sortiert nach dem frühesten Sitzungsbeginn.
Query: pageSize (1–50, Standard 50), cursor (undurchsichtig, von
nextCursor). Ohne Cursor beginnt die erste Seite.
Erfolg: 200, Body { "data": Course[], "nextCursor": string | null, "pageSize": number },
Header Cache-Control: private, no-store.
data enthält höchstens pageSize Kurse. Jeder Kurs enthält höchstens
pageSize Termine, früheste zuerst. Weitere Kurse folgen über nextCursor.
Weitere Termine eines Kurses liefert GET /api/public/v1/courses/{courseId}.
Die JSON-Antwort ist zusätzlich auf 256 KiB begrenzt.
| Feld | Typ | Bedeutung |
|---|---|---|
id |
string | ID der Kursdurchführung |
title |
string | Titel der Kursvorlage |
description |
string | Beschreibung der Kursvorlage |
audience |
string | Zielgruppe der Kursvorlage |
offeringKind |
string | series oder coaching |
priceInCents |
integer | Preis in Cent, 0 ist kostenlos |
capacity |
integer | Plätze der Durchführung |
waitlistEnabled |
boolean | Warteliste für diese Durchführung |
sessions |
Session[] |
geplante Termine der aktuellen Seite, höchstens pageSize |
Session
Abschnitt betitelt „Session“| Feld | Typ | Bedeutung |
|---|---|---|
id |
string | Sitzungs-ID |
startsAt |
string (ISO-8601 UTC) | Beginn |
durationMinutes |
integer | Dauer in Minuten |
timeZone |
string | IANA-Zeitzone, zum Beispiel Europe/Berlin |
location |
string | Veranstaltungsort |
deliveryMode |
string | in_person oder online |
Entwürfe, nicht veröffentlichte Durchführungen und abgesagte Sitzungen fehlen. Online-Zugangsdaten gehören nicht zur Antwort.
GET /api/public/v1/courses/{courseId}
Abschnitt betitelt „GET /api/public/v1/courses/{courseId}“Liefert eine veröffentlichte Kursdurchführung des Key-Workspaces. Sitzungen
sind nach Beginn sortiert und mit denselben Query-Parametern pageSize und
cursor paginiert.
Erfolg: 200, Body { "data": Course, "nextCursor": string | null, "pageSize": number }.
data.sessions enthält die aktuelle Sitzungsseite. Unbekannte,
unveröffentlichte oder zu einem anderen Workspace gehörende IDs ergeben
404 / course_not_found. Ungültiger Cursor oder unzulässige Seitengröße
ergeben 400 / invalid_pagination.
GET /api/public/v1/bookings
Abschnitt betitelt „GET /api/public/v1/bookings“Liefert Buchungen des Key-Workspaces, absteigend nach createdAt und bei
gleichem Zeitstempel nach id. Ohne Query-Parameter bleiben die neuesten
100 Buchungen unter data erhalten.
| Query-Parameter | Bedeutung |
|---|---|
pageSize |
1 bis 100 Buchungen, Standard 100 |
cursor |
unveränderter nextCursor der vorherigen Buchungsseite |
Erfolg: 200, Body { "data": Booking[], "nextCursor": string | null, "pageSize": number }, Header
Cache-Control: private, no-store.
Sende nextCursor als cursor, bis nextCursor den Wert null hat. Die
Seitengröße darf zwischen Abrufen wechseln. Cursor gelten nur für Buchungen
desselben Workspaces. Ungültige oder fremde Cursor und ungültige Seitengrößen
liefern 400 / invalid_pagination. Es gibt keine Filterparameter.
Booking
Abschnitt betitelt „Booking“| Feld | Typ | Bedeutung |
|---|---|---|
id |
string | Buchungs-ID |
courseRunId |
string | zugehörige Kursdurchführung |
source |
string | admin, public_page, widget oder api |
status |
string | pending, confirmed, waitlisted, cancelled oder expired |
createdAt |
string (ISO-8601 UTC) | Anlage |
updatedAt |
string (ISO-8601 UTC) | letzte Änderung |
participant |
object, optional | nur mit Scope participants:read |
participant
Abschnitt betitelt „participant“| Feld | Typ | Bedeutung |
|---|---|---|
name |
string | Name der teilnehmenden Person |
email |
string | E-Mail-Adresse |
Ohne participants:read fehlt das Feld participant vollständig. Es wird
nicht als null geliefert.
Jede Fehlerantwort hat diese Form:
{ "error": { "code": "invalid_api_key", "message": "A valid API key is required" }}| HTTP | error.code |
Wann |
|---|---|---|
400 |
invalid_pagination |
Cursor ungültig oder pageSize außerhalb von 1–50 für Kurse bzw. 1–100 für Buchungen |
401 |
invalid_api_key |
Header fehlt, Key ungültig, widerrufen oder rotiert |
403 |
missing_scope |
Key gültig, Scope für den Endpunkt fehlt |
403 |
capability_not_in_plan |
Key gültig, aber der Plan des Workspace enthält die Entwickler-API nicht |
404 |
course_not_found |
Kurs-ID unbekannt, unveröffentlicht oder fremder Workspace |
405 |
method_not_allowed |
andere Methode als GET auf /api/public/v1/ |
429 |
rate_limit_exceeded |
mehr als 120 Anfragen in 60 Sekunden |
Bei 429 setzt die API den Header Retry-After: 60. Clients sollen so viele
Sekunden warten, bevor sie erneut senden.
Die beiden 403-Fälle brauchen verschiedene Reaktionen. missing_scope löst
du mit einem Key, der den fehlenden Scope enthält. capability_not_in_plan
bleibt bestehen, bis der Workspace auf einen Plan mit Entwickler-API wechselt;
ein neuer Key hilft nicht. Siehe
Plan und Abrechnung.
Limits und Caching
Abschnitt betitelt „Limits und Caching“| Limit | Wert |
|---|---|
| Rate-Limit | 120 Anfragen / 60 Sekunden / API-Key |
| Kurs- und Sitzungsseite | 50, Cursor nextCursor, Query pageSize und cursor |
| JSON-Antwort Kurse | höchstens 256 KiB |
| Bookings-Seite | Standard und Maximum 100, Cursor nextCursor, Query pageSize und cursor |
| Cache | Cache-Control: private, no-store |
| Methoden | nur GET; andere Methoden liefern 405 |
Gültige API-Keys haben jeweils ihr eigenes Budget. Eine gemeinsame
Client-IP führt nicht zu einem gemeinsamen Limit. Nur ungültige,
widerrufene oder abgelaufene Keys teilen sich ein zusätzliches IP-Budget von
600 Anfragen in 60 Sekunden. Bei dessen Überschreitung gilt ebenfalls 429,
mit der verbleibenden Wartezeit in Retry-After.
Das Rate-Limit ist von Auth-, Widget- und Widget-Analytics-Limits getrennt. Es gibt keine schreibenden öffentlichen REST-Endpunkte in Version 1.
Nicht Teil dieses Vertrags
Abschnitt betitelt „Nicht Teil dieses Vertrags“- Widget-API unter
/api/widget/v1/ - Dashboard-tRPC
- Google-Kalender-Synchronisation (Roadmap)
- native Zapier-App (Roadmap; signierte Webhooks sind der v1-Weg)
Kalenderfeed und Webhooks stehen in Webhooks und Kalender.

