peritio API

v1 · Stand 04.11.2026 · openapi.json

Schnittstelle für die Verwaltung einer Einrichtung

Was eine Einrichtung in der Verwaltung pflegt, lässt sich großenteils auch von außen abfragen, vieles auch schreiben: Personen anlegen, Klassen füllen, Lernstand abholen, Zertifikate prüfen, Kursinhalte und Fragen eigener Kurse pflegen, Standorte, Betriebe und Stellen, Bewerbungs-Coach und Förderplanung abfragen. Die Funktionsübersicht ordnet die Routen nach Bereichen, und an jeder Route steht, welches Recht sie braucht. Dazu die Ausbildungsberufe mit ihrem Rahmenplan – Abschnitte, Positionen, Lernziele und zeitliche Richtwerte, so wie die Verordnung sie ordnet. Eine Adresse für alle Einrichtungen – https://api.peritio.de/v1 – der Schlüssel bestimmt, um welche es geht.

Rechts steht der Playground: Jede Route lässt sich mit „Im Playground öffnen“ dorthin übernehmen und direkt ausprobieren.

Erste Schritte

  1. Schlüssel anlegen

    In der Verwaltung der Einrichtung unter API. Ein Schlüssel gehört zu einem Zweck, nicht zu einer Person – etwa „Nachtabgleich Personalsystem“. Beim Anlegen wählen Sie je Bereich, was er darf: nichts, lesen oder schreiben.

    Der Schlüssel ist genau einmal zu sehen. Danach steht in der Verwaltung nur noch sein Anfang. Verloren heißt: neu anlegen.

  2. Schlüssel prüfen

    Die Route /v1/ping braucht kein Recht und sagt, welche Einrichtung hinter dem Schlüssel steht und was er darf.

    curl -H "Authorization: Bearer peritio_live_ihrkuerzel_…" \
         https://api.peritio.de/v1/ping
  3. Daten holen

    curl -H "Authorization: Bearer peritio_live_ihrkuerzel_…" \
         "https://api.peritio.de/v1/personen?limit=10"

    Oder gleich rechts im Playground, ohne Terminal.

Grundlagen

Anmeldung

Der Schlüssel steht in der Kopfzeile:

Authorization: Bearer peritio_live_<kürzel>_<zeichen>

Schlüssel mit peritio_test_… gehören zur Testumgebung derselben Einrichtung – gut zum Ausprobieren, damit im Ernstfall nichts verrutscht. Ohne gültigen Schlüssel: 401. Ohne Recht für den Bereich: 403. Die API läuft nur über HTTPS.

Antworten

Immer JSON. Ein Datensatz steht direkt in der Antwort, Listen stehen unter daten. Fehler stehen unter fehler: ein code zum Auswerten, ein Satz für Menschen, bei Eingabefehlern das feld und die Kennung der Anfrage für Rückfragen:

{
  "fehler": {
    "code": "nicht_gefunden",
    "text": "Diese Person gibt es in dieser Einrichtung nicht.",
    "feld": null,
    "anfrage_id": "9c79193a240a7108"
  }
}

Codes: nicht_angemeldet, paket_fehlt, recht_fehlt, nicht_gefunden, ungueltig, konflikt, zu_viele_anfragen, serverfehler.

Zeitpunkte (Felder auf _am) stehen in ISO 8601 mit Zeitzone, etwa 2026-09-18T14:30:00+00:00; Tage ohne Uhrzeit als JJJJ-MM-TT.

Listen und Cursor

Listen kommen in Abschnitten: ?limit= (bis 200, Standard 50) und ?cursor=. Die Antwort führt naechster_cursor mit; ist er null, war das die letzte Seite.

Der Cursor hält die zuletzt gelieferte Kennung, keine Position. Kommt während des Durchlaufs ein Datensatz hinzu, überspringt er deshalb nichts.

Grenzen

Je Schlüssel gilt eine Grenze pro Minute (Standard 120); die Antwort führt sie in X-RateLimit-Limit und X-RateLimit-Remaining mit. Darüber: 429.

Schreibende Anfragen dürfen eine Kopfzeile Idempotency-Key tragen. Derselbe Schlüssel liefert 24 Stunden lang dieselbe Antwort, ohne die Arbeit zu wiederholen.

Die API setzt das Zusatzpaket „API-Zugang“ voraus; ohne Paket antwortet sie mit 402.

Zwei Adressen, eine API

https://api.peritio.de/v1/… gilt für alle Einrichtungen; der Schlüssel bestimmt, welche gemeint ist. Alternativ geht auch die eigene Adresse der Einrichtung: https://<kürzel>.peritio.de/api/v1/… – dort muss der Schlüssel zu genau dieser Einrichtung gehören.

Was die API nicht tut

Prüfungsinhalte gibt sie nur zu eigenen Kursen heraus: Fragen samt Lösungen liest und schreibt nur ein Schlüssel mit dem Recht fragen, und bei Kursen aus dem globalen Katalog antwortet sie mit 409, auch beim Lesen. Den Text einer bestehenden Frage ändert sie nicht; eine Frage lässt sich stilllegen und neu anlegen. Nachtragen lassen sich ihre Einordnung, Kontext, Erklärung und die Fassung in einfacher Sprache. Kurse, Seminare, Folien und Material eigener Kurse lassen sich als JSON pflegen; Kursdateien selbst nimmt die API nicht an. Außerhalb des Bereichs fragen erscheinen Fragetexte nur an zwei Stellen, und beide haben ein eigenes Recht: in der Kursstatistik (Urteil je Frage) und bei den Rückmeldungen Lernender.

Kurse des globalen Katalogs gelten für alle Einrichtungen, deshalb pflegt sie die Plattform. Über die Schnittstelle geht das nur mit einem Schlüssel von peritio selbst, dem die Katalogpflege eingeräumt ist. GET ping nennt das als katalogpflege, GET kurse/{id} je Kurs als pflegbar. Für jeden anderen Schlüssel bleibt es beim 409.

Ausbildungsberufe von peritio gibt sie heraus, ändert sie aber nicht: Ihr Rahmenplan folgt einer Verordnung und gilt in jedem Haus gleich. Wer einen Beruf zuschneiden will, legt mit vorlage_beruf_id eine eigene Kopie an und führt sie danach selbst – ein Schreibversuch am Original endet mit 409.

Sie reicht nie über die eigene Einrichtung hinaus. Schlüssel legt sie nicht an und entsperrt sie nicht, Branding, Buchungen und Rechnungen liest sie nur. Admin-Konten ändert nur ein Schlüssel mit einrichtung: schreiben.

Jeder Aufruf wird protokolliert (Zeit, Route, Status, Schlüssel), damit nachvollziehbar bleibt, wer was geändert hat.

PHP-SDK

Wer aus PHP anbindet, muss Kopfzeilen, Cursor und Fehlerformen nicht selbst nachbauen. Das Paket peritio/api-client kennt jede Route dieser Seite und braucht nur PHP 8.1 mit ext-curl.

Einbinden

peritio-php-sdk.zip herunterladen (41 KB)

Entpacken, etwa nach lib/peritio-php-sdk, und in der eigenen composer.json einbinden:

"autoload": {
    "psr-4": { "Peritio\\Api\\": "lib/peritio-php-sdk/src/" }
}

Ohne Composer genügt ein eigener Autoloader auf src/. Die README im Archiv beschreibt alle Optionen.

use Peritio\Api\Client;

$peritio = new Client(getenv('PERITIO_API_KEY'));
$peritio->ping();

Den Schlüssel in eine Umgebungsvariable legen, nicht in den Code.

Abgleich und Listen

$person = $peritio->personen()->setzenNachFremdId('P-4711', [
    'vorname'  => 'Mia',
    'nachname' => 'Sander',
    'email'    => '[email protected]',
]);
$peritio->klassen()->aufnehmen(12, $person['id']);

foreach ($peritio->personen()->alle(['klasse' => 12]) as $p) {
    echo $p['nachname'], "\n";
}

alle() blättert selbst weiter; liste() liefert eine Seite samt naechsterCursor.

Themen und Zielstufen

Das Thema sagt, worum es geht; die Zielstufe, bis wohin es sitzen muss – 1 erinnern bis 6 erschaffen. Erst beides zusammen macht aus Ergebnissen eine Aussage.

$thema = $peritio->themen()->anlegen([
    'titel'     => 'Auskunft am Telefon',
    'zielstufe' => 3,
]);

// "noch nicht entschieden" ist eine gültige Aussage
$peritio->themen()->aendern($thema['id'], ['zielstufe' => null]);

// Themengruppen ordnen dagegen Kurse - die gueltigen Werte fuer themengruppe_id
foreach ($peritio->themen()->gruppen() as $gruppe) {
    echo $gruppe['id'], ' ', $gruppe['titel'], "\n";
}

Themen sind nicht nach Einrichtungen getrennt: Dasselbe Thema trägt oft die Fragen mehrerer Kurse, auch verschiedener Häuser. Wer hier schreibt oder löscht, wirkt überall – genau wie in der Verwaltung.

Fallszenarien

Ein Szenario ist ein Graph aus Lagen und Entscheidungen. Vor dem Schreiben wird er gelesen: unerreichbare Szenen, Lagen ohne Ausgang und Fälle, die sich nicht gut ausgehen lassen, enden mit 422 – und zwar mit allen Mängeln auf einmal.

$peritio->kurse()->szenarioSetzen($kursId, $seminarId, [
    'schluessel' => 'anruf-zeitdruck',
    'titel'      => 'Der Anruf unter Zeitdruck',
    'thema'      => 'Auskunft am Telefon',
    'start'      => 'anruf',
    'szenen'     => [
        'anruf' => [
            'text'     => 'Am Telefon verlangt jemand Auskunft über einen Kollegen.',
            'optionen' => [
                ['text' => 'Rückruf anbieten', 'ziel' => 'gut'],
                ['text' => 'Auskunft geben',   'ziel' => 'schlecht'],
            ],
        ],
        'gut'      => ['text' => 'Sie rufen zurück.', 'ende' => 'gut',
                       'lehre' => 'Ein Rückruf klärt, wer fragt.'],
        'schlecht' => ['text' => 'Die Auskunft ging an eine fremde Person.',
                       'ende' => 'schlecht', 'lehre' => 'Am Telefon sieht man niemanden.'],
    ],
]);

Erkannt wird am schluessel: Derselbe Aufruf legt an und schreibt fort. Szenen behalten dabei ihre Kennung, solange ihr Schlüssel bleibt – gegangene Durchgänge bleiben lesbar.

Spickzettel und Werkstatt

Karten und Werkstatt-Vorlagen gehören zusammen und werden zusammen gesetzt – wie in der Kursdatei. Eine weggelassene Liste bleibt unverändert, eine leere löscht ihren Teil.

$peritio->kurse()->spickzettelSetzen($kursId, [
    'karten' => [
        ['bereich' => 'HTML', 'titel' => 'Überschriften', 'sprache' => 'html',
         'text' => 'h1 bis h6 gliedern die Seite.', 'code' => '

Titel

', 'thema' => 'Aufbau einer Seite'], ], ]);

Steht bei einer Karte ein thema, erscheint sie im Training als Hilfe zu Fragen dieses Themas. Ein unbekannter Titel ist kein Fehler – die Karte steht dann nur auf der Nachschlageseite.

Unterlagen und Verweise

Am Kurs oder am Seminar liegen Dateien, Verweise ins Netz und Podcasts – für Lernende dasselbe: etwas zum Nachschlagen neben dem Seminar.

$peritio->kurse()->materialHochladen($kursId, 'merkblatt.pdf', $inhalt, [
    'seminar_id' => $seminarId,
    'titel'      => 'Merkblatt Erste Hilfe',
]);

$peritio->kurse()->materialVerweis($kursId, 'Gesetzestext',
    'https://www.gesetze-im-internet.de/…');

// Titel, Platz und "einfache Sprache" aendern, ohne die Datei neu zu senden
$peritio->kurse()->materialAendern($kursId, $materialId, [
    'titel'       => 'Merkblatt Erste Hilfe (Stand 2026)',
    'reihenfolge' => 20,
]);

Verweise nehmen nur http und https an. Beim Ändern der Angaben bleibt die Datei unberührt, auch ihr Dateiname. Den Inhalt tauscht ein eigener Aufruf aus – Kennung und Name bleiben, und damit jede Folie und jede Aufgabe, die auf die Datei zeigt. Der neue Inhalt muss zur Endung der vorhandenen Datei passen.

$peritio->kurse()->materialDateiErsetzen($kursId, $materialId, file_get_contents('schema-neu.png'));

Kursbild

Das Bild im Kopf des Kurses und auf seiner Kachel: PNG, JPEG oder WebP, im Verhältnis 8:3 mindestens 1920 × 720 Pixel. Was davon abweicht, wird mittig beschnitten; ein kleineres Bild wird mit 422 abgewiesen.

$peritio->kurse()->bildSetzen($kursId, file_get_contents('kopf.jpg'));
$peritio->kurse()->bildLoeschen($kursId);   // zurück zum Vorgabebild

Fragen und Antworten

Prüfungsinhalte haben ein eigenes Recht: Wer Folien und Material pflegt, liest den Fragenkatalog nicht mit. Angelegt wird im Aufbau der Kursdateien, geprüft von derselben Klasse – die Aufgabenform ergibt sich aus dem, was dasteht.

$peritio->fragen()->anlegen($kursId, $seminarId, [
    [
        'text'      => 'Welche Klausel filtert Zeilen?',
        'antworten' => ['WHERE', 'SELECT', 'ORDER BY', 'LIMIT'],
        'thema'     => 'SQL-Grundlagen',
        'stufe'     => 2,
    ],
]);

// Stilllegen statt aendern: An einer Frage haengen abgegebene Antworten
$peritio->fragen()->aktivSetzen($kursId, $frageId, false);

Text und Antworten einer Frage stehen fest, sobald sie da ist. Eine geänderte Frage ist eine andere Frage, und die Ergebnisse der alten wären nicht mehr zu deuten; der Weg ist stilllegen und neu anlegen. Löschen geht nur, solange niemand geantwortet hat – sonst kommt ein 409.

Die Fassung in einfacher Sprache ist dieselbe Frage in anderen Worten. Sie lässt sich beim Anlegen mitgeben (text_leicht, kontext_leicht, antworten_leicht) und jederzeit nachtragen, auch wenn schon geantwortet wurde. Die Antworten werden über ihren genauen Wortlaut zugeordnet; stimmt einer nicht, kommt ein 422, und nichts wird geschrieben.

$peritio->fragen()->leichtSetzen($kursId, $frageId, [
    'text_leicht'      => 'Welches Wort filtert Zeilen?',
    'antworten_leicht' => [
        ['antwort' => 'ORDER BY', 'leicht' => 'ORDER BY (sortiert)'],
    ],
]);

Ebenso lassen sich Einordnung und Begleittexte einer bestehenden Frage ändern: Thema, Stufe, Kontext und die Erklärung, die im Training nach der Antwort erscheint. Nichts davon ändert, was gefragt und was als richtig gewertet wird.

$peritio->fragen()->nachpflegen($kursId, $frageId, [
    'thema'      => 'SQL-Grundlagen',
    'stufe'      => 3,
    'erklaerung' => 'WHERE filtert Zeilen, bevor sortiert und begrenzt wird.',
]);

Das Bild einer Hotspot-Aufgabe muss im Material des Kurses liegen, bevor die Aufgabe angelegt wird; sonst kommt ein 422.

Auch die Sachaufgabe gehört zu den Aufgabenformen: ein Text mit markierten Angaben und dazu {"sachaufgabe": {"frage": "…", "ergebnis": 7}} im Antwortteil.

Der SQL-Baukasten braucht nur die Musterlösung: {"sqlbaukasten": {"loesung": "SELECT name FROM produkte WHERE preis > 100;"}}. Aus ihr entstehen der Vergleich und das Angebot an Bausteinen. Erlaubt sind SELECT, FROM, WHERE mit Vergleichen, AND und OR sowie ORDER BY; alles andere wird mit 422 abgewiesen. Dazu können stufe (1 bis 3), ablenker (spalten, tabellen, werte), spaltenfolge, ziele und bereiche kommen. bereiche legt feste Bereiche auf die Arbeitsfläche: true für alle angebotenen Klauseln oder eine Liste wie ["select", "from"]. auch nennt weitere Abfragen, die als richtig gelten.

Folien einzeln ändern

Ein ganzer Kursabgleich gleicht alle Folien eines Seminars ab. Folien, die sich wiedererkennen lassen – gleicher Inhalt oder gleiche Art und gleicher Titel –, bleiben dieselben und behalten den Folienfortschritt der Lernenden; nur neu angelegte gelten als ungesehen, und was im Satz fehlt, entfällt. Für eine Korrektur an einer Folie ist das trotzdem zu grob, dafür gibt es eigene Wege.

$folien = $peritio->kurse()->folien($kursId, $seminarId);

$peritio->kurse()->folieAendern($kursId, $folien['daten'][0]['id'], [
    'text' => 'Erst melden, dann handeln.',
]);

// Ausblenden statt löschen - die Folie bleibt für später erhalten
$peritio->kurse()->folieAendern($kursId, $alteFolie, ['aktiv' => false]);

$peritio->kurse()->folienOrdnen($kursId, $seminarId, [12, 10, 11]);

Aufgaben in Einführungsfolien

Eine Einführung soll die Lernenden etwas tun lassen. Eine Folie nimmt deshalb eine Aufgabe an: Text und Antworten wie bei einer Frage, dazu eine Kennung, die beim Überarbeiten gleich bleibt. Erlaubt sind die Formen, die der Browser selbst wertet – Auswahl, Mehrfachauswahl, Reihenfolge, Richtig/Falsch, Lückentext, Markieren, Zuordnen, Gruppieren, Diagnose, Berechnung und Sachaufgabe.

$peritio->kurse()->folieAnlegen($kursId, $seminarId, [
    'titel'   => 'Was fehlt im Vertrag?',
    'text'    => '

Sieh dir den Vertrag an.

', 'aufgabe' => [ 'kennung' => 'f01-pflichtangaben', 'text' => 'Welche zwei Pflichtangaben fehlen?', 'antworten' => ['mehrfach' => ['richtig' => ['Probezeit', 'Vergütung'], 'falsch' => ['Lieblingsfarbe']]], 'erklaerung' => 'Probezeit und Vergütung gehören in jeden Ausbildungsvertrag.', 'bezug' => ['bild' => 'vertrag.svg', 'titel' => 'Vertrag ansehen'], ], ]);

Die Aufgabe steht am Ende des Folientexts oder an der Stelle von {{aufgabe}}. aufgabe_leicht trägt dieselbe Aufgabe in einfacher Sprache; fehlt sie, steht in der leichten Fassung der Folie die Aufgabe im Standardwortlaut. "aufgabe": null nimmt sie heraus. Für Lernende gilt die Folie erst als erledigt, wenn die Aufgabe abgegeben ist.

Gespeichert wird die Aufgabe als Markup im Folientext. Beim Lesen kommt deshalb der Text mit dem Markup zurück, nicht das Feld – so bleibt lesen, ändern, zurückschicken verlustfrei.

Herausforderung

Die Herausforderung eines Kurses prüft, ob das Gelernte in einer neuen Lage trägt: Transferkarten mit einem Prinzip und Aufgaben dazu. Karten und Aufgaben werden an ihrem schluessel wiedererkannt. Was im Rumpf fehlt, wird stillgelegt und nicht gelöscht. Recht: fragen.

$stand = $peritio->fragen()->herausforderung($kursId);
$stand['karten'][0]['aufgaben'][0]['hinweis'] = 'Wer wird Eigentümer der Ware?';

$peritio->fragen()->herausforderungSetzen($kursId, ['probe' => true] + $stand);     // nur prüfen
$peritio->fragen()->herausforderungSetzen($kursId, ['freigeben' => true] + $stand); // schreiben und zeigen

Jede Aufgabe braucht eine Lage, eine Frage und Optionen; genau eine Option trägt fehlue – die Antwort, zu der greift, wer das Prinzip nicht übertragen hat.

Desktop-Simulation

Eine Welt der Desktop-Simulation ist ein Baum aus Bildschirmen und Übergängen, dazu Aufgaben mit Start und Ziel. Welten gehören zu keinem Kurs und gelten für alle Einrichtungen. Welche es gibt, zeigt desktopwelten() jedem Schlüssel mit dem Recht inhalte; Inhalt lesen und schreiben geht nur mit Katalogpflege.

$welt = $peritio->kurse()->desktopwelt('win11-netzwerk');
$peritio->kurse()->desktopweltSetzen('win11-netzwerk', ['probe' => true] + $welt);

Vor dem Schreiben wird geprüft, was beim Durchklicken niemand findet: Jeder Knoten muss vom Start aus erreichbar sein, und das Ziel jeder Aufgabe von ihrem Start.

Rahmenplan lesen

Der Ausbildungsrahmenplan kommt als Baum: Abschnitte, darin Positionen, darin Lernziele und Zeiten. Die Verschachtelung ist die Gliederung der Verordnung.

$plan = $peritio->berufe()->rahmenplan(335);

foreach ($plan['abschnitte'] as $abschnitt) {
    echo $abschnitt['kuerzel'], ' ', $abschnitt['titel'], "\n";

    foreach ($abschnitt['positionen'] as $position) {
        echo '  ', $position['kuerzel'], ' ', $position['titel'],
             ' (', count($position['lernziele']), " Lernziele)\n";
    }
}

Welche Berufe ein Fachbereich ausbildet, steht unter berufe()->jeFachbereich($id); gesetzt wird die Liste mit fachbereichSetzen($id, [1, 2]).

Fehler als Ausnahmen

Jeder Code wird zu einer eigenen Ausnahme unter Peritio\Api\Fehler: RechtFehlt, NichtGefunden, Konflikt, Ungueltig (mit $e->feld) und weitere. Jede trägt $e->anfrageId.

try {
    $peritio->personen()->loeschen($id);
} catch (Fehler\Konflikt) {
    $peritio->personen()->loeschen($id, mitErgebnissen: true);
}

Wiederholen ohne Doppel

Anlegen und Ändern bekommen automatisch einen Idempotency-Key. Nach einem Netzfehler oder 5xx wiederholt die SDK deshalb gefahrlos, bei 429 wartet sie Retry-After ab.

Wer ein Importskript neu starten können will, gibt einen eigenen Schlüssel mit: anlegen($daten, idempotenz: 'import-P-4711').

Funktionsübersicht

Dienst

Ohne Recht aufrufbar.

GET /v1/ping

Schlüssel prüfen: Einrichtung, Rechte, Zeit.

Der erste Aufruf für einen neuen Schlüssel - er braucht kein Recht.

Antwort: Einrichtung, Name und Umgebung des Schlüssels, die Rechte je Bereich.

GET /v1/openapi.json

Diese Schnittstelle als OpenAPI 3.1.

Antwort: Die Beschreibung, aus der auch diese Seite entsteht.

GET /v1/standorte

Reha-Einrichtungen im Umkreis finden: Berufsbildungswerke (bbw), Berufsförderungswerke (bfw), Berufliche Trainingszentren (btz), RPK-Einrichtungen (rpk) und Phase-II-Einrichtungen (mbr).

Öffentliche Verzeichnisdaten der Verbände; jeder gültige Schlüssel darf lesen. Entfernungen sind Luftlinie. Koordinaten: OpenStreetMap (ODbL), Postleitzahlen: GeoNames (CC BY 4.0).

Parameter in der Adresse

plz
Fünfstellige Postleitzahl als Mittelpunkt.
ort
Oder ein Ortsname (Mittel aller Postleitzahlen des Orts). Gibt es den Ort mehrfach, kommt 400 mit den Kandidaten im Text.
land
Bundesland, um einen mehrdeutigen Ortsnamen festzulegen (z. B. Schleswig-Holstein).
lat
Oder Breitengrad (zusammen mit lon).
lon
Längengrad.
umkreis
Radius in km, 1 bis 500 (Standard 50).
art
Kommagetrennt: bbw, bfw, btz, rpk, mbr (Standard: alle).
beruf
Nur Einrichtungen mit diesem Beruf (Adresse des Berufs, z. B. fachinformatiker-systemintegration).
wohnen
1: nur Einrichtungen mit Wohnmöglichkeit.
alle
1: jeden Standort einzeln statt je Einrichtung nur den nächsten.
limit
Wie viele Treffer (1 bis 200, Standard 20).

Antwort: mittelpunkt (plz, ort, lat, lon, umkreis_km) und daten, nach Entfernung sortiert: je Treffer entfernung_km (Luftlinie), einrichtung (id, art, name, traeger, telefon, email, web, ansprech, wohnen mit status, begleitet, angebotsname, arten, hinweis, quelle) und standort (id, hauptsitz, name, strasse, plz, ort, land, telefon, lat, lon, genau: adresse oder plz).

GET /v1/einrichtungen/{id}

Eine Reha-Einrichtung mit allen Standorten und Berufen.

Antwort: Wie einrichtung bei standorte, dazu standorte (Liste) und berufe (slug, titel, form, bezeichnung, beleg).

GET /v1/kammer-auskunft

Welche Stelle ist für eine Ausbildung zuständig, wer prüft, welches Verfahren gilt? Eingang sind Beruf und der Ort der Ausbildungsstätte.

Jeder gültige Schlüssel darf lesen. Die Route antwortet nie zu einer Person: Steht im Ausbildungsvertrag eine andere Stelle, gilt die. Ohne belegte Regel kommt nicht_ermittelt mit den offiziellen Findern unter ausweichwege - es wird keine Kammer geraten. "fehlt" nennt im Klartext, welche Angabe die Auskunft eindeutig machen würde. Quellen: Gemeinden und amtliche Gemeindeschlüssel Statistisches Bundesamt (dl-de/by-2-0), Postleitzahl je Ort OpenStreetMap (ODbL), Postleitzahl je Kreis GeoNames (CC BY 4.0).

Parameter in der Adresse

beruf
Adresse des Berufs (z. B. fisi) oder seine Id. Welche beantwortet werden, sagt kammer-berufe.
plz
Fünfstellige Postleitzahl der Ausbildungsstätte, führende Null eingeschlossen.
ags
Oder der achtstellige amtliche Gemeindeschlüssel, wenn er bekannt ist.
betrieb
Betriebsart: alle (Standard), handwerk, nichthandwerk, oeffentlich, landwirtschaft. In einem Betrieb des Handwerks ist nach § 71 Absatz 7 BBiG die Handwerkskammer zuständig, auch bei einem kaufmännischen Beruf - ohne diese Angabe bleibt die Auskunft unvollständig.
weg
Ausbildungsweg: alle (Standard), regulaer, umschulung, extern, paragraf66.
teil
Prüfungsteil für das Verfahren: teil1, teil2, zwischen, abschluss.
saison
Prüfungssaison für das Verfahren, z. B. sommer-2027.

Antwort: ergebnis (belegt, recherchiert, rueckfrage, nicht_ermittelt) fasst zusammen. Dazu status (geografie, fachlich, pruefstelle, verfahren) einzeln, ort beziehungsweise kreis, kandidaten (je mit art: gemeinde, kreis oder stelle), zustaendige_stelle und pruefende_stelle (schluessel, name, art, website, pruefungsportal, regel, quelle), pruefstelle_kandidaten, verfahren, weil_betrieb, begruendung, fehlt und ausweichwege. weil_betrieb steht nur, wenn erst die Betriebsart die Stelle umgelenkt hat, und nennt den Grund im Klartext.

GET /v1/kammer-berufe

Die Berufe, zu denen kammer-auskunft eine Zuordnung kennt.

Die Plattform kennt mehr Berufe; hier stehen nur die mit mindestens einer Gebietsregel.

Antwort: Je Beruf id, slug und titel.

Katalog

Kurse, Blöcke und Seminare lesen; freischalten und entziehen

GET /v1/kurse katalog: lesen

Kurse der Einrichtung: eigene und freigeschaltete aus dem Katalog.

Parameter in der Adresse

limit
Wie viele Einträge (1 bis 200, Standard 50).
cursor
Weiterlesen ab dieser Kennung; steht als naechster_cursor in der Antwort.

Antwort: Je Kurs Titel, Kurzbeschreibung, Zahl der Seminare und Fragen, Herkunft (eigen oder katalog).

GET /v1/kurse/{id} katalog: lesen

Ein Kurs mit Beschreibung, Lerneinheiten und Vorgänger.

Antwort: Der Kurs; 404, wenn es ihn nicht gibt oder er nicht freigeschaltet ist.

GET /v1/kurse/{id}/seminare katalog: lesen

Blöcke und Seminare des Kurses.

Fragen und Antworten gibt die API nicht heraus - Inhalte werden über Kursdateien gepflegt.

Antwort: Je Seminar Titel, Zahl der Fragen, Bestehensgrenze und Dauer des Examens.

PUT /v1/kurse/{id}/freischaltung katalog: schreiben

Einen Kurs des globalen Katalogs für die Einrichtung freischalten.

Antwort: 409, wenn es ein eigener Kurs ist - der ist immer sichtbar.

DELETE /v1/kurse/{id}/freischaltung katalog: schreiben

Die Freischaltung wieder entziehen.

Antwort: Der Kurs verschwindet aus dem Angebot; Ergebnisse bleiben erhalten.

Kursinhalte

eigene Kurse, Blöcke, Seminare, Folien und Material lesen; anlegen, ändern, löschen – ohne Fragen

GET /v1/kurse/{id}/inhalt inhalte: lesen

Kurs, Blöcke, Seminare und Einführungsfolien im Aufbau einer Kursdatei.

Guter Ausgangspunkt: lesen, ändern, zurückschicken.

Antwort: Objekt mit kurs und bloecke - dieselbe Form, die PUT kurse/{id}/inhalt annimmt. Ohne Fragen.

GET /v1/kurse/{id}/material inhalte: lesen

Material des Kurses und seiner Seminare.

Antwort: Je Datei Kennung, Seminar, Titel, Dateiname, Typ und Größe.

POST /v1/kurse inhalte: schreiben

Einen eigenen Kurs der Einrichtung anlegen, auf Wunsch gleich mit Blöcken und Seminaren.

Fragen und Antworten nimmt die API nicht an - ein Rumpf mit "fragen" wird mit 422 abgewiesen. Kursdateien selbst (PHP) werden nie über das Netz angenommen.

Felder im Rumpf

kurs
Pflicht: Objekt mit titel; dazu kurzbeschreibung, beschreibung, lerneinheiten, le_minuten, einfache_sprache, training_frei, zertifikat_ort, voraussetzungen und weitere Angaben wie bei GET kurse/{id}/inhalt.
bloecke
Optional, wie bei PUT kurse/{id}/inhalt.
katalog
Optional, im Objekt kurs: true legt den Kurs im globalen Katalog an statt als eigenen Kurs. Nur mit einem Schlüssel des Hauptmandanten, dem die Katalogpflege eingeräumt ist; sonst 403.

Antwort: 201 mit dem Kurs im Aufbau von GET kurse/{id}/inhalt.

PATCH /v1/kurse/{id} inhalte: schreiben

Angaben eines eigenen Kurses ändern; nicht genannte Felder bleiben.

Katalogkurse pflegt die Plattform. Über die Schnittstelle geht das an allen Routen für Kursinhalte, Fragen, Szenarien und Spickzettel nur mit einem Schlüssel des Hauptmandanten, dem die Katalogpflege eingeräumt ist - GET ping nennt das als katalogpflege, GET kurse/{id} je Kurs als pflegbar.

Felder im Rumpf

titel
Optional.
kurzbeschreibung
Optional.
beschreibung
Optional.
lerneinheiten
Optional.
le_minuten
Optional.
einfache_sprache
Optional.
training_frei
Optional.
spick_im_training
true: Lernende dürfen im Training den Spickzettel öffnen.
leitung
Kennung des Kontos, das den Kurs leitet, oder null. Das Konto muss zur Einrichtung gehören.
voraussetzungen
Liste der Kurs-IDs, die abgeschlossen sein müssen, bevor der Kurs startet; [] hebt alle auf. Kreise werden abgewiesen.
vorgaenger_id
Veraltet: setzt genau eine Voraussetzung. Beim Lesen die erste aus voraussetzungen.
themengruppe_id
Kennung der Themengruppe, null löst die Zuordnung.
fuer_rolle
Für wen der Kurs gilt: "" (alle), ausbilder, admin, plattform.
examen_wartezeit_wert
Wartezeit nach einem nicht bestandenen Examen, 0 = keine.
examen_wartezeit_einheit
minuten, stunden oder tage.
examen_durchsicht
true: die Ergebnisseite nennt die verfehlten Fragen (ohne Lösung).
nachweis_art
Behördlicher Nachweis, der den Kurs freigibt; "" hebt ihn auf.
examen_aufsicht
true: Examen beginnen erst nach Freigabe durch die Begleitung.
status
Stand des Kurses: importiert, entwurf, freigegeben oder gesperrt. Gelernt wird nur in einem freigegebenen Kurs; die übrigen bleiben sichtbar und behalten ihre Zuweisungen.

Antwort: Der Kurs; 409 bei Kursen aus dem globalen Katalog.

DELETE /v1/kurse/{id} inhalte: schreiben

Einen eigenen Kurs löschen.

Antwort: 409, sobald Fragen, Durchgänge oder Zertifikate daran hängen - dann nur in der Verwaltung.

PUT /v1/kurse/{id}/bild inhalte: schreiben

Das Kursbild setzen - das Bild im Kopf des Kurses und auf seiner Kachel.

Aus dem Original entstehen fünf Breiten in AVIF, WebP und JPEG. Ein vorhandenes Bild wird ersetzt. GET kurse/{id}/inhalt nennt das Bild unter kurs.bild (null ohne eigenes Bild).

Felder im Rumpf

inhalt_base64
Pflicht: PNG, JPEG oder WebP, Base64-kodiert. Im Verhältnis 8:3 mindestens 1920 × 720 Pixel; was davon abweicht, wird mittig beschnitten.

Antwort: id und bild mit breite, hoehe und url der größten Fassung. 422, wenn das Bild zu klein oder kein Bild ist.

DELETE /v1/kurse/{id}/bild inhalte: schreiben

Das Kursbild entfernen; der Kurs zeigt wieder das Vorgabebild.

Antwort: 404, wenn der Kurs kein eigenes Bild hat.

PUT /v1/kurse/{id}/inhalt inhalte: schreiben

Kurs, Blöcke, Seminare und Folien eines eigenen Kurses angleichen.

Erkannt wird über id, sonst über den Titel - ein geänderter Titel ohne id legt neu an. Folien werden abgeglichen, nicht ersetzt: Eine Folie mit unverändertem Inhalt oder mit gleicher Art und gleichem Titel bleibt dieselbe und behält den Folienfortschritt der Lernenden, auch an anderer Stelle. Nur neu angelegte Folien gelten als ungesehen; was im Satz fehlt, entfällt. Bei einem Fehler bleibt alles beim Alten.

Felder im Rumpf

kurs
Optional: Angaben zum Kurs wie bei PATCH kurse/{id}.
bloecke
Liste; je Block id oder titel, reihenfolge und seminare. Je Seminar id oder titel, vorspann, vorspann_leicht, fragen_examen, prozent_pass, examen_dauer, vorgaenger_id, aktiv, einfuehrung_pflicht und folien. Je Folie art (text, bild, audio, video, podcast), titel, text, titel_leicht, text_leicht, bei Medien material mit dem Dateinamen aus GET kurse/{id}/material, bei podcast das Feld podcast: {"id": 5, "key": "…"} oder die Player-Adresse. Dazu aufgabe und aufgabe_leicht wie bei POST …/folien.

Antwort: Der neue Stand und ein bericht: neu angelegt, geändert, folien_ersetzt (Seminare, deren Folien sich geändert haben), folien (je Seminar: geblieben, geaendert, angelegt, entfernt), nicht genannte Blöcke und Seminare (die bleiben stehen).

DELETE /v1/kurse/{id}/bloecke/{block_id} inhalte: schreiben

Einen Block samt Seminaren löschen.

Antwort: 409, sobald Fragen oder Durchgänge daran hängen.

DELETE /v1/kurse/{id}/seminare/{seminar_id} inhalte: schreiben

Ein Seminar samt Folien und Material löschen.

Antwort: 409, sobald Fragen oder Durchgänge daran hängen - dann mit "aktiv": false ausblenden.

GET /v1/kurse/{id}/seminare/{seminar_id}/folien inhalte: lesen

Die Einführungsfolien eines Seminars, auch stillgelegte.

Antwort: Je Folie id, art, titel, text (jeweils auch in leichter Sprache), material, aktiv und reihenfolge.

POST /v1/kurse/{id}/seminare/{seminar_id}/folien inhalte: schreiben

Eine Folie am Ende der Einführung anlegen.

Gespeichert wird die Aufgabe als Markup im Folientext. Beim Lesen kommt deshalb der Text mit dem Markup zurück, nicht das Feld aufgabe - GET und PUT bleiben verlustfrei. Die Folie gilt für Lernende erst als erledigt, wenn die Aufgabe abgegeben ist.

Felder im Rumpf

art
text, bild, audio, video oder podcast (Standard text).
titel
Pflicht.
text
Pflicht bei art text.
titel_leicht
Optional, leichte Sprache.
text_leicht
Optional.
material
Bei Medien: Dateiname aus GET kurse/{id}/material.
podcast
Bei art podcast: {"id": 5, "key": "…"} oder die Player-Adresse.
aufgabe
Optional: eine Aufgabe, die die Lernenden auf der Folie lösen. Objekt mit kennung (Kleinbuchstaben, Ziffern, Bindestrich; bleibt beim Überarbeiten gleich), text und antworten wie bei einer Frage, dazu erklaerung und bezug ({"bild": Dateiname aus dem Material, "titel": …} oder {"html": …}). Erlaubt sind die Formen, die der Browser selbst wertet. Die Aufgabe steht am Ende des Textes oder an der Stelle von {{aufgabe}}; null nimmt sie heraus.
aufgabe_leicht
Optional: dieselbe Aufgabe in einfacher Sprache (text, antworten, erklaerung); braucht text_leicht. Fehlt sie, steht in der leichten Fassung die Aufgabe im Standardwortlaut.
aktiv
Optional, Standard true.

Antwort: 201 mit der Folie. 422, wenn Titel oder Medium fehlen oder die Aufgabe nicht trägt.

PATCH /v1/kurse/{id}/folien/{folie_id} inhalte: schreiben

Eine Folie ändern; nicht genannte Felder bleiben.

Felder im Rumpf

art
Optional.
titel
Optional.
text
Optional.
titel_leicht
Optional.
text_leicht
Optional.
material
Optional.
podcast
Optional.
aktiv
false legt die Folie still.
aufgabe
Wie bei POST …/folien: setzt oder ersetzt die Aufgabe der Folie; null nimmt sie heraus.
aufgabe_leicht
Wie bei POST …/folien.

Antwort: Die geänderte Folie. Der Folienfortschritt der Lernenden bleibt.

DELETE /v1/kurse/{id}/folien/{folie_id} inhalte: schreiben

Eine Folie löschen.

Antwort: Zum Ausblenden genügt "aktiv": false - dann bleibt sie für spätere Kurse erhalten.

PUT /v1/kurse/{id}/seminare/{seminar_id}/folien/reihenfolge inhalte: schreiben

Die Reihenfolge der Folien eines Seminars setzen.

Felder im Rumpf

folien_ids
Pflicht: alle Folien dieses Seminars in der gewünschten Abfolge.

Antwort: Die neue Liste. 422, wenn die Liste nicht genau die Folien des Seminars nennt.

GET /v1/themen inhalte: lesen

Alle Themen mit ihren Zielstufen, in Baumreihenfolge.

Parameter in der Adresse

limit
Wie viele Einträge (1 bis 200, Standard 50).
cursor
Weiterlesen ab dieser Kennung; steht als naechster_cursor in der Antwort.

Antwort: Je Thema id, parent_id, titel, zielstufe (1 erinnern bis 6 erschaffen, null = nicht festgelegt), reihenfolge, tiefe, pfad (Titel der Oberthemen mit › getrennt) und fragen_zahl.

POST /v1/themen inhalte: schreiben

Ein Thema anlegen. Themen gelten für alle Einrichtungen.

Felder im Rumpf

titel
Pflicht, höchstens 160 Zeichen.
parent_id
Optional: Kennung des Oberthemas.
zielstufe
Optional, 1 bis 6; leer heißt "noch nicht entschieden".
reihenfolge
Optional.

Antwort: 201 mit dem Thema.

GET /v1/themen/{id} inhalte: lesen

Ein Thema mit Zielstufe und Zahl der eingeordneten Fragen.

PATCH /v1/themen/{id} inhalte: schreiben

Ein Thema ändern oder seine Zielstufe setzen; nicht genannte Felder bleiben.

Felder im Rumpf

titel
Optional.
parent_id
Optional.
zielstufe
null nimmt die Vorgabe zurück.
reihenfolge
Optional.

Antwort: Das geänderte Thema. 422, wenn ein Thema unter sich selbst hinge.

DELETE /v1/themen/{id} inhalte: schreiben

Ein Thema löschen.

Themen sind nicht nach Einrichtungen getrennt: Dasselbe Thema trägt oft die Fragen mehrerer Kurse. Löschen wirkt überall, die Fragen selbst bleiben.

Antwort: fragen_ohne_thema nennt, wie viele Fragen ihre Einordnung verloren haben - in jedem Kurs, der das Thema nutzte.

GET /v1/desktopwelten inhalte: lesen

Die Welten der Desktop-Simulation.

Antwort: Je Welt schluessel, titel, art, beschreibung, aktiv und die Zahl der Knoten und Aufgaben.

GET /v1/desktopwelten/{schluessel} inhalte: lesen

Eine Welt mit Knoten, Übergängen und Aufgaben.

Nur mit Katalogpflege: Im Inhalt stehen die Wege und die Auflösungen der Aufgaben. Sonst 409.

Antwort: Im Aufbau, den PUT annimmt.

PUT /v1/desktopwelten/{schluessel} inhalte: schreiben

Eine Welt der Desktop-Simulation anlegen oder fortschreiben.

Nur mit Katalogpflege, sonst 409. Geprüft wird vor dem Schreiben: genau ein Startknoten, jeder Übergang zeigt auf einen vorhandenen Knoten, jeder Knoten ist vom Start aus erreichbar, und das Ziel jeder Aufgabe ist von ihrem Start aus erreichbar. Knoten und Aufgaben werden am schluessel wiedererkannt; Übergänge werden ersetzt. Knoten, die im Rumpf fehlen, bleiben stehen und stehen im bericht unter verwaist.

Felder im Rumpf

titel
Pflicht.
art
Optional, Vorgabe client.
beschreibung
Optional.
knoten
Pflicht: Liste. Je Knoten schluessel (Pflicht), art, kopf, pfad, inhalt, inhalt_leicht, ist_start (genau einer) und uebergaenge. Je Übergang text (Pflicht), text_leicht, auf (Schlüssel des Zielknotens oder null), hinweis, taste, stelle, art.
aufgaben
Optional: Liste. Je Aufgabe schluessel, titel, lage, auftrag (Pflicht), start und ziel (Schlüssel zweier Knoten), aufloesung und die leichten Fassungen lage_leicht, auftrag_leicht, aufloesung_leicht.
probe
Optional: true prüft nur und schreibt nichts.

Antwort: Der neue Stand wie bei GET, dazu bericht (knoten, uebergaenge, aufgaben, verwaist, erreichbar, unerreichbar) und hinweise. 201 beim ersten Anlegen.

GET /v1/themengruppen inhalte: lesen

Die Themengruppen des Katalogs - die gültigen Werte für themengruppe_id an einem Kurs.

Nicht zu verwechseln mit den Themen: Eine Themengruppe ordnet Kurse ("IT und Technik"), ein Thema ordnet Fragen in der Diagnose. Beide gelten für alle Einrichtungen.

Antwort: Je Gruppe id, titel, kurzbeschreibung, icon, reihenfolge und kurs_zahl.

GET /v1/kurse/{id}/seminare/{seminar_id}/szenarien inhalte: lesen

Die Fallszenarien eines Seminars, jedes vollständig.

Antwort: Je Szenario schluessel, titel, einleitung, thema, stufe, start und szenen. Je Szene text, ende (gut, knapp, schlecht), lehre und optionen mit dem Schlüssel der Zielszene - dieselbe Form, die PUT entgegennimmt.

PUT /v1/kurse/{id}/seminare/{seminar_id}/szenarien inhalte: schreiben

Ein Fallszenario anlegen oder fortschreiben; erkannt wird am schluessel.

Szenen behalten ihre Kennung, solange ihr Schlüssel bleibt; gegangene Durchgänge bleiben damit lesbar. Szenen, die im Rumpf fehlen, werden entfernt. Entscheidungen werden jedes Mal neu gesetzt.

Felder im Rumpf

schluessel
Pflicht: die sprechende Kennung des Falls im Seminar.
titel
Pflicht.
start
Pflicht: Schlüssel der Anfangsszene.
szenen
Pflicht: Objekt, Schlüssel → Szene. Je Szene text (Pflicht), text_leicht, lehre, lehre_leicht; eine Endszene trägt ende (gut, knapp, schlecht) und keine optionen, jede andere mindestens eine Entscheidung: text, ziel (Schlüssel einer Szene), hinweis.
einleitung
Optional, auch einleitung_leicht und titel_leicht.
thema
Optional: Titel eines Themas - ohne bleibt der Fall in der Diagnose unsichtbar.
stufe
Optional, 1 bis 6; Vorgabe 5 (bewerten).
reihenfolge
Optional.
aktiv
Optional: false legt den Fall still - er verschwindet aus dem Training, abgegebene Durchläufe bleiben auswertbar. Ohne Angabe bleibt der bisherige Stand; ein neues Szenario ist aktiv.

Antwort: 201 beim ersten Mal, sonst 200 mit dem fortgeschriebenen Fall. 422 nennt alle gefundenen Mängel auf einmal - unerreichbare Szenen, Sackgassen, fehlender guter Ausgang, Entscheidungen ins Leere.

DELETE /v1/kurse/{id}/szenarien/{szenario_id} inhalte: schreiben

Ein Fallszenario samt Szenen und Entscheidungen löschen.

Antwort: 409, sobald jemand den Fall durchgespielt hat - dann in der Verwaltung ausblenden.

GET /v1/kurse/{id}/spickzettel inhalte: lesen

Die Spickzettel eines Kurses und die Vorlagen seiner Code-Werkstatt.

Antwort: karten (bereich, titel, text, text_leicht, sprache, code, thema) und werkstatt (titel, beschreibung, html, css, js), jeweils in ihrer Reihenfolge.

PUT /v1/kurse/{id}/spickzettel inhalte: schreiben

Spickzettel und Werkstatt-Vorlagen eines eigenen Kurses setzen.

Einzelne Karten lassen sich nicht ändern - auch die Kursdatei schreibt immer den ganzen Satz. 409 bei Kursen aus dem globalen Katalog - außer mit Katalogpflege.

Felder im Rumpf

karten
Liste; je Karte bereich und titel (Pflicht), dazu text, text_leicht, sprache (html, css, js oder text - text ist ein Merkblock ohne Ausprobieren) und code. thema ist der Titel eines Themas dieses Kurses - dann erscheint die Karte im Training als Hilfe zu Fragen dieses Themas; ein unbekannter Titel ist kein Fehler, die Karte steht dann nur auf der Nachschlageseite.
werkstatt
Liste; je Vorlage titel (Pflicht), dazu beschreibung, html, css und js.

Antwort: Der neue Stand. Beide Listen ersetzen den Bestand; eine weggelassene Liste bleibt unverändert, eine leere Liste löscht ihren Teil.

POST /v1/kurse/{id}/material inhalte: schreiben

Eine Datei, einen Verweis oder einen Podcast am Kurs oder an einem seiner Seminare ablegen.

Geprüft wird der Inhalt, nicht der Name; die Datei bekommt einen eigenen Namen.

Felder im Rumpf

dateiname
Pflicht bei Dateien; die Endung bestimmt die Art (pdf, png, jpg, webp, svg, mp3, mp4 …).
inhalt_base64
Pflicht bei Dateien: der Dateiinhalt, base64-kodiert.
podcast
Statt einer Datei: {"id": 5, "key": "…"} - Episode und Schlüssel des Podcast-Servers (music.seolizer.de); der Titel ist dann Pflicht. Optional "theme": "light" (sonst dunkel) und "chapters": false (Kapitel ausblenden).
url
Statt einer Datei: ein Verweis ins Netz (http oder https); der Titel ist dann Pflicht.
seminar_id
Optional; ohne Angabe hängt die Datei am Kurs.
titel
Optional; sonst aus dem Dateinamen.
einfache_sprache
Optional: true, wenn die Datei in einfacher Sprache ist.

Antwort: 201 mit dem Material; 422, wenn Endung und Inhalt nicht zusammenpassen oder die Adresse eines Verweises nicht mit http:// oder https:// beginnt.

PATCH /v1/kurse/{id}/material/{material_id} inhalte: schreiben

Titel, Platz in der Liste oder die Kennzeichnung "einfache Sprache" ändern.

Die Datei selbst bleibt unberührt - auch ihr Dateiname, der in Verweisen steht. Den Inhalt tauscht PUT kurse/{id}/material/{material_id}/datei aus.

Felder im Rumpf

titel
Neuer Titel, höchstens 200 Zeichen.
reihenfolge
Platz in der Liste; kleiner steht weiter vorn.
einfache_sprache
true, wenn die Datei in einfacher Sprache ist.

Antwort: Das geänderte Material. Nicht genannte Felder bleiben.

PUT /v1/kurse/{id}/material/{material_id}/datei inhalte: schreiben

Den Inhalt einer Datei austauschen.

Kennung und Dateiname bleiben, und damit jede Folie, jede Hotspot-Aufgabe und jeder Bezug, der auf die Datei zeigt. Der Typ lässt sich so nicht wechseln - dafür neu anlegen.

Felder im Rumpf

inhalt_base64
Pflicht: der neue Inhalt, Base64-kodiert.

Antwort: Das Material mit neuer Größe. 422, wenn der Inhalt nicht zur Endung der vorhandenen Datei passt oder das Material ein Verweis ist.

DELETE /v1/kurse/{id}/material/{material_id} inhalte: schreiben

Eine Datei entfernen.

Antwort: Folien damit werden zu Textfolien (folien_ohne_datei); 409, wenn eine Frage die Datei nutzt.

Personen

Konten lesen; anlegen, ändern, Rolle setzen, Kurse einzeln zuweisen, Passwort neu setzen, löschen

GET /v1/personen personen: lesen

Konten der Einrichtung.

Eine Person gehört zu genau einer Einrichtung. Wer per Signal übernommen wird (Pairing), wechselt ganz hierher und ist danach ein Konto dieser Einrichtung. heimat ist deshalb "eigen"; "aufgenommen" kommt nur noch bei Altbeständen vor.

Parameter in der Adresse

limit
Wie viele Einträge (1 bis 200, Standard 50).
cursor
Weiterlesen ab dieser Kennung; steht als naechster_cursor in der Antwort.
klasse
Nur Mitglieder dieser Klasse.
fremd_id
Nur das Konto mit dieser Kennung des Fremdsystems (nur eigene Konten).
rolle
lernend, ausbilder oder admin.
geaendert_ab
Nur seit diesem Zeitpunkt geänderte Konten (2026-09-01 oder 2026-09-01T10:00:00).

Antwort: Je Konto Kennung, heimat, Name, E-Mail, Rolle, aktiv (false = gesperrt), fremd_id, Zeitpunkte und lernstatus: kurse, kurse_abgeschlossen (alle aktiven Seminare im Examen bestanden), zuletzt_aktiv, tage_inaktiv und status (aktiv, inaktiv ab 14 Tagen ohne Aktivität, gesperrt).

GET /v1/personen/fremd/{fremd_id} personen: lesen

Ein Konto über die Kennung des Fremdsystems.

So findet das andere System seinen Datensatz wieder, ohne unsere IDs zu speichern.

Antwort: Das Konto samt seinen Klassen.

GET /v1/personen/{id} personen: lesen

Ein Konto samt seinen Klassen.

Antwort: Das Konto mit klassen, lernstatus und standort (id, name, herkunft, zuordnung_noetig); 404, wenn es zu einer anderen Einrichtung gehört.

POST /v1/personen personen: schreiben

Eine Person anlegen.

Felder im Rumpf

vorname
Pflicht.
nachname
Pflicht.
email
Pflicht, eindeutig in der Einrichtung.
username
Ohne Angabe aus dem Namen gebildet. Eindeutig in ganz peritio, nicht nur in der Einrichtung.
fremd_id
Kennung des Fremdsystems, höchstens 64 Zeichen, eindeutig in der Einrichtung.
geburtsjahr
Optional, vierstellig.
geburtsort
Optional.

Antwort: 201 mit dem Konto und dem Startpasswort - das steht nur in dieser einen Antwort.

PUT /v1/personen/fremd/{fremd_id} personen: schreiben

Person über die Kennung des Fremdsystems anlegen oder aktualisieren.

Der übliche Weg beim nächtlichen Abgleich: derselbe Aufruf wirkt immer gleich.

Felder im Rumpf

vorname
Pflicht beim Anlegen.
nachname
Pflicht beim Anlegen.
email
Pflicht beim Anlegen.

Antwort: 201 beim Anlegen, 200 beim Aktualisieren.

PATCH /v1/personen/{id} personen: schreiben

Angaben ändern; nicht genannte Felder bleiben, wie sie sind.

Admin-Konten ändert nur ein Schlüssel mit „Einrichtung: schreiben“; Plattform-Konten gar nicht. Wer nicht mehr Ausbilder ist, verliert seine Fachbereiche.

Felder im Rumpf

vorname
Optional.
nachname
Optional.
email
Optional.
username
Optional.
fremd_id
Optional.
geburtsjahr
Optional.
geburtsort
Optional.
rolle
lernend, ausbilder oder admin. Zu oder von admin nur mit „Einrichtung: schreiben“.
aktiv
false sperrt das Konto (keine Anmeldung, Daten bleiben), true gibt es frei. Den letzten aktiven Admin sperrt die API nicht (409).

Antwort: Das geänderte Konto; 422 mit Klartext, wenn eine Angabe nicht passt.

POST /v1/personen/{id}/passwort personen: schreiben

Ein neues Startpasswort setzen; das alte gilt sofort nicht mehr.

Für Admin-Konten nur mit „Einrichtung: schreiben“.

Antwort: Das neue Startpasswort - einmalig, es wird nirgends protokolliert.

DELETE /v1/personen/{id} personen: schreiben

Eine Person aus der Einrichtung entfernen - Lernende, Ausbilder und Admins.

Eine Einrichtung löscht kein Konto (seit 30.09.2026): Die Zugehörigkeit endet, Kurse, Klassen und Fachbereiche hier enden mit, die Rolle wird lernend. Die Person meldet sich danach auf peritio.de an und behält Ergebnisse und Zertifikate. Admins nur mit „Einrichtung: schreiben“, und nie der letzte. Plattform-Konten nie. mit_ergebnissen wirkt nur noch für die Plattform selbst.

Antwort: geloescht: false, zugehoerigkeit_beendet: true. Die Person gehört danach zu peritio.

GET /v1/personen/{id}/kurse personen: lesen

Alle Kurse der Person, wie im Lernstand gezählt.

PUT und DELETE auf personen/{id}/kurse/{kurs_id} ändern nur die Herkunft persoenlich; Kurse der Klasse werden über klassen/{id}/kurse zugewiesen.

Antwort: Je Kurs kurs_id, titel, herkunft (persoenlich, klasse, bearbeitet), persoenlich (true, wenn einzeln zugewiesen) und klassen (über welche laufende Klasse).

PUT /v1/personen/{id}/kurse/{kurs_id} personen: schreiben

Einer Person einen Kurs direkt zuweisen, zusätzlich zur Klasse.

Antwort: Die einzeln zugewiesenen Kurse; ein zweiter Aufruf ändert nichts.

DELETE /v1/personen/{id}/kurse/{kurs_id} personen: schreiben

Die direkte Zuweisung zurücknehmen.

Antwort: Die einzeln zugewiesenen Kurse. Über die Klasse bleibt der Kurs gegebenenfalls sichtbar; Ergebnisse bleiben.

GET /v1/personen/{id}/fachbereiche personen: lesen

Fachbereiche, in denen ein Ausbilder tätig ist.

Antwort: Je Fachbereich Kennung, Titel und Kürzel.

PUT /v1/personen/{id}/fachbereiche personen: schreiben

Die Fachbereiche eines Ausbilders auf genau diese Liste setzen.

Ein Ausbilder sieht nur die Klassen seiner Fachbereiche.

Felder im Rumpf

fachbereich_ids
Pflicht: Liste von Kennungen, leer entfernt alle.

Antwort: Die Fachbereiche; 409, wenn die Person kein Ausbilder ist.

GET /v1/personen/{id}/standort personen: lesen

Der Standort, an dem eine Person lernt oder arbeitet.

Einzutragen ist der Standort nur, wenn ein Fachbereich der Person an mehreren Standorten angeboten wird. "Mein Weg" rechnet zu diesem Standort.

Antwort: id und name des Standorts, herkunft (zugeordnet: eingetragen; fachbereich: der einzige Standort ihrer Fachbereiche; haupt: kein Fachbereich, also der Hauptstandort; fehlt: mehrere kommen in Frage, keiner eingetragen - dann sind id und name null), zuordnung_noetig und moeglich (Standorte, die über die Fachbereiche in Frage kommen).

PUT /v1/personen/{id}/standort personen: schreiben

Den Standort einer Person eintragen oder die Eintragung aufheben.

Felder im Rumpf

standort_id
Pflicht: Kennung eines Standorts der Einrichtung, null hebt die Eintragung auf.

Antwort: Wie GET personen/{id}/standort; 422 bei einem unbekannten Standort.

Fachbereiche, Klassen und Berufe

lesen; anlegen, ändern, stilllegen, löschen, Mitglieder und Kurse zuweisen; Ausbildungsberufe samt Rahmenplan lesen, eigene Berufe pflegen; Standorte der Einrichtung pflegen

GET /v1/berufe klassen: lesen

Ausbildungsberufe: die von peritio und die eigenen der Einrichtung.

Parameter in der Adresse

limit
Wie viele Einträge (1 bis 200, Standard 50).
cursor
Weiterlesen ab dieser Kennung; steht als naechster_cursor in der Antwort.
art
Filter: anerkannt, fachpraktiker, gesundheit, schulisch.
fachbereich
Nur Berufe dieses Fachbereichs.
suche
Titel, Schlüssel oder Kurzname.

Antwort: Je Beruf Titel, Art, Dauer in Monaten und die Zahl der Rahmenplan-Positionen. herkunft ist "grundsystem" (peritio pflegt ihn) oder "eigen"; aenderbar sagt, ob dieser Schlüssel ihn schreiben darf.

POST /v1/berufe klassen: schreiben

Einen eigenen Beruf der Einrichtung anlegen, auf Wunsch als Kopie.

Felder im Rumpf

titel
Pflicht.
dauer_monate
Pflicht, Zahl: 24 = 2 Jahre, 42 = 3,5 Jahre.
art
Optional, Standard anerkannt.
kurzname
Optional.
schluessel
Optional, sonst aus dem Titel.
verordnung
Optional.
beschreibung
Optional.
teil1_bereich
Optional.
teil1_monate
Optional, Zahl.
aktiv
Optional, Standard true.
vorlage_beruf_id
Optional: Kopie eines sichtbaren Berufs samt Rahmenplan. Die Kopie wird danach selbst gepflegt.

Antwort: 201 mit dem Beruf. 422, wenn Titel oder Dauer fehlen.

GET /v1/berufe/{id} klassen: lesen

Ein Beruf mit Verordnung, Grundlage und Prüfungsaufbau.

Antwort: Der Beruf; 404, wenn er in dieser Einrichtung nicht sichtbar ist.

PATCH /v1/berufe/{id} klassen: schreiben

Einen eigenen Beruf ändern.

Felder im Rumpf

titel
Optional.
kurzname
Optional.
art
Optional.
dauer_monate
Optional.
verordnung
Optional.
grundlage
Optional.
beschreibung
Optional.
themen
Optional.
teil1_bereich
Optional.
teil1_monate
Optional.
reihenfolge
Optional.
aktiv
Optional.

Antwort: Der geänderte Beruf. 409 bei einem Beruf von peritio - der gilt für alle Einrichtungen und ist nur als eigene Kopie änderbar.

DELETE /v1/berufe/{id} klassen: schreiben

Einen eigenen Beruf samt Rahmenplan löschen.

Antwort: 409 bei einem Beruf von peritio. Kurse bleiben bestehen und verlieren nur ihren Platz im Rahmenplan.

GET /v1/berufe/{id}/rahmenplan klassen: lesen

Der Ausbildungsrahmenplan als Baum.

Antwort: abschnitte (Kürzel, Titel, Art, Fachrichtung), darin positionen mit Wortlaut der Verordnung, Paragraf, Prüfungsteil, zugeordnetem Kurs, lernziele (Buchstabe und Text) und zeiten (Monatsfenster oder Wochen je Prüfungsteil). Positionen ohne Abschnitt stehen am Ende unter kuerzel null.

GET /v1/fachbereiche/{id}/berufe klassen: lesen

Die Berufe, die dieser Fachbereich ausbildet.

Antwort: Liste wie bei GET berufe, ohne Cursor.

PUT /v1/fachbereiche/{id}/berufe klassen: schreiben

Die Berufe eines Fachbereichs setzen - die Liste ersetzt den Bestand.

Felder im Rumpf

beruf_ids
Pflicht: Liste von Kennungen, leere Liste entfernt alle.

Antwort: Die neue Liste. 422, wenn eine Kennung hier nicht sichtbar ist.

GET /v1/fachbereiche klassen: lesen

Fachbereiche der Einrichtung.

Parameter in der Adresse

limit
Wie viele Einträge (1 bis 200, Standard 50).
cursor
Weiterlesen ab dieser Kennung; steht als naechster_cursor in der Antwort.

Antwort: Je Fachbereich Titel und Zahl der Klassen.

GET /v1/einrichtung/standorte klassen: lesen

Die Standorte der eigenen Einrichtung.

Nicht zu verwechseln mit GET standorte, dem Verzeichnis der Reha-Einrichtungen. Ein Fachbereich ohne Standort gilt als am Hauptstandort.

Antwort: daten: je Standort id, name, art (einrichtung, aussenstelle, berufsschule, kooperation), strasse, plz, ort, ist_haupt, beginn, ende, puffer_min, fachbereich_ids, telefon, email, beschreibung, hat_foto und zeiten: je Wochentag "1" (Montag) bis "7" (Sonntag) beginn und ende oder null für geschlossen. Ohne eigene Zeiten gelten beginn und ende von Montag bis Freitag.

GET /v1/einrichtung/standorte/{id} klassen: lesen

Ein Standort.

Antwort: Der Standort wie in der Liste.

POST /v1/einrichtung/standorte klassen: schreiben

Einen Standort anlegen. Der erste wird Hauptstandort.

Felder im Rumpf

name
Pflicht.
strasse
Pflicht, mit Hausnummer.
ort
Pflicht.
plz
Optional, 4 oder 5 Ziffern.
beginn
Pflicht, HH:MM.
ende
Pflicht, HH:MM, nach dem Beginn.
puffer_min
Optional, 0 bis 60, Standard 10.
art
Optional: einrichtung (Standard), aussenstelle, berufsschule, kooperation.
fachbereich_ids
Optional: Fachbereiche, die hier angeboten werden.
zeiten
Optional: {"1": {"beginn": "07:30", "ende": "16:15"}, …, "6": null} - je Wochentag, null heißt geschlossen. beginn und ende des Standorts folgen dann dem häufigsten Tag.
telefon
Optional.
email
Optional.
beschreibung
Optional, höchstens 500 Zeichen.

Antwort: 201 mit dem Standort; 422 mit dem Grund, wenn eine Angabe fehlt oder nicht passt.

PATCH /v1/einrichtung/standorte/{id} klassen: schreiben

Einen Standort ändern; nicht genannte Felder bleiben.

Felder im Rumpf

name
Optional.
art
Optional.
strasse
Optional.
plz
Optional.
ort
Optional.
beginn
Optional.
ende
Optional.
puffer_min
Optional.
fachbereich_ids
Ersetzt die Liste, wenn gesendet.
zeiten
Nur die genannten Tage ändern sich.
telefon
Optional.
email
Optional.
beschreibung
Optional.

Antwort: Der geänderte Standort.

DELETE /v1/einrichtung/standorte/{id} klassen: schreiben

Einen Standort löschen.

Personen, die ihm zugeordnet waren, verlieren die Eintragung.

Antwort: geloescht und id; 409 beim Hauptstandort.

PUT /v1/einrichtung/standorte/{id}/haupt klassen: schreiben

Diesen Standort zum Hauptstandort machen.

Antwort: Der Standort.

POST /v1/fachbereiche klassen: schreiben

Einen Fachbereich anlegen.

Felder im Rumpf

titel
Pflicht.
kuerzel
Optional.
beschreibung
Optional.
reihenfolge
Optional, Zahl.
aktiv
Optional, Standard true.

Antwort: 201 mit dem Fachbereich.

GET /v1/fachbereiche/{id} klassen: lesen

Ein Fachbereich.

Antwort: Der Fachbereich mit Zahl der Klassen.

PATCH /v1/fachbereiche/{id} klassen: schreiben

Einen Fachbereich ändern oder mit "aktiv": false stilllegen.

Felder im Rumpf

titel
Optional.
kuerzel
Optional.
beschreibung
Optional.
reihenfolge
Optional.
aktiv
Optional.

Antwort: Der geänderte Fachbereich.

DELETE /v1/fachbereiche/{id} klassen: schreiben

Einen Fachbereich löschen.

Antwort: 409, solange Klassen daran hängen.

GET /v1/klassen klassen: lesen

Klassen der Einrichtung.

Parameter in der Adresse

limit
Wie viele Einträge (1 bis 200, Standard 50).
cursor
Weiterlesen ab dieser Kennung; steht als naechster_cursor in der Antwort.
fachbereich
Nur Klassen dieses Fachbereichs.

Antwort: Je Klasse Titel, Fachbereich, Jahrgang, Zeitraum und Zahl der Mitglieder.

GET /v1/klassen/{id} klassen: lesen

Eine Klasse.

Antwort: Die Klasse mit Zeitraum und Fachbereich.

GET /v1/klassen/{id}/mitglieder klassen: lesen

Mitglieder der Klasse.

Antwort: Je Mitglied Konto, Eintritt, Austritt und ob die Mitgliedschaft läuft.

GET /v1/klassen/{id}/kurse klassen: lesen

Kurse der Klasse samt Freigabedatum.

Antwort: Je Kurs Titel und ab wann er sichtbar ist.

POST /v1/klassen klassen: schreiben

Eine Klasse anlegen.

Felder im Rumpf

titel
Pflicht.
fachbereich_id
Pflicht.
jahrgang
Optional, vierstellig.
beginnt_am
Optional, Datum.
endet_am
Optional, Datum.

Antwort: 201 mit der Klasse.

PATCH /v1/klassen/{id} klassen: schreiben

Eine Klasse ändern, in einen anderen Fachbereich hängen oder stilllegen.

Felder im Rumpf

titel
Optional.
fachbereich_id
Optional.
jahrgang
Optional.
beginnt_am
Optional.
endet_am
Optional.
aktiv
false legt die Klasse still, true nimmt sie wieder in Betrieb.

Antwort: Die geänderte Klasse.

DELETE /v1/klassen/{id} klassen: schreiben

Eine Klasse löschen.

Antwort: 409, sobald jemals jemand darin war - dann mit PATCH und "aktiv": false stilllegen.

PUT /v1/klassen/{id}/mitglieder/{person_id} klassen: schreiben

Eine Person in die Klasse aufnehmen.

Felder im Rumpf

ab
Eintritt, Datum; ohne Angabe heute.

Antwort: Die Mitgliedschaft; ein zweiter Aufruf ändert nur den Eintritt.

DELETE /v1/klassen/{id}/mitglieder/{person_id} klassen: schreiben

Eine Person entlassen.

Parameter in der Adresse

zum
Austritt zu diesem Datum; ohne Angabe heute.
streichen
1: die Mitgliedschaft ganz entfernen, als hätte es sie nie gegeben.

Antwort: Ohne streichen bleibt die Mitgliedschaft mit Austrittsdatum in der Geschichte stehen.

PUT /v1/klassen/{id}/kurse/{kurs_id} klassen: schreiben

Der Klasse einen Kurs zuweisen.

Felder im Rumpf

frei_ab
Ab wann der Kurs sichtbar ist; ohne Angabe sofort.

Antwort: Die Zuweisung; ein zweiter Aufruf ändert nur das Datum.

DELETE /v1/klassen/{id}/kurse/{kurs_id} klassen: schreiben

Der Klasse einen Kurs entziehen.

Antwort: Der Kurs verschwindet aus der Übersicht; Ergebnisse bleiben.

Lernstand und Fortschritt

Ergebnisse je Person und Klasse lesen

GET /v1/personen/{id}/lernstand lernstand: lesen

Lernstand einer Person je Kurs.

Parameter in der Adresse

seminare
1: zusätzlich je Seminar (seminar_liste).
auswertung
1: zusätzlich Einordnung und Empfehlungen für Lehrkräfte. Braucht das Recht „Lernstand-Auswertung: lesen“, sonst 403.

Antwort: Je Kurs Fortschritt, letzter Zugriff und bestandene Examen; zuletzt bearbeiteter Kurs zuerst, nie bearbeitete am Ende nach Titel. Je Seminar: versuche = alle abgeschlossenen Läufe, training = davon Trainings, examen = davon Examensversuche; entwicklung = Punkte zwischen erstem und jüngstem Lauf; im_training_sicher = im Training stabil über der Grenze, Examen noch nicht bestanden. Mit auswertung: auswertung (prozent, seminare, staerken, sicher_im_training, verbessert, letzter_versuch_am, tage_seit_letztem_versuch, pause, klasse_laeuft, zustand, pruefungsfragen), pruefungsfragen: Examen und Abschlussprüfungen getrennt nach neu und bekannt (in einem früheren Lauf schon beantwortet), je gestellt, richtig und quote (null unter 10 Fragen), dazu laeufe. schwellen (pause_tage) und empfehlungen in der Reihenfolge der Verwaltung, je Eintrag code, ton, titel, handlung, grund und bei Bedarf link (absolut). ton: dringend, wichtig, hinweis, chance. code: einstieg, pause, fehlversuch, bruch, inhaltsluecke, tiefenluecke, unter_schwelle, schwerpunkt, hartnaeckig, wiederholt, rueckgang, erst_trainieren, einfuehrung_offen, zeitbedarf, examen_ruhe, tempo, abbrueche, duenn, uebung_erschoepft, weitere_kurse, verstehen_pruefen, examen_ermutigen, erneut_antreten, fortschritt, lernfortschritt, examen_schleife, mehr_fordern. Neue Werte werden nur angehängt; unbekannte bitte neutral anzeigen. bruch, tiefenluecke und fairness sind zurückgehalten (Regel am Bestand widerlegt) und kommen derzeit nicht vor.

GET /v1/personen/fremd/{fremd_id}/lernstand lernstand: lesen

Lernstand einer Person über die Kennung des Fremdsystems - ohne vorherige Kontosuche.

Parameter in der Adresse

seminare
1: zusätzlich je Seminar.
auswertung
1: zusätzlich Einordnung und Empfehlungen. Braucht „Lernstand-Auswertung: lesen“.

Antwort: Wie personen/{id}/lernstand; 404, wenn keine Person diese fremd_id trägt.

GET /v1/klassen/{id}/lernstand lernstand: lesen

Lernstand aller Mitglieder einer Klasse.

Antwort: Je Mitglied die Kurse mit Fortschritt - die Grundlage für eigene Auswertungen.

Förderplanung

Beobachtungsbelege je lernender Person lesen, nach ICF geordnet (ohne Beurteilungsmerkmal)

GET /v1/personen/{id}/belege foerderung: lesen

Beobachtungen für den Förderplan, nach ICF geordnet: d210 Einzelaufgabe übernehmen (mit d135 Üben), d825 Berufsausbildung, e1301 Hilfsmittel für Bildung.

Kein ICF-Beurteilungsmerkmal (.0 bis .4) und keine Körperfunktionen: Das Merkmal setzt die Fachkraft. Nur für lernende Personen; bei anderen ist daten leer.

Antwort: person_id und daten: je Beleg code, kategorie, quelle, stand (muster, ressource, unauffaellig, fakt, zu_wenig), text, optional verlauf, zusatz, hinweis (Gegenprobe) und formulierung (Satz für den Förderplan).

Bewerbungs-Coach

Stand der begleiteten Lernenden lesen: Phase, Bewerbungen, Unterlagen, offene Vereinbarungen, Hinweise

GET /v1/begleitung begleitung: lesen

Bewerbungs-Coach: alle begleiteten Lernenden mit ihrem Stand.

Nur Personen in begleiteten Klassen (Einstellungen des Bewerbungs-Coachs). 409, wenn das Modul nicht gebucht ist.

Antwort: daten: je Person person_id, Name, klasse, fachbereich, beruf, begleitet_seit, phase (nummer 1 bis 5, titel: Orientierung, Betriebssuche, Vorbereitung, Bewerbungsphase, Praktikum), begonnen, praktikumsbereich, zahlen (vorgemerkt, vorbereiten, versendet, laufend, wartet, kontakt, angeboten, zusagen, absagen, beendet), unterlagen (lebenslauf, anschreiben: fehlt, begonnen, fertig), gespraeche, letzte_aktivitaet, tage_ohne_aktivitaet und hinweise (art, stufe, titel, text).

GET /v1/personen/{id}/begleitung begleitung: lesen

Bewerbungs-Coach: Stand einer begleiteten Person.

Gesprächsinhalte und Notizen gibt die API nicht heraus. 404, wenn die Person nicht begleitet wird.

Antwort: Wie in der Liste, dazu bewerbungen (betrieb, status, beworben_am, termin_am, zusage_am, geaendert_am) und offene_vereinbarungen (anlass, vereinbarung, faellig_am, gefuehrt_am).

Betriebe und Stellen

Betriebe und Praktikumsangebote lesen; Betriebe anlegen und ändern, eigene Stellenausschreibungen einstellen und beenden

GET /v1/betriebe betriebe: lesen

Betriebe der Einrichtung für Praktika.

409, wenn der Bewerbungs-Coach nicht gebucht ist.

Parameter in der Adresse

limit
Wie viele Einträge (1 bis 200, Standard 50).
cursor
Weiterlesen ab dieser Kennung; steht als naechster_cursor in der Antwort.
status
vorgeschlagen, freigegeben oder abgelehnt.
herkunft
einrichtung, jobboerse (Praktikumsbot), vorschlag (von Lernenden) oder bewerbung.
suche
Teil von Name oder Ort.
geaendert_ab
Nur seit diesem Zeitpunkt geänderte Betriebe.

Antwort: Je Betrieb id, fremd_id, Name, Website, Adresse, Branche, eigene Zusatzbeschreibung, Informationsquelle, bereich, praktikum (bestaetigt, wahrscheinlich, unbekannt), bewerbungsweg, kontakt_email, herkunft, status, bestaetigt_am, angebote_aktiv und Zeitpunkte.

GET /v1/betriebe/{id} betriebe: lesen

Ein Betrieb samt seinen Stellen.

Antwort: Der Betrieb und angebote (aktive zuerst).

PUT /v1/betriebe/fremd/{fremd_id} betriebe: schreiben

Betrieb über die Kennung des Fremdsystems anlegen oder aktualisieren.

Gibt es einen ähnlichen Betrieb schon (Name oder Website), kommt 409 mit den Kandidaten statt einer Dublette - verknüpfen per PATCH betriebe/{id} mit fremd_id, oder "trotz_dublette": true.

Felder im Rumpf

name
Pflicht beim Anlegen.
website
Optional; vergleichbar gespeichert (ohne Schema und www).
strasse
Optional.
plz
Optional.
ort
Optional.
stadtteil
Optional.
branche
Optional.
zusatzbeschreibung
Eigener Beschreibungstext der Einrichtung.
quellenhinweis
Konkrete Quelle der Betriebsinformationen.
bereich_id
Praktikumsbereich der Einrichtung.
praktikum
bestaetigt, wahrscheinlich oder unbekannt (Standard).
bewerbungsweg
Wie man sich bewirbt, kurz.
kontakt_email
Optional.
trotz_dublette
true: auch anlegen, wenn es einen ähnlich benannten Betrieb gibt.

Antwort: 201 beim Anlegen, 200 beim Aktualisieren. Was die Einrichtung einträgt, ist freigegeben.

PATCH /v1/betriebe/{id} betriebe: schreiben

Betrieb ändern; nicht genannte Felder bleiben.

Der Status wechselt nur mit "status"; ein Vorschlag wird durch eine Änderung freigegeben.

Felder im Rumpf

name
Optional.
website
Optional.
zusatzbeschreibung
Optional.
quellenhinweis
Optional.
praktikum
Optional.
bereich_id
Optional.
fremd_id
Verknüpft den Betrieb mit dem Fremdsystem.
status
freigegeben oder abgelehnt (Vorschläge von Lernenden entscheiden).

Antwort: Der geänderte Betrieb.

GET /v1/angebote betriebe: lesen

Praktikumsangebote: eigene Ausschreibungen und die des Praktikumsbots.

Parameter in der Adresse

limit
Wie viele Einträge (1 bis 200, Standard 50).
cursor
Weiterlesen ab dieser Kennung; steht als naechster_cursor in der Antwort.
quelle
einrichtung (eigene) oder ba (Bundesagentur für Arbeit).
betrieb
Nur Angebote dieses Betriebs.
aktiv
1 (Standard), 0 für beendete, alle.

Antwort: Je Angebot id, quelle, referenz, betrieb (id, name, fremd_id), titel, beruf, beschreibung, Adresse, beginn, dauer_monate, arbeitszeit, verguetung, link, veroeffentlicht_am, gefunden_am, aktiv und aenderbar.

PUT /v1/angebote/{referenz} betriebe: schreiben

Eigene Stellenausschreibung anlegen oder aktualisieren - über die Referenz des Fremdsystems.

Ändern lassen sich nur eigene Ausschreibungen (quelle einrichtung); die des Praktikumsbots nicht.

Felder im Rumpf

betrieb_id
Betrieb der Einrichtung. Pflicht beim Anlegen, wenn betrieb_fremd_id fehlt.
betrieb_fremd_id
Oder die Kennung des Betriebs im Fremdsystem.
titel
Pflicht beim Anlegen.
beruf
Beruf oder Tätigkeit, kurz.
beschreibung
Text der Ausschreibung.
strasse
Ohne Angabe gilt die Adresse des Betriebs.
plz
Optional.
ort
Optional.
beginn
Datum, z. B. 2026-11-01.
dauer_monate
0 bis 60.
arbeitszeit
Optional.
verguetung
Optional.
link
Adresse der Ausschreibung, optional.

Antwort: 201 beim Anlegen, 200 beim Aktualisieren. Die Stelle erscheint im Praktikumsmarkt, sobald der Betrieb freigegeben ist.

DELETE /v1/angebote/{referenz} betriebe: schreiben

Eigene Ausschreibung beenden.

Gelöscht wird nichts: Bewerbungen darauf behalten ihren Bezug. Ein späteres PUT mit derselben Referenz schaltet sie wieder an.

Antwort: Die Ausschreibung mit aktiv: false.

Fragen und Antworten

Prüfungsinhalte eigener Kurse lesen; anlegen, ändern, stilllegen

GET /v1/kurse/{id}/seminare/{seminar_id}/fragen fragen: lesen

Die Fragen eines Seminars mit ihren Antwortzeilen.

409 bei Kursen aus dem globalen Katalog, auch beim Lesen - außer mit Katalogpflege.

Antwort: Je Frage id, text, text_leicht, format, code_sprache, kontext, kontext_leicht, erklaerung, erklaerung_leicht, thema, stufe, aktiv und die Zeilen aus `antwort` (luecke_nr, position, bereich, text, text_leicht, ist_korrekt) - roh, weil jede Aufgabenform ihre Lösung anders ablegt.

POST /v1/kurse/{id}/seminare/{seminar_id}/fragen fragen: schreiben

Fragen anlegen - im Aufbau der Kursdateien.

Geprüft und geschrieben wird mit derselben Klasse wie beim Einspielen einer Kursdatei (src/Fragenpflege.php). Eine Frage mit gleichem Text wird abgewiesen, nicht ersetzt: An ihr hängen abgegebene Antworten. Das Bild einer Hotspot-Aufgabe muss vorher im Material liegen, sonst 422.

Felder im Rumpf

fragen
Pflicht: Liste. Je Frage text (Pflicht), antworten, thema, stufe, kontext, erklaerung und erklaerung_leicht (erscheint im Training nach der Antwort, nie im Examen); dazu die Fassung in einfacher Sprache: text_leicht, kontext_leicht und antworten_leicht ({"Wortlaut der Antwort": "leichte Fassung"} oder eine Liste aus {"antwort": "…", "leicht": "…"}). Die Aufgabenform ergibt sich aus dem, was dasteht: Marken [[1]] im Text = Lückentext, Sternchen = Markieren, ein benannter Schlüssel im Antwortteil (mehrfach, reihenfolge, zuordnen, gruppieren, wahrfalsch, tabelle, diagnose, verbinden, berechnung, sachaufgabe, hotspot, code, sqlbaukasten, schreibtischtest, nutzwert, ablaufplan, klassendiagramm, subnetz) = die jeweilige Form; eine einfache Liste ist Multiple Choice, die erste Antwort die richtige.

Antwort: 201 mit fragen, antworten, eingeordnet, leicht (gesetzte leichte Fassungen) und den Kennungen. 422 nennt jede Beanstandung einzeln - geschrieben wird erst, wenn alle Fragen tragen.

PATCH /v1/kurse/{id}/fragen/{frage_id} fragen: schreiben

Eine Frage stilllegen oder wieder in Betrieb nehmen; Einordnung, Kontext, Erklärung und die Fassung in einfacher Sprache setzen.

Text und Antworten lassen sich nicht ändern: Eine geänderte Frage ist eine andere Frage, und die Ergebnisse der alten wären nicht mehr zu deuten. Stilllegen und neu anlegen ist der Weg. Die leichte Fassung ist dieselbe Frage in anderen Worten und lässt sich deshalb jederzeit nachtragen.

Felder im Rumpf

aktiv
false nimmt die Frage aus Training und Examen.
thema
Titel eines Themas aus GET themen; null löst die Einordnung.
stufe
1 bis 6 oder null.
kontext
Der Text, der vor der Frage steht; null nimmt ihn weg.
erklaerung
Erscheint im Training nach der Antwort; null nimmt sie weg.
erklaerung_leicht
Die Erklärung in einfacher Sprache.
text_leicht
Die Frage in einfacher Sprache; null nimmt sie weg.
kontext_leicht
Der Kontext in einfacher Sprache; null nimmt ihn weg.
antworten_leicht
Je Antwort die leichte Fassung, zugeordnet über den genauen Wortlaut: {"Wortlaut": "leichte Fassung"} oder eine Liste aus {"antwort": "…", "leicht": "…"}. Ein unbekannter Wortlaut ergibt 422, geschrieben wird dann nichts.

Antwort: id, aktiv, thema, stufe, kontext, kontext_leicht, erklaerung, erklaerung_leicht, text_leicht und leicht (Zahl der gesetzten leichten Fassungen).

DELETE /v1/kurse/{id}/fragen/{frage_id} fragen: schreiben

Eine Frage samt Antworten löschen.

Antwort: 409, sobald jemand sie beantwortet hat - dann bleibt nur das Stilllegen.

GET /v1/kurse/{id}/herausforderung fragen: lesen

Die Herausforderung eines Kurses: Transferkarten mit ihren Aufgaben und Optionen.

409 bei Kursen aus dem globalen Katalog - außer mit Katalogpflege.

Antwort: titel, beschreibung, umfang, prozent_pass, wiederholung_tage, freigegeben, vorrat und karten - im Aufbau, den PUT annimmt, auch stillgelegte Karten und Aufgaben (aktiv: false). 404, wenn der Kurs keine hat.

PUT /v1/kurse/{id}/herausforderung fragen: schreiben

Die Herausforderung eines Kurses anlegen oder fortschreiben.

Karten und Aufgaben werden an ihrem schluessel wiedererkannt, nicht am Wortlaut. Was im Rumpf fehlt, wird stillgelegt und nicht gelöscht: Ein laufender Durchgang kommt damit zu Ende. Geprüft und geschrieben wird mit derselben Klasse wie beim Einspielen (src/Transferpflege.php). Reicht der vorrat nicht für den umfang, lässt sich kein Lauf starten.

Felder im Rumpf

titel
Pflicht.
beschreibung
Optional.
umfang
Aufgaben je Lauf, Vorgabe 12.
prozent_pass
Bestehensgrenze, Vorgabe 70.
wiederholung_tage
Abstand bis zur Wiederholung, Vorgabe 28.
karten
Pflicht: Liste. Je Karte schluessel, thema, prinzip, fehluebertragung (alle Pflicht), typ, verankerung und aufgaben. Je Aufgabe schluessel, lage, frage (Pflicht), format (mc, wahrfalsch, zuordnen, reihenfolge, berechnung, diagnose), stufe, hinweis, loesung, anker (Textanfang einer Frage des Kurses), die leichten Fassungen lage_leicht, frage_leicht, hinweis_leicht und optionen. Je Option text, leicht, korrekt, fehlue (die Fehlübertragung), zuordnung.
freigeben
Optional: true macht die Herausforderung für Lernende sichtbar, false nimmt sie zurück. Ohne Angabe bleibt der Stand; eine neue ist nicht freigegeben.
probe
Optional: true prüft nur und schreibt nichts.

Antwort: Der neue Stand wie bei GET, dazu bericht (neu, geaendert, stillgelegt, vorrat, umfang) und hinweise. 201 beim ersten Anlegen. 422 nennt jede Beanstandung.

Kursstatistik

Kennzahlen aus Trainings und Examen je Kurs, Seminar und Frage lesen

GET /v1/kurse/{id}/statistik kursstatistik: lesen

Was echte Trainings und Examen über den Kurs sagen: Kennzahlen, Verlauf je Seminar, Lernziel je Seminar, Urteil je Frage, Handlungsbefunde.

Bei Katalogkursen zählen nur Personen der eigenen Einrichtung; befunde.nutzung ist dort null, weil sie über alle Einrichtungen rechnet - befunde.auswertung (Reichweite, Training gegen Prüfung, Datenlage) folgt derselben Grenze und ist deshalb immer da. missverstaendlich heißt: Starke scheitern wie Schwache, ein Ablenker zieht mehr als die Lösung, oder Lernende melden die Lösung als falsch oder mehrdeutig.

Parameter in der Adresse

fragen
0: ohne Fragenliste - nur Seminare und Befunde, deutlich schneller.

Antwort: kurstyp (eigen oder peritio), kursqualitaet (false bei peritio-Kursen: urteile und befunde nicht als Urteil über den Kurs anzeigen), nur_eigene_personen, kennzahlen (teilnehmende, aktiv_30_tage, abgeschlossen, examen_erstversuche, examen_erst_bestanden, examen_erst_quote), verlauf je Seminar (stufen: einfuehrung, training, training_ok, examen, examen_ok - Personen - und auffaelliger_verlust), datenbasis (alle oder einrichtung), schwellen, urteile (Zahl aktiver Fragen je Urteil) und je Seminar training und examen (personen, laeufe, median_prozent - je Person Median der Trainings bzw. erster Examensversuch; dazu erstversuch_bestanden_prozent und lernziel: erreicht, wackelt, verfehlt, zu_wenig, ohne_daten). Je Frage urteil (missverstaendlich, zu_schwer, nachbessern, zu_leicht, passend, zu_wenig_daten; so auch sortiert), antworten und loesungsquote aus der ersten Begegnung je Person, trennschaerfe (erst ab 50 Antworten, sonst null), je_modus (training, examen: alle Antworten), ablenker, offene rueckmeldungen und gruende mit art und text. Je Seminar steht ausserdem personen: wie viele das Seminar ueberhaupt erreicht haben. befunde: aufbau, nutzung und auswertung, je Eintrag ton, titel, handlung, grund, link.

Zertifikate

Zertifikate und PDF lesen

GET /v1/personen/{id}/zertifikate zertifikate: lesen

Zertifikate einer Person.

Antwort: Je Zertifikat Code, Kurs, Datum und Ergebnis.

GET /v1/zertifikate/{code} zertifikate: lesen

Ein Zertifikat über seinen Code.

Antwort: Das Zertifikat; 404, wenn der Code zu einer anderen Einrichtung gehört.

GET /v1/zertifikate/{code}/pdf zertifikate: lesen

Das Dokument als PDF.

Antwort: Das PDF als base64 im Feld inhalt - so bleibt die Antwort JSON.

Rückmeldungen zu Fragen

pseudonym lesen; Status setzen

GET /v1/rueckmeldungen rueckmeldungen: lesen

Rückmeldungen von Lernenden zu einzelnen Fragen.

Parameter in der Adresse

limit
Wie viele Einträge (1 bis 200, Standard 50).
cursor
Weiterlesen ab dieser Kennung; steht als naechster_cursor in der Antwort.
status
offen, erledigt oder verworfen.
kurs
Nur zu diesem Kurs.

Antwort: Bewertung, Text und Fundstelle - ohne Namen, die Rückmeldungen sind pseudonym.

PATCH /v1/rueckmeldungen/{id} rueckmeldungen: schreiben

Den Status einer Rückmeldung setzen.

Felder im Rumpf

status
offen, erledigt oder verworfen.

Antwort: Die Rückmeldung mit neuem Status.

Einrichtung

Stammdaten, Branding, Buchungen, Rechnungen und Schlüssel lesen; Schlüssel sperren, Admin-Rolle vergeben oder entziehen

GET /v1/einrichtung einrichtung: lesen

Stammdaten und Branding der eigenen Einrichtung.

Branding pflegt die Plattform; andere Einrichtungen sind nie erreichbar.

Antwort: Kürzel, Name, Adresse, Farben, Zertifikats- und Rechtsangaben, welche Bilder hinterlegt sind.

GET /v1/einrichtung/buchungen einrichtung: lesen

Gebuchte Pakete mit Preisen und Laufzeit.

Antwort: Je Buchung Paket, Preise in Cent, gültig ab und bis; dazu die Zahl der Lernenden jetzt.

GET /v1/einrichtung/rechnungen einrichtung: lesen

Gestellte Rechnungen und Gutschriften.

Antwort: Je Rechnung Nummer, Zeitraum, Beträge in Cent, Status und Fälligkeit. Entwürfe fehlen.

GET /v1/einrichtung/rechnungen/{id}/pdf einrichtung: lesen

Eine Rechnung als PDF.

Antwort: Das PDF als base64 im Feld inhalt_base64.

GET /v1/einrichtung/schluessel einrichtung: lesen

Die API-Schlüssel der Einrichtung.

Antwort: Je Schlüssel Name, Anfang, Rechte, Grenzen, letzte Nutzung; dieser: true beim eigenen. Nie das Geheimnis.

PUT /v1/einrichtung/schluessel/{id}/sperre einrichtung: schreiben

Einen Schlüssel sofort sperren, etwa wenn er in fremde Hände geraten ist.

Anlegen und Entsperren gehen nur in der Verwaltung - ein Schlüssel soll sich keine Rechte verschaffen können.

Antwort: Der Schlüssel ist gesperrt; ein zweiter Aufruf ändert nichts.