heftwerkAnmeldenRegistrieren

Dokumentation der Schnittstelle

Über die Schnittstelle liest die IT eines Ausbildungsbetriebs Azubis, bestätigte Wochen, Abwesenheiten und Nachweise und legt Azubis an. Diese Seite beschreibt Anmeldung, Wege, Fehlercodes und Grenzen.

Stand: 17.10.2026

In Entwicklung: Die Schnittstelle wird freigeschaltet, sobald sie fertig geprüft ist. Bis dahin lassen sich keine Zugangsschlüssel anlegen, und ein vorhandener Schlüssel erhält 403 noch_nicht_freigeschaltet.

Basis-URL und Anmeldung

Alle Wege beginnen mit https://heftwerk.app/api/v1/extern. Anfragen und Antworten sind JSON in UTF-8, nur der Nachweis kommt als PDF.

Jede Anfrage trägt den Zugangsschlüssel in der Kopfzeile Authorization:

Authorization: Bearer hw_live_…

Zugangsschlüssel legt in heftwerk an, wer den Betrieb verwaltet: die Inhaber und die Personen mit Verwaltungsrecht, unter Schnittstellen. Der Schlüssel erscheint genau einmal, direkt nach dem Anlegen; gespeichert wird danach nur seine Prüfsumme. Ein Schlüssel gehört zu genau einem Betrieb und sieht nur dessen Daten.

Umfänge

Beim Anlegen wird festgelegt, was der Schlüssel darf. Fehlt ihm der Umfang eines Wegs, antwortet der Weg mit 403 umfang_fehlt.

UmfangErlaubt
azubis:lesenAzubis des Betriebs lesen.
azubis:schreibenAzubis anlegen und ihre Stammdaten ändern.
wochen:lesenBestätigte Wochen und die Abwesenheiten daraus lesen.
nachweise:lesenDen Nachweis einer bestätigten Woche als PDF abrufen.

Gültigkeit und Widerruf der Schlüssel

Wie lange ein Schlüssel gilt, wird beim Anlegen gewählt: 1 Stunde, 1 Woche, 1 Monat, 1 Jahr, 10 Jahre oder unbegrenzt. Vorausgewählt ist 1 Jahr. Nach Ablauf antwortet jeder Weg mit 401 schluessel_abgelaufen; dann ist ein neuer Schlüssel nötig.

Je Betrieb gelten höchstens 20 Schlüssel zugleich. Gezählt werden die, die weder widerrufen noch abgelaufen sind.

Die Verwaltung kann einen Schlüssel jederzeit widerrufen; er gilt dann ab der nächsten Anfrage nicht mehr. Wird die Person, die ihn angelegt hat, aus dem Team entfernt oder verliert sie das Verwaltungsrecht, werden ihre Schlüssel widerrufen.

Jeder Zugriff mit einem Schlüssel wird protokolliert: Zeitpunkt, Methode, Weg ohne Abfrageparameter, Status, Dauer und Absenderadresse. Die Verwaltung sieht die Zugriffe der letzten 90 Tage beim jeweiligen Schlüssel; danach werden sie gelöscht.

Wege

GET /api/v1/extern/azubis

Azubis auflisten. Die Azubis des Betriebs ohne archivierte, sortiert nach Kennung, in Seiten.

Umfang: azubis:lesen

ParameterOrtPflichtArt und Bedeutung
limitAbfrageneinganze Zahl, 1 bis 200, ohne Angabe 50. Einträge je Seite.
cursorAbfrageneinText, 0 bis 200 Zeichen. naechsterCursor der vorigen Seite.

Beispielantwort mit Status 200:

{
  "daten": [
    {
      "id": "cmgw79z2k0003lukas",
      "vorname": "Lukas",
      "nachname": "Brenner",
      "email": "[email protected]",
      "beruf": "Kaufmann für Büromanagement",
      "ausbildungsbeginn": "2025-08-01",
      "ausbildungsende": "2028-07-31",
      "standort": "Werk Nord",
      "externeId": "P-10427"
    }
  ],
  "naechsterCursor": null
}

Fehlercodes: unbekannter_parameter (400), ungueltige_eingabe (400), unauthorized (401), schluessel_abgelaufen (401), noch_nicht_freigeschaltet (403), betrieb_angehalten (403), umfang_fehlt (403), rate_limit (429), interner_fehler (500)

POST /api/v1/extern/azubis

Azubi anlegen. Legt einen Azubi an, mit denselben Regeln wie in heftwerk: angenommener Auftragsverarbeitungsvertrag, freie E-Mail-Adresse, bei Minderjährigen eine Adresse der Erziehungsberechtigten. Der Azubi bekommt eine Einladung per E-Mail und meldet sich ohne Passwort an. Überschreitet er die gebuchte Preisstufe, wird nicht höhergestuft: Die Antwort ist 409 stufe_voll. Hat der Betrieb das Anlegen auf die Verwaltung und den Bereich „Azubis anlegen“ beschränkt, gilt das auch für den Schlüssel, gemessen an der Person, die ihn angelegt hat. Mit externeId lässt sich der Aufruf gefahrlos wiederholen: Ist die Kennung schon vergeben, kommt 409 externe_id_vergeben mit der Kennung des Azubis.

Umfang: azubis:schreiben

Feld im KörperPflichtArt und Bedeutung
vornamejaText, 1 bis 80 Zeichen
nachnamejaText, 1 bis 80 Zeichen
emailjaE-Mail-Adresse. Die Anmeldung des Azubis. Später nur in heftwerk änderbar.
geburtsdatumneinTag JJJJ-MM-TT. Kalendertag als JJJJ-MM-TT.
berufneinText, 1 bis 120 Zeichen
ausbildungsjahrneinganze Zahl, 1 bis 5
ausbildungsbeginnneinTag JJJJ-MM-TT. Kalendertag als JJJJ-MM-TT.
nachweisExternBisneinTag JJJJ-MM-TT. Ein Tag aus der letzten Woche, die noch anderswo geführt wurde, etwa im Berichtsheft der IHK.
angerechneteMonateneinganze Zahl, 0 bis 36
benachrichtigungsadressenneinListe, höchstens 10 Einträge
externeIdneinText, 1 bis 100 Zeichen. Kennung im System des Betriebs, je Betrieb eindeutig.

Beispiel für den Körper:

{
  "vorname": "Lukas",
  "nachname": "Brenner",
  "email": "[email protected]",
  "beruf": "Kaufmann für Büromanagement",
  "ausbildungsbeginn": "2025-08-01",
  "externeId": "P-10427"
}

Beispielantwort mit Status 201:

{
  "daten": {
    "id": "cmgw79z2k0003lukas",
    "vorname": "Lukas",
    "nachname": "Brenner",
    "email": "[email protected]",
    "beruf": "Kaufmann für Büromanagement",
    "ausbildungsbeginn": "2025-08-01",
    "ausbildungsende": "2028-07-31",
    "standort": "Werk Nord",
    "externeId": "P-10427"
  },
  "einladungVersendet": true
}

Fehlercodes: unbekannter_parameter (400), ungueltige_eingabe (400), ungueltiges_json (400), erziehungsberechtigter_pflicht (400), erziehungsberechtigter_volljaehrig (400), unauthorized (401), schluessel_abgelaufen (401), noch_nicht_freigeschaltet (403), betrieb_angehalten (403), umfang_fehlt (403), vertrag_ruht (403), anlegen_nur_mit_bereich (403), externe_id_vergeben (409), email_bereits_verwendet (409), stufe_voll (409), staffel_auf_anfrage (409), avv_nicht_angenommen (409), zu_gross (413), falscher_inhaltstyp (415), rate_limit (429), interner_fehler (500)

PATCH /api/v1/extern/azubis/{id}

Stammdaten eines Azubis ändern. Ändert nur, was mitkommt; null nimmt eine Angabe zurück. Dieselben Regeln wie in heftwerk. Die E-Mail-Adresse lässt sich hier nicht ändern, weil sie die Anmeldung des Azubis umstellt. Namen ändert der Betrieb in heftwerk.

Umfang: azubis:schreiben

ParameterOrtPflichtArt und Bedeutung
idPfadjaText. Kennung des Azubis aus GET /azubis.
Feld im KörperPflichtArt und Bedeutung
berufneinText, 1 bis 120 Zeichen oder null
ausbildungsbeginnneinTag JJJJ-MM-TT oder null. Kalendertag als JJJJ-MM-TT, null nimmt die Angabe zurück.
ausbildungsendeneinTag JJJJ-MM-TT oder null. Kalendertag als JJJJ-MM-TT, null nimmt die Angabe zurück.
nachweisExternBisneinTag JJJJ-MM-TT oder null. Kalendertag als JJJJ-MM-TT, null nimmt die Angabe zurück.
probezeitMonateneinganze Zahl, 1 bis 4 oder null
geburtsdatumneinTag JJJJ-MM-TT oder null. Kalendertag als JJJJ-MM-TT, null nimmt die Angabe zurück.
externeIdneinText, 1 bis 100 Zeichen oder null. Kennung im System des Betriebs, je Betrieb eindeutig.

Beispiel für den Körper:

{
  "ausbildungsende": "2028-01-31",
  "externeId": "P-10427"
}

Beispielantwort mit Status 200:

{
  "daten": {
    "id": "cmgw79z2k0003lukas",
    "vorname": "Lukas",
    "nachname": "Brenner",
    "email": "[email protected]",
    "beruf": "Kaufmann für Büromanagement",
    "ausbildungsbeginn": "2025-08-01",
    "ausbildungsende": "2028-01-31",
    "standort": "Werk Nord",
    "externeId": "P-10427"
  }
}

Fehlercodes: unbekannter_parameter (400), ungueltige_eingabe (400), ungueltiges_json (400), email_nicht_aenderbar (400), ende_vor_beginn (400), extern_vor_beginn (400), extern_nach_ende (400), ungueltiges_geburtsdatum (400), erziehungsberechtigter_pflicht (400), unauthorized (401), schluessel_abgelaufen (401), noch_nicht_freigeschaltet (403), betrieb_angehalten (403), umfang_fehlt (403), vertrag_ruht (403), nicht_gefunden (404), externe_id_vergeben (409), azubi_archiviert (409), zu_gross (413), falscher_inhaltstyp (415), rate_limit (429), interner_fehler (500)

GET /api/v1/extern/wochen

Bestätigte Wochen auflisten. Nur bestätigte Wochen, also Nachweise, von Azubis ohne Archiv, sortiert nach Kennung, in Seiten. Der Parameter status bleibt aus Gründen der Verträglichkeit erlaubt; jeder andere Wert als der Status bestätigter Wochen ergibt eine leere Liste.

Umfang: wochen:lesen

ParameterOrtPflichtArt und Bedeutung
limitAbfrageneinganze Zahl, 1 bis 200, ohne Angabe 50. Einträge je Seite.
cursorAbfrageneinText, 0 bis 200 Zeichen. naechsterCursor der vorigen Seite.
azubiIdAbfrageneinText, 0 bis 60 Zeichen. Nur die Wochen dieses Azubis.
jahrAbfrageneinganze Zahl, 2000 bis 2100. Nur Wochen dieses Jahres.
statusAbfrageneineiner von: entwurf, eingereicht, vorgeprueft, bestaetigt, zurueckgegeben. Nur der Status bestätigter Wochen liefert Einträge.

Beispielantwort mit Status 200:

{
  "daten": [
    {
      "id": "cmgw7a1x80001lukas39",
      "azubiId": "cmgw79z2k0003lukas",
      "jahr": 2026,
      "kalenderwoche": 39,
      "von": "2026-09-21",
      "bis": "2026-09-27",
      "art": "betrieb",
      "status": "bestaetigt",
      "abteilung": "Auftragsbearbeitung",
      "anzahlEintraege": 5,
      "eingereichtAm": "2026-09-25T15:00:00.000Z",
      "genehmigtVon": "[email protected]",
      "genehmigtAm": "2026-09-28T08:00:00.000Z"
    }
  ],
  "naechsterCursor": null
}

Fehlercodes: unbekannter_parameter (400), ungueltige_eingabe (400), unauthorized (401), schluessel_abgelaufen (401), noch_nicht_freigeschaltet (403), betrieb_angehalten (403), umfang_fehlt (403), rate_limit (429), interner_fehler (500)

GET /api/v1/extern/abwesenheiten

Abwesenheiten eines Jahres. Urlaub, Krankheit, Freistellung und Feiertage je Tag, nur aus bestätigten Wochen. Ohne Blättern.

Umfang: wochen:lesen

ParameterOrtPflichtArt und Bedeutung
jahrAbfragejaganze Zahl, 2000 bis 2100. Das Jahr.
azubiIdAbfrageneinText, 0 bis 64 Zeichen. Nur dieser Azubi.

Beispielantwort mit Status 200:

{
  "daten": [
    {
      "azubiId": "cmgw79z2k0003lukas",
      "datum": "2026-10-05",
      "art": "urlaub",
      "stunden": 0,
      "wocheId": "cmgw7a1x80001lukas41"
    }
  ]
}

Fehlercodes: unbekannter_parameter (400), ungueltige_eingabe (400), unauthorized (401), schluessel_abgelaufen (401), noch_nicht_freigeschaltet (403), betrieb_angehalten (403), umfang_fehlt (403), rate_limit (429), interner_fehler (500)

GET /api/v1/extern/nachweise/{wocheId}.pdf

Nachweis einer Woche als PDF. Der Nachweis einer bestätigten Woche. Was die Liste der Wochen nicht zeigt, gibt es auch hier nicht.

Umfang: nachweise:lesen

ParameterOrtPflichtArt und Bedeutung
wocheIdPfadjaText. Kennung der Woche aus GET /wochen.

Antwort mit Status 200: Das PDF, als Anhang nachweis-JJJJ-KWnn.pdf.

Fehlercodes: unbekannter_parameter (400), unauthorized (401), schluessel_abgelaufen (401), noch_nicht_freigeschaltet (403), betrieb_angehalten (403), umfang_fehlt (403), nicht_gefunden (404), rate_limit (429), interner_fehler (500)

Fehler

Jede Fehlerantwort hat diese Form:

{ "error": "umfang_fehlt", "meldung": "Diesem Schlüssel fehlt der Umfang für diesen Weg." }

error ist ein fester Code, an dem ein Programm entscheiden kann. meldung erklärt ihn in Worten und kann sich ändern. Manche Antworten tragen weitere Felder, etwa azubiId bei externe_id_vergeben.

StatusCodeBedeutung
400unbekannter_parameterAbfrage oder Körper enthält ein Feld, das der Weg nicht kennt. Die Meldung nennt es.
400ungueltige_eingabeEin Pflichtfeld fehlt, oder ein Feld hat die falsche Form. Die Meldung nennt die Felder.
400ungueltiges_jsonDer Körper ist kein gültiges JSON.
400email_nicht_aenderbarDie E-Mail-Adresse lässt sich über die Schnittstelle nicht ändern.
400erziehungsberechtigter_pflichtFür Minderjährige fehlt eine Benachrichtigungsadresse vom Typ erziehungsberechtigter.
400erziehungsberechtigter_volljaehrigDer Azubi ist nach dem Geburtsdatum volljährig; eine Adresse vom Typ erziehungsberechtigter lässt sich nicht eintragen.
400ende_vor_beginnDas Ende der Ausbildung liegt vor ihrem Beginn.
400extern_vor_beginnnachweisExternBis liegt vor dem Beginn der Ausbildung.
400extern_nach_endenachweisExternBis liegt nach dem Ende der Ausbildung.
400ungueltiges_geburtsdatumDas Geburtsdatum liegt in der Zukunft oder vor 1950.
400ungueltige_anfrageDie Anfrage lässt sich nicht verarbeiten.
401unauthorizedDer Schlüssel fehlt, ist unbekannt, widerrufen oder falsch geschrieben. Die Antwort unterscheidet das bewusst nicht.
401schluessel_abgelaufenDie Gültigkeit des Schlüssels ist abgelaufen. Ein neuer Schlüssel ist nötig.
403noch_nicht_freigeschaltetDie Schnittstelle ist noch nicht freigeschaltet.
403umfang_fehltDem Schlüssel fehlt der Umfang, den der Weg verlangt.
403betrieb_angehaltenDer Zugang des Betriebs ist eingeschränkt. Bei einer Schreibsperre bleibt Lesen möglich.
403vertrag_ruhtFür den Betrieb läuft kein Vertrag. Lesen bleibt möglich, Anlegen und Ändern nicht.
403anlegen_nur_mit_bereichDer Betrieb lässt Azubis nur durch die Verwaltung und den Bereich „Azubis anlegen“ anlegen, und wer den Schlüssel angelegt hat, hat dieses Recht nicht.
404nicht_gefundenDie Kennung gibt es nicht oder nicht in diesem Betrieb. Beides antwortet gleich.
409externe_id_vergebenDie externeId trägt schon ein Azubi des Betriebs. azubiId nennt ihn.
409email_bereits_verwendetDie E-Mail-Adresse ist in heftwerk schon vergeben.
409stufe_vollDer Azubi überschreitet die gebuchte Preisstufe. Die Höherstufung lässt sich nur in heftwerk bestätigen.
409staffel_auf_anfrageAb 81 Azubis gilt ein individuelles Angebot.
409avv_nicht_angenommenDer Betrieb hat den Auftragsverarbeitungsvertrag noch nicht angenommen.
409azubi_archiviertDer Azubi ist archiviert und lässt sich nicht ändern.
413zu_grossDie Anfrage ist zu groß.
415falscher_inhaltstypDer Körper kommt nicht als application/json.
429rate_limitZu viele Aufrufe. Retry-After nennt die Wartezeit in Sekunden.
500interner_fehlerEin Fehler auf unserer Seite. Die Antwort enthält keine Einzelheiten.

Grenzen, Blättern und Zeitformat

Je Schlüssel gelten höchstens 120 Aufrufe in der Minute, je Absenderadresse höchstens 600. Darüber antwortet die Schnittstelle mit 429 rate_limit und der Kopfzeile Retry-After, die die Wartezeit in Sekunden nennt.

Listen kommen in Seiten. limit legt die Größe fest, 1 bis 200, ohne Angabe 50. Jede Seite nennt naechsterCursor; diesen Wert als cursor mitschicken, um die nächste Seite zu holen. Ist naechsterCursor gleich null, ist die Liste zu Ende. Sortiert wird nach Kennung, so verschiebt ein neu angelegter Datensatz keine Seite.

Zeitpunkte stehen in ISO 8601 und UTC, etwa 2026-09-28T08:00:00.000Z. Kalendertage stehen als JJJJ-MM-TT, etwa 2026-09-21.

Abfrage und Körper sind streng: Ein Parameter oder Feld, das der Weg nicht kennt, ergibt 400 unbekannter_parameter. So fällt ein Tippfehler auf, statt still übergangen zu werden. Jede Antwort trägt Cache-Control: no-store.

Versionen und Abkündigung

Die Version steht im Pfad, heute v1. Innerhalb von v1 kommen Felder und Wege hinzu; es fällt nichts weg, und kein Feld ändert seine Bedeutung. Programme sollten unbekannte Felder in Antworten überlesen.

Soll ein Weg oder Feld wegfallen, kündigen wir das mindestens sechs Monate vorher an, auf dieser Seite und in den Antworten mit den Kopfzeilen Deprecation und Sunset. Sunset nennt den Tag, ab dem der Weg nicht mehr antwortet. Heute ist nichts abgekündigt.

Sicherheitshinweise

Beispiele mit curl

Die Beispiele erwarten den Schlüssel in der Umgebungsvariablen HEFTWERK_SCHLUESSEL.

Azubis auflisten, 50 je Seite:

curl -H "Authorization: Bearer $HEFTWERK_SCHLUESSEL" \
  "https://heftwerk.app/api/v1/extern/azubis?limit=50"

Die nächste Seite, mit naechsterCursor der vorigen:

curl -H "Authorization: Bearer $HEFTWERK_SCHLUESSEL" \
  "https://heftwerk.app/api/v1/extern/azubis?limit=50&cursor=cmgw79z2k0003lukas"

Bestätigte Wochen eines Jahres:

curl -H "Authorization: Bearer $HEFTWERK_SCHLUESSEL" \
  "https://heftwerk.app/api/v1/extern/wochen?jahr=2026"

Einen Azubi anlegen, mit externer Kennung:

curl -X POST -H "Authorization: Bearer $HEFTWERK_SCHLUESSEL" \
  -H "Content-Type: application/json" \
  -d '{"vorname":"Lukas","nachname":"Brenner","email":"[email protected]","externeId":"P-10427"}' \
  "https://heftwerk.app/api/v1/extern/azubis"

Das Ende der Ausbildung ändern:

curl -X PATCH -H "Authorization: Bearer $HEFTWERK_SCHLUESSEL" \
  -H "Content-Type: application/json" \
  -d '{"ausbildungsende":"2028-01-31"}' \
  "https://heftwerk.app/api/v1/extern/azubis/cmgw79z2k0003lukas"

Den Nachweis einer Woche als PDF speichern:

curl -H "Authorization: Bearer $HEFTWERK_SCHLUESSEL" -o nachweis.pdf \
  "https://heftwerk.app/api/v1/extern/nachweise/cmgw7a1x80001lukas39.pdf"

Maschinenlesbare Beschreibung

Dieselben Wege als OpenAPI 3.1, ohne Anmeldung abrufbar: /api/v1/openapi.json.