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
- Melden Sie sich als Inhaber des Kontos an. Andere Rollen sehen diese Seite nicht.
- Öffnen Sie Einstellungen → API-Zugänge.
- 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“.
- 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.
| Eigenschaft | Wert |
|---|---|
| Gültigkeit | ein Jahr (365 Tage) ab Erzeugung |
| Rechte | nur lesend, nur Pfade unter /api/v1, nur die Daten Ihres Kontos |
| Widerruf | jederzeit in derselben Liste; wirkt spätestens nach einer Minute |
| Anzahl | höchstens 10 gültige Token je Konto |
| Anfragen | 600 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:
https://api.fue-stundenzettel.de/api/v1Das Token steht bei jeder Anfrage im Kopf Authorization:
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.
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.0oder32.000). Werten Sie sie als Zahl aus, nicht als Text.
Endpunkte
| Pfad | Inhalt |
|---|---|
/projects | alle Vorhaben des Kontos |
/projects/{projectId}/workpackages | Arbeitspakete eines Vorhabens |
/persons | Personen mit den Stammdaten eines Jahres |
/timeentries | Zeiteinträge, seitenweise |
/exports/pdf | Stundenzettel als PDF (Monat oder Jahr) |
/exports/excel | Jahresmappe als Excel-Datei |
/exports/csv | Zeiteinträ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.
curl -H "Authorization: Bearer $TOKEN" \
"https://api.fue-stundenzettel.de/api/v1/projects"{
"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.
curl -H "Authorization: Bearer $TOKEN" \
"https://api.fue-stundenzettel.de/api/v1/projects/e225b654-bba4-4260-97ae-3fdbf8d13e46/workpackages"{
"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.
| Parameter | Bedeutung |
|---|---|
year | Jahr der Stammdaten, optional. Ohne Angabe das laufende Jahr. |
curl -H "Authorization: Bearer $TOKEN" \
"https://api.fue-stundenzettel.de/api/v1/persons?year=2026"{
"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 profile | Bedeutung |
|---|---|
agreedWeeklyHours | vereinbarte Wochenarbeitszeit in Stunden |
contractualVacationDays | vertraglicher Urlaub in Tagen |
specialLeaveDays | Sonderurlaub in Tagen |
shortTimeWorkDays | Kurzarbeit in Tagen |
publicHolidays | Feiertage in Tagen |
sickDays | Summe der in der Anwendung erfassten Krankheitstage; ein halber Tag zählt 0,5 |
sickDaysOverride | Jahressumme der Krankheitstage aus dem Lohnsystem, falls hinterlegt. Ist sie gesetzt, rechnet der Stundenzettel mit ihr statt mit sickDays. |
rateKind | Art des Stundensatzes: employed (Beschäftigte), ownWork (Eigenleistung) oder external (Auftragsforschung) |
hourlyRate | hinterlegter Stundensatz in Euro. Bei ownWork rechnet die Anwendung mit dem gesetzlichen Satz, nicht mit diesem Feld. |
externalInvoiceAmount | Rechnungsbetrag der Auftragsforschung in Euro, nur bei external |
activityLabel | Kurzbezeichnung der FuE-Tätigkeit, wie sie im Kopf des Stundenzettels steht |
fueStartMonth, fueEndMonth | erster 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.
| Parameter | Bedeutung |
|---|---|
from, to | erster und letzter Tag, beide einschließlich (Ortszeit) |
projectId | nur dieses Vorhaben |
personId | nur diese Person (id aus /persons) |
pageSize | Richtgröße einer Seite, 1 bis 1.000. Vorgabe und Höchstwert: 1.000. Größere Werte werden auf 1.000 gesetzt. |
cursor | der Wert nextCursor der vorigen Antwort, siehe Paginierung |
curl -H "Authorization: Bearer $TOKEN" \
"https://api.fue-stundenzettel.de/api/v1/timeentries?from=2026-03-01&to=2026-03-31&pageSize=2"{
"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"
}| Feld | Bedeutung |
|---|---|
date, start, end | Tag, Beginn und Ende in deutscher Ortszeit – so, wie sie auf dem Stundenzettel stehen. end ist null, solange ein Eintrag noch läuft. |
startUtc, endUtc | dieselben Zeitpunkte in UTC |
minutes, hours | Dauer in Minuten und in Stunden, auf zwei Nachkommastellen gerundet |
isFue | true für FuE-Zeit, false für sonstige Zeit |
activity | Tätigkeitsbeschreibung des Eintrags |
origin | recorded: selbst erfasst. distributed: aus einer dokumentierten Summe auf Arbeitstage verteilt (Rekonstruktion); originNote nennt dann die Quelle. |
importBatchId | Kennung 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.
| Parameter | Bedeutung |
|---|---|
projectId | Vorhaben, Pflicht |
year | Jahr, Pflicht |
month | Monat 1 bis 12, optional |
includeFunding | true ergänzt auf der Jahresübersicht eine Zeile mit Bemessungsgrundlage und erwarteter Forschungszulage in Euro. Vorgabe false; beim Monatsblatt ohne Wirkung. |
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.
| Parameter | Bedeutung |
|---|---|
projectId | Vorhaben, Pflicht |
year | Jahr, Pflicht |
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.
| Parameter | Bedeutung |
|---|---|
projectId | nur dieses Vorhaben, optional. Ohne Angabe alle Vorhaben des Kontos. |
from, to | erster und letzter Tag, optional. Ohne Angabe der 1. Januar beziehungsweise der 31. Dezember des laufenden Jahres. |
curl -OJ -H "Authorization: Bearer $TOKEN" \
"https://api.fue-stundenzettel.de/api/v1/exports/csv?from=2026-01-01&to=2026-12-31"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.
- Erste Anfrage ohne
cursor. - Ist
hasMoregleichtrue, wiederholen Sie die Anfrage mit denselben Filtern undcursorgleich dem Wert ausnextCursor. - Ist
hasMoregleichfalse, haben Sie alles;nextCursorist dannnull.
- Ü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
400abgelehnt. pageSizeist 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 beipageSize=2– und im Sonderfall, dass sehr viele Einträge denselben Beginn haben, auch länger.countist die Zahl der Einträge auf dieser Seite, nicht die Gesamtzahl.
Ein ganzes Jahr abholen, mit curl und jq:
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')
doneFehlercodes
| Code | Bedeutung |
|---|---|
400 | Ein 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. |
401 | Das Token fehlt, ist unbekannt, abgelaufen oder widerrufen. |
403 | Eine schreibende Methode (POST, PUT, PATCH, DELETE) oder ein Pfad außerhalb von /api/v1. Ein Token erreicht ausschließlich die hier beschriebenen Endpunkte. |
404 | Das Vorhaben gibt es nicht oder es gehört zu einem anderen Konto. Beide Fälle beantworten wir absichtlich gleich. |
429 | Mehr 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:
{
"error": "Dieses API-Token wurde widerrufen. Bitte erzeugen Sie im Konto unter Einstellungen → API ein neues.",
"apiToken": true
}{
"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
403abgelehnt. - 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.