Ressourcen

API-Dokumentation

Lesender Zugriff auf die Nachweise Ihres Unternehmens – für Steuerberatung, Fördermittelberatung und das eigene Controlling. Diese Seite beschreibt alle Endpunkte der Partner-API, Fassung 1, mit Beispielaufrufen, Antworten und Grenzen.

Stand: 20.09.2026

Wofür die API da ist

Über die API holt ein Programm die Daten ab, die Sie sonst in der Oberfläche ansehen oder herunterladen: Vorhaben, Arbeitspakete, Personen mit ihren Stammdaten, Zeiteinträge und die fertigen Exporte als PDF, Excel und CSV. Typische Fälle:

  • Steuerberatung: Die Kanzlei ruft Stundenzettel und Zeiten selbst ab, ohne dass jemand Dateien verschickt oder ein Passwort weitergibt.
  • Fördermittelberatung: Stunden je Vorhaben und Arbeitspaket fließen in die eigene Auswertung.
  • Eigenes Controlling oder BI: Ihr Auswertungswerkzeug liest die Zeiteinträge regelmäßig ein.

Was die API nicht enthält: Sie ist ausschließlich lesend. Zeiten erfassen, Personen anlegen oder Stammdaten ändern ist darüber nicht möglich. Abrechnung, Rechnungen, Nutzerverwaltung, Einladungen und das Änderungsprotokoll sind nicht Teil der API. Wer ein Token vergibt, gibt Einblick in die Nachweise – nicht in den Vertrag.

Token erzeugen

  1. Melden Sie sich als Inhaber des Kontos an. Andere Rollen sehen diese Seite nicht.
  2. Öffnen Sie Einstellungen → API-Zugänge.
  3. Geben Sie dem Token einen Namen, an dem Sie es später wiedererkennen, zum Beispiel den Namen der Kanzlei, und wählen Sie „Token erzeugen“.
  4. Kopieren Sie den angezeigten Schlüssel sofort. Er wird genau einmal im Klartext angezeigt. Gespeichert wird danach nur sein Hash; auch wir können ihn nicht wiederherstellen. Geht er verloren, erzeugen Sie ein neues Token.
EigenschaftWert
Gültigkeitein Jahr (365 Tage) ab Erzeugung
Rechtenur lesend, nur Pfade unter /api/v1, nur die Daten Ihres Kontos
Widerrufjederzeit in derselben Liste; wirkt spätestens nach einer Minute
Anzahlhöchstens 10 gültige Token je Konto
Anfragen600 Anfragen je Token in 10 Minuten

Die Liste unter API-Zugänge zeigt je Token den Namen, die letzten Zeichen des Schlüssels, das Ablaufdatum und wann es zuletzt benutzt wurde. Die Angabe „zuletzt benutzt“ wird höchstens alle fünf Minuten aktualisiert.

Anmeldung und Basis-URL

Alle Endpunkte liegen unter dieser Basis-URL:

Basis-URL
https://api.fue-stundenzettel.de/api/v1

Das Token steht bei jeder Anfrage im Kopf Authorization:

HTTP-Kopf
Authorization: Bearer <Ihr Token>

In den Beispielen unten steht das Token in der Umgebungsvariable TOKEN. Zu welchem Konto eine Anfrage gehört, ergibt sich allein aus dem Token – eine Konto-Kennung in der Adresse gibt es nicht.

Erster Aufruf
export TOKEN="<Ihr Token>"
curl -H "Authorization: Bearer $TOKEN" \
  "https://api.fue-stundenzettel.de/api/v1/projects"

Für alle Endpunkte gilt:

  • Die Endpunkte antworten auf GET. Antworten sind JSON in UTF-8, die Exporte sind Dateien.
  • Feldnamen stehen in camelCase. Felder ohne Wert sind null.
  • Datumsangaben in Abfragen schreiben Sie als YYYY-MM-DD. Gemeint ist die deutsche Ortszeit (Europe/Berlin).
  • Kennungen von Vorhaben, Arbeitspaketen, Personen und Zeiteinträgen sind UUIDs.
  • Dezimalzahlen können mit unterschiedlich vielen Nachkommastellen ankommen (32.0 oder 32.000). Werten Sie sie als Zahl aus, nicht als Text.

Endpunkte

PfadInhalt
/projectsalle Vorhaben des Kontos
/projects/{projectId}/workpackagesArbeitspakete eines Vorhabens
/personsPersonen mit den Stammdaten eines Jahres
/timeentriesZeiteinträge, seitenweise
/exports/pdfStundenzettel als PDF (Monat oder Jahr)
/exports/excelJahresmappe als Excel-Datei
/exports/csvZeiteinträge als CSV

Die Beispielantworten stammen aus unserem Demo-Konto mit erfundenen Personen und sind gekürzt.

GET/api/v1/projects

Alle Vorhaben des Kontos, sortiert nach Name. Keine Parameter.

Aufruf
curl -H "Authorization: Bearer $TOKEN" \
  "https://api.fue-stundenzettel.de/api/v1/projects"
Antwort (Beispiel)
{
  "items": [
    {
      "id": "e225b654-bba4-4260-97ae-3fdbf8d13e46",
      "name": "KIFEA: KI-gestützte Fehleranalyse-Engine",
      "fueId": "FuE-2026-KIFEA-001",
      "description": "Entwicklung eines selbstlernenden Algorithmus …",
      "startDate": "2026-01-01T00:00:00",
      "endDate": "2026-12-31T00:00:00",
      "isActive": true
    }
  ],
  "count": 1
}

fueId ist die Vorhabens-Kennung, die Sie im Vorhaben hinterlegt haben. endDate ist null, solange kein Ende eingetragen ist.

GET/api/v1/projects/{projectId}/workpackages

Die Arbeitspakete eines Vorhabens, sortiert nach Name. projectId ist die id aus /projects. targetHours sind die geplanten Stunden des Arbeitspakets.

Aufruf
curl -H "Authorization: Bearer $TOKEN" \
  "https://api.fue-stundenzettel.de/api/v1/projects/e225b654-bba4-4260-97ae-3fdbf8d13e46/workpackages"
Antwort (Beispiel)
{
  "items": [
    {
      "id": "6a74d3e6-4a03-4a57-9489-1b755325b107",
      "projectId": "e225b654-bba4-4260-97ae-3fdbf8d13e46",
      "name": "AP 1: Konzeption & Anforderungsspezifikation",
      "description": "Erstellung des Projektkonzepts …",
      "targetHours": 220,
      "startDate": "2026-01-01T00:00:00",
      "endDate": "2026-02-28T00:00:00"
    }
  ],
  "count": 1
}

GET/api/v1/persons

Alle Personen des Kontos mit den Stammdaten eines Jahres, sortiert nach Nachname und Vorname.

ParameterBedeutung
yearJahr der Stammdaten, optional. Ohne Angabe das laufende Jahr.
Aufruf
curl -H "Authorization: Bearer $TOKEN" \
  "https://api.fue-stundenzettel.de/api/v1/persons?year=2026"
Antwort (Beispiel)
{
  "year": 2026,
  "items": [
    {
      "id": "42fdd229-2bc4-4096-a821-6b73137ed930",
      "firstName": "Max",
      "lastName": "Fischer",
      "email": "max@innovation.ag",
      "isActive": true,
      "isImportPlaceholder": false,
      "year": 2026,
      "profile": {
        "agreedWeeklyHours": 32.0,
        "contractualVacationDays": 30.0,
        "specialLeaveDays": 0.0,
        "shortTimeWorkDays": 0.0,
        "publicHolidays": 11.0,
        "sickDays": 0.5,
        "rateKind": "employed",
        "hourlyRate": 47.00,
        "externalInvoiceAmount": null,
        "activityLabel": "Versuchsingenieur",
        "fueStartMonth": null,
        "fueEndMonth": null,
        "sickDaysOverride": null
      }
    }
  ],
  "count": 1
}

profile ist null, wenn für die Person in diesem Jahr keine Stammdaten gepflegt sind. isImportPlaceholder kennzeichnet Personen ohne eigenen Zugang, die über einen Import oder eine Rekonstruktion angelegt wurden, etwa ehemalige Beschäftigte.

Feld in profileBedeutung
agreedWeeklyHoursvereinbarte Wochenarbeitszeit in Stunden
contractualVacationDaysvertraglicher Urlaub in Tagen
specialLeaveDaysSonderurlaub in Tagen
shortTimeWorkDaysKurzarbeit in Tagen
publicHolidaysFeiertage in Tagen
sickDaysSumme der in der Anwendung erfassten Krankheitstage; ein halber Tag zählt 0,5
sickDaysOverrideJahressumme der Krankheitstage aus dem Lohnsystem, falls hinterlegt. Ist sie gesetzt, rechnet der Stundenzettel mit ihr statt mit sickDays.
rateKindArt des Stundensatzes: employed (Beschäftigte), ownWork (Eigenleistung) oder external (Auftragsforschung)
hourlyRatehinterlegter Stundensatz in Euro. Bei ownWork rechnet die Anwendung mit dem gesetzlichen Satz, nicht mit diesem Feld.
externalInvoiceAmountRechnungsbetrag der Auftragsforschung in Euro, nur bei external
activityLabelKurzbezeichnung der FuE-Tätigkeit, wie sie im Kopf des Stundenzettels steht
fueStartMonth, fueEndMontherster und letzter Monat der FuE-Tätigkeit (1–12). Beide null bedeutet: ganzes Jahr.

GET/api/v1/timeentries

Die Zeiteinträge des Kontos, aufsteigend nach Beginn sortiert und seitenweise geliefert. Alle Parameter sind optional; ohne Filter kommen alle Zeiteinträge.

ParameterBedeutung
from, toerster und letzter Tag, beide einschließlich (Ortszeit)
projectIdnur dieses Vorhaben
personIdnur diese Person (id aus /persons)
pageSizeRichtgröße einer Seite, 1 bis 1.000. Vorgabe und Höchstwert: 1.000. Größere Werte werden auf 1.000 gesetzt.
cursorder Wert nextCursor der vorigen Antwort, siehe Paginierung
Aufruf
curl -H "Authorization: Bearer $TOKEN" \
  "https://api.fue-stundenzettel.de/api/v1/timeentries?from=2026-03-01&to=2026-03-31&pageSize=2"
Antwort (Beispiel)
{
  "items": [
    {
      "id": "cfca066e-573c-4d52-8504-c3962bc543b8",
      "personId": "f18b19ac-7e01-4c6f-a359-8f6950df65c6",
      "person": "Elias Maier",
      "projectId": "e225b654-bba4-4260-97ae-3fdbf8d13e46",
      "project": "KIFEA: KI-gestützte Fehleranalyse-Engine",
      "fueId": "FuE-2026-KIFEA-001",
      "workPackageId": "e35e8808-d01a-43c4-bee6-9166af78eed5",
      "workPackage": "AP 2: Entwicklung des KI-Kern-Algorithmus (PoC)",
      "date": "2026-03-02",
      "start": "2026-03-02T08:45:00",
      "end": "2026-03-02T09:45:00",
      "startUtc": "2026-03-02T07:45:00",
      "endUtc": "2026-03-02T08:45:00",
      "minutes": 60,
      "hours": 1,
      "isFue": true,
      "activity": "Implementierung des Online-Learning-Mechanismus …",
      "origin": "recorded",
      "originNote": null,
      "importBatchId": null
    }
  ],
  "count": 1,
  "pageSize": 2,
  "hasMore": true,
  "nextCursor": "djE6NjM5MDgwMzQzMDAwMDAwMDAw"
}
FeldBedeutung
date, start, endTag, Beginn und Ende in deutscher Ortszeit – so, wie sie auf dem Stundenzettel stehen. end ist null, solange ein Eintrag noch läuft.
startUtc, endUtcdieselben Zeitpunkte in UTC
minutes, hoursDauer in Minuten und in Stunden, auf zwei Nachkommastellen gerundet
isFuetrue für FuE-Zeit, false für sonstige Zeit
activityTätigkeitsbeschreibung des Eintrags
originrecorded: selbst erfasst. distributed: aus einer dokumentierten Summe auf Arbeitstage verteilt (Rekonstruktion); originNote nennt dann die Quelle.
importBatchIdKennung des Rekonstruktionslaufs, aus dem der Eintrag stammt; sonst null

GET/api/v1/exports/pdf

Der Stundenzettel nach BMF-Vorlage als PDF – dieselbe Datei, die Sie in der Oberfläche herunterladen. Mit month erhalten Sie das Monatsblatt, ohne month die Jahresübersicht.

ParameterBedeutung
projectIdVorhaben, Pflicht
yearJahr, Pflicht
monthMonat 1 bis 12, optional
includeFundingtrue ergänzt auf der Jahresübersicht eine Zeile mit Bemessungsgrundlage und erwarteter Forschungszulage in Euro. Vorgabe false; beim Monatsblatt ohne Wirkung.
Aufruf
curl -OJ -H "Authorization: Bearer $TOKEN" \
  "https://api.fue-stundenzettel.de/api/v1/exports/pdf?projectId=e225b654-bba4-4260-97ae-3fdbf8d13e46&year=2026&month=3"

Antwort: application/pdf, Dateiname Stundenzettel_03_2026.pdf beziehungsweise Jahresuebersicht_2026.pdf. Mit -OJ speichert curl die Datei unter diesem Namen.

GET/api/v1/exports/excel

Die Jahresmappe eines Vorhabens als Excel-Datei: ein Blatt mit Stammdaten, je Person ein Jahresblatt und ein Blatt mit allen Zeiteinträgen.

ParameterBedeutung
projectIdVorhaben, Pflicht
yearJahr, Pflicht
Aufruf
curl -OJ -H "Authorization: Bearer $TOKEN" \
  "https://api.fue-stundenzettel.de/api/v1/exports/excel?projectId=e225b654-bba4-4260-97ae-3fdbf8d13e46&year=2026"

Antwort: eine .xlsx-Datei mit dem Namen FuE-Nachweis_2026.xlsx.

GET/api/v1/exports/csv

Die Zeiteinträge als CSV, so aufgebaut, dass die Datei in Excel auf einem deutschen Rechner direkt aufgeht: Semikolon als Trennzeichen, UTF-8 mit Byte-Order-Markierung, Dezimalkomma, Datum als TT.MM.JJJJ, Uhrzeiten in Ortszeit.

ParameterBedeutung
projectIdnur dieses Vorhaben, optional. Ohne Angabe alle Vorhaben des Kontos.
from, toerster und letzter Tag, optional. Ohne Angabe der 1. Januar beziehungsweise der 31. Dezember des laufenden Jahres.
Aufruf
curl -OJ -H "Authorization: Bearer $TOKEN" \
  "https://api.fue-stundenzettel.de/api/v1/exports/csv?from=2026-01-01&to=2026-12-31"
Antwort (Beispiel, gekürzt)
Person;PersonId;Datum;Beginn;Ende;Minuten;Stunden;IsFuE;Vorhaben;VorhabensId;Arbeitspaket;Tätigkeit;Herkunft;ImportBatchId
Elias Maier;f18b19ac-…;02.03.2026;08:45;09:45;60;1,00;ja;KIFEA: KI-gestützte Fehleranalyse-Engine;FuE-2026-KIFEA-001;AP 2: Entwicklung des KI-Kern-Algorithmus (PoC);Implementierung …;Erfasst;

Die Spalte Herkunft enthält „Erfasst“ oder bei rekonstruierten Einträgen den Hinweis auf die Quelle. Der Dateiname lautet Zeiteintraege_2026-01-01_2026-12-31.csv.

Paginierung der Zeiteinträge

Nur /timeentries liefert seitenweise; alle anderen Endpunkte antworten vollständig. Statt einer Seitennummer gibt es einen Cursor: Er markiert den Zeitpunkt, bis zu dem alles geliefert ist. Kommt während des Abholens ein Eintrag dazu, wird dadurch keiner übersprungen und keiner doppelt geliefert.

  1. Erste Anfrage ohne cursor.
  2. Ist hasMore gleich true, wiederholen Sie die Anfrage mit denselben Filtern und cursor gleich dem Wert aus nextCursor.
  3. Ist hasMore gleich false, haben Sie alles; nextCursor ist dann null.
  • Übernehmen Sie den Cursor unverändert und URL-kodiert. Sein Aufbau ist nicht Teil der Schnittstelle; ein selbst gebauter oder veränderter Wert wird mit 400 abgelehnt.
  • pageSize ist eine Richtgröße. Eine Seite endet immer an einer Zeitgrenze: Einträge mit demselben Beginn bleiben zusammen. Deshalb kann eine Seite kürzer ausfallen – im Beispiel oben ein Eintrag bei pageSize=2 – und im Sonderfall, dass sehr viele Einträge denselben Beginn haben, auch länger.
  • count ist die Zahl der Einträge auf dieser Seite, nicht die Gesamtzahl.

Ein ganzes Jahr abholen, mit curl und jq:

Schleife über alle Seiten
CURSOR=""
while : ; do
  RESP=$(curl -s -G -H "Authorization: Bearer $TOKEN" \
    --data-urlencode "from=2026-01-01" \
    --data-urlencode "to=2026-12-31" \
    --data-urlencode "cursor=$CURSOR" \
    "https://api.fue-stundenzettel.de/api/v1/timeentries")
  echo "$RESP" | jq -c '.items[]'
  [ "$(echo "$RESP" | jq -r '.hasMore')" = "true" ] || break
  CURSOR=$(echo "$RESP" | jq -r '.nextCursor')
done

Fehlercodes

CodeBedeutung
400Ein Parameter ist ungültig, zum Beispiel ein veränderter cursor, ein Datum, das sich nicht lesen lässt, oder ein Monat außerhalb von 1 bis 12. Bei den Exporten auch: Die Datei lässt sich nicht erzeugen, etwa weil Stammdaten fehlen – die Meldung nennt den Grund.
401Das Token fehlt, ist unbekannt, abgelaufen oder widerrufen.
403Eine schreibende Methode (POST, PUT, PATCH, DELETE) oder ein Pfad außerhalb von /api/v1. Ein Token erreicht ausschließlich die hier beschriebenen Endpunkte.
404Das Vorhaben gibt es nicht oder es gehört zu einem anderen Konto. Beide Fälle beantworten wir absichtlich gleich.
429Mehr als 600 Anfragen in 10 Minuten mit demselben Token. Warten Sie einige Minuten und setzen Sie dann fort.

Fehlerantworten mit Text tragen ihn im Feld error, als deutschen Satz. Lehnt die Token-Prüfung eine Anfrage ab (abgelaufen, widerrufen, schreibende Methode, fremder Pfad), steht zusätzlich "apiToken": true in der Antwort:

401 nach einem Widerruf
{
  "error": "Dieses API-Token wurde widerrufen. Bitte erzeugen Sie im Konto unter Einstellungen → API ein neues.",
  "apiToken": true
}
429
{
  "error": "Zu viele Anfragen an die Partner-API. Bitte in ein paar Minuten erneut - das Limit liegt bei 600 Anfragen je Token und zehn Minuten."
}

Zwei Ausnahmen: Fehlt das Token ganz oder ist es kein gültiger Schlüssel, kommt 401 ohne Inhalt. Lässt sich ein Parameter nicht lesen (etwa from=gestern), kommt 400 mit einer englischen Standardmeldung in den Feldern title und errors.

Grenzen und Sicherheit

  • Nur lesend. Ein Token kann nichts anlegen, ändern oder löschen. Schreibende Anfragen werden mit 403 abgelehnt.
  • Nur /api/v1. Die übrigen Adressen der Anwendung sind der angemeldeten Oberfläche vorbehalten.
  • Nur Ihr Konto. Ein Token sieht alle Vorhaben, Personen und Zeiten Ihres Kontos – und nichts außerhalb davon. Eine Einschränkung auf einzelne Vorhaben gibt es nicht.
  • 600 Anfragen je 10 Minuten, gezählt je Token. Zwei Token bremsen sich nicht gegenseitig. Ein Jahr Zeiteinträge sind bei 1.000 Einträgen je Seite meist wenige Dutzend Anfragen.
  • Ablauf nach einem Jahr. Danach antwortet die API mit 401. Erzeugen Sie rechtzeitig ein neues Token und tauschen Sie es im Werkzeug aus; verlängern lässt sich ein Token nicht.
  • Widerruf. Unter Einstellungen → API-Zugänge, jederzeit. Der Zugriff endet spätestens eine Minute später. Ein widerrufenes Token lässt sich nicht wieder aktivieren.
  • Behandeln Sie das Token wie ein Passwort. Es gehört in die geschützte Konfiguration des Werkzeugs – nicht in E-Mails, Tabellen, Quelltext-Ablagen oder Adresszeilen. Vergeben Sie je Empfänger ein eigenes Token; dann widerrufen Sie im Zweifel nur dieses eine.
  • Protokoll. Erzeugung und Widerruf eines Tokens stehen im Änderungsprotokoll Ihres Kontos.

Geben Sie Daten an Dritte weiter, etwa an eine Kanzlei, bleiben Sie dafür verantwortlich, dass die Weitergabe datenschutzrechtlich zulässig ist. Die API liefert Namen, E-Mail-Adressen, Arbeitszeiten, Krankheitstage und Stundensätze Ihrer Beschäftigten.

Versionierung und Änderungen

Die Fassung steht im Pfad: /api/v1. Innerhalb von Fassung 1 können Felder und Endpunkte dazukommen; bestehende Felder benennen wir nicht um und entfernen sie nicht. Bauen Sie Ihre Auswertung deshalb so, dass sie unbekannte Felder ignoriert. Eine Änderung, die das nicht einhält, bekäme eine neue Fassung unter /api/v2.

Eine maschinenlesbare Beschreibung derselben Endpunkte (OpenAPI) liegt unter api.fue-stundenzettel.de/swagger. Diese Seite gibt den Stand vom 20.09.2026 wieder.

Kontakt

Fragen zur Anbindung, ein fehlendes Feld oder ein Verhalten, das von dieser Seite abweicht: Schreiben Sie an mail@fue-stundenzettel.de oder nutzen Sie das Kontaktformular. Nennen Sie bitte den Endpunkt, den Zeitpunkt und den Statuscode – aber niemals das Token selbst.