Kurse, Menschen, Kursbelegungen, Fortschritt und Nachweise dieser Einrichtung — lesbar und teilweise schreibbar über HTTP.
BUILT7 Endpunkte, Schlüssel je Einrichtung mit Takt je Minute, OpenAPI-Beschreibung, signierte Webhooks — gebaut und geprüft; erste externe Anbindung als Projektleistung.
SCORM 1.2 / 2004 — Import
LIVE
Fremde Kurspakete hochladen (Verwaltung → Kurse → SCORM), sie laufen im eigenen Player; Fortschritt und Abschluss werden übernommen.
LTI 1.3 — Tool-Rolle
BUILT
Login (OIDC), Deep Linking und Notenrückgabe (AGS) implementiert; die erste Anbindung an ein externes LMS erfolgt als Projektleistung.
SAML-SSO
BUILT
Anmeldung über den Identitätsanbieter Ihres Hauses (SAML 2.0); Einrichtung je Kunde als Projektleistung.
Datenausgang in offenen Formaten
LIVE
Personen als JSON (Art. 15/20 DSGVO), Kurse als PowerPoint und PDF, Rechnungen als CSV.
Schlüssel
Ein Schlüssel wird in der Verwaltung ausgestellt, unter /admin/api-schluessel. Er wird genau einmal angezeigt und lässt sich nicht wiederherstellen — auch nicht vom Betreiber. Geht er verloren, wird ein neuer ausgestellt und der alte gesperrt.
Jeder Schlüssel gehört genau einer Einrichtung und sieht ausschließlich deren Daten. Einen Schlüssel über mehrere Einrichtungen hinweg gibt es nicht.
Nie in der Adresszeile (?api_key=…) — dort stünde er in jedem Zugriffsprotokoll, jedem Verlauf und jedem Verweis-Kopf. Diesen Weg gibt es deshalb nicht.
Rechte
Beim Ausstellen wird angekreuzt, was der Schlüssel darf. Ein Schlüssel ohne Kreuz darf nichts — die Liste ist eine weiße, keine schwarze.
courses:read
learners:read
learners:write
enrolments:read
enrolments:write
progress:read
credentials:read
Tempo
Jede Antwort trägt RateLimit-Limit, RateLimit-Remaining und RateLimit-Reset — auch die erfolgreiche. Damit lässt sich das eigene Tempo einstellen, bevor man gegen die Grenze läuft. Wird sie überschritten, kommt 429 mit Retry-After in Sekunden.
Seitenweise
Listen liefern limit (Vorgabe 50, höchstens 200) und offset. Die Antwort nennt total und has_more, damit man nicht bis zur ersten leeren Seite raten muss.
Endpunkte
GET/api/v1/coursescourses:read
List this organisation's courses
Returns the courses that belong to the organisation this key was issued for. `course_slug` is the value to use when enrolling someone — it is derived from the id and should be passed through rather than rebuilt.
Parameter
limitPage size, 1–200. Defaults to 50.
offsetRows to skip. Defaults to 0.
statusFilter by course status, e.g. `published`.
Felder der Antwort
id · course_slug · title · description · status · program_key · semester · created_at · updated_at
GET/api/v1/learnerslearners:read
List this organisation's people
Deliberately a narrow selection. The underlying record carries 45 columns including a career profile, a phone number and marketing preferences; none of that is exposed here. `is_test_account` travels with it so an importing system can tell test records from real ones.
Only name fields can be changed. Role, organisation and lecturer status are permissions, not master data — a key that could set them would be a key that grants access. Creating people is not possible through this API; use the invitation flow. A person outside this organisation answers 404, the same as one that does not exist.
An enrolment is the record created when a person starts a course; it carries the coarse progress (`current_checkpoint`, `completed_modules`). Consumption and billing figures (avatar minutes, tokens, cost) are the operator's, not the learner's, and stay out.
Parameter
limitPage size, 1–200. Defaults to 50.
offsetRows to skip. Defaults to 0.
learner_idOnly this person's enrolments.
course_slugOnly this course's enrolments.
Felder der Antwort
id · user_id · course_slug · status · current_checkpoint · completed_modules · started_at · last_active_at · completed_at
POST/api/v1/enrolmentsenrolments:write
Enrol a person in a course
Both the person and the course must belong to this organisation; either one outside it answers 404. Only this organisation's own courses can be enrolled into — that is exactly the set `GET /api/v1/courses` returns. Enrolling somebody twice is not an error: the existing enrolment comes back with 200 and `created: false`, so a nightly sync does not fill its log with failures.
Rumpf
learner_iduuid, required
course_slugstring, required — from GET /api/v1/courses
Felder der Antwort
id · user_id · course_slug · status · current_checkpoint · completed_modules · started_at · last_active_at · completed_at
GET/api/v1/progressprogress:read
Per-lesson progress
The FINE-grained progress, lesson by lesson. For how far somebody is in a course overall, read the enrolment instead — a system that confuses the two pulls a thousand rows for one number.
The signed credential itself (`vc_jwt`) is NOT returned — it belongs to the holder, not to an integrating system. Proctoring flags are a behavioural observation and stay out for the same reason. `qr_verification_code` lets anyone confirm a certificate publicly without handling the credential.
Unknown or revoked key. Deliberately indistinguishable — otherwise the error could be used to search for valid keys.
403
MISSING_SCOPE
The key does not carry the scope this endpoint needs.
429
RATE_LIMITED
More than `RateLimit-Limit` calls in one minute. `Retry-After` says how long to wait.
503
UPSTREAM_ERROR
The query did not run. Deliberately NOT an empty list — a nightly sync would otherwise delete your records.
Webhooks — die Gegenrichtung
Statt nachts zu fragen, ob jemand fertig ist, kann die Anlage Bescheid sagen. Ein Ziel wird in der Verwaltung angelegt (/admin/webhooks), mit einer https-Adresse und den Ereignissen, die es hören soll. Es bekommt ein Geheimnis, genau einmal angezeigt.
Ereignisse
enrolment.createdA person was enrolled in a course — through the API or inside the platform.
enrolment.completedA person finished a course.
credential.issuedA certificate was issued. The payload carries the public verification code, never the signed credential itself.
learner.updatedA person's name was changed through the API.
Antworten Sie mit 2xx, sobald Sie das Ereignis abgelegt haben — nicht erst, wenn Sie es verarbeitet haben. Nach zehn Sekunden gilt der Aufruf als gescheitert. Dann wird mit wachsenden Abständen nachgeliefert, insgesamt 26 Stunden lang; die Kennung in X-Universe-Delivery bleibt dabei gleich, damit Sie Wiederholungen erkennen.
Die Unterschrift prüfen
Unterschrieben wird `${t}.${rumpf}` mit HMAC-SHA256 über Ihr Geheimnis; t ist der Zeitpunkt in Sekunden. Verwerfen Sie Zustellungen, deren t mehr als 300 Sekunden von Ihrer Uhr abweicht — sonst ließe sich eine mitgeschnittene Zustellung später noch einmal einspielen.
Wichtig: über den rohen Rumpf, nicht über das geparste und neu serialisierte JSON — jede Umformung ändert die Bytes.
Maschinenlesbar
Dieselbe Beschreibung als OpenAPI 3.1: /api/v1/openapi.json — ohne Schlüssel abrufbar, damit eine Anbindung gebaut werden kann, bevor einer ausgestellt ist.