{"openapi":"3.0.1","info":{"title":"Timebutler API v2","description":"REST-API für die Timebutler HR-SaaS-Plattform. Wird von Integratoren für Abwesenheits-, Zeiterfassungs-, Vertretungs- und Benutzerprofilprozesse verwendet. Alle Endpunkte außer /oauth2/token erfordern ein Bearer-JWT-Zugriffstoken.","contact":{"name":"Timebutler Support","url":"https://www.timebutler.com","email":"support@timebutler.com"},"license":{"name":"Proprietary"},"version":"1.0","x-logo":{"url":"/images/logo/logo.png","altText":"Timebutler"}},"servers":[{"url":"https://app.timebutler.com/api/v2"},{"url":"/api/v2"}],"tags":[{"name":"Abwesenheitsanträge","description":"Urlaubs-, Krankheits- und sonstige Abwesenheitsanträge. Mitarbeiter stellen und verwalten ihre eigenen Anträge (`/my`, `POST`, `PUT /{id}`, `DELETE /{id}`); Manager und Administratoren sehen die offenen Anträge ihres Teams (`/team`, `/pending-count`) und können sie genehmigen oder ablehnen (`POST /{id}/approve`, `POST /{id}/reject`). Alle Endpunkte erfordern ein Inhaber-Token."},{"name":"Abwesenheitsarten","description":"Referenzliste der im Unternehmen konfigurierten Abwesenheitsarten (Urlaub, Krankheit usw.). Wird zur Befüllung von Auswahlfeldern beim Erstellen eines Abwesenheitsantrags verwendet."},{"name":"Stornierungsanträge","description":"Anträge von Mitarbeitern zur Stornierung (Löschung) einer bereits genehmigten Abwesenheit. Vorgesetzte und Administratoren sehen offene Stornierungsanträge ihres Teams und können diese genehmigen oder ablehnen."},{"name":"Kategorien","description":"Referenzliste der Zeiterfassungskategorien des Unternehmens. Wird beim Erstellen oder Beenden eines Zeiteintrags verwendet."},{"name":"Authentifizierung","description":"OAuth-2.0-Token-Endpunkt. Stellt JWT-Zugriffstoken und rotierende Refresh-Token aus. Dieser Endpunkt ist öffentlich — kein Inhaber-Token erforderlich."},{"name":"Projekte","description":"Referenzliste der Zeiterfassungsprojekte des Unternehmens. Favoriten werden zuerst aufgeführt. Wird beim Erstellen eines Zeiteintrags verwendet."},{"name":"Vertretungsmitarbeiter","description":"Referenzliste der Mitarbeiter, die beim Einreichen eines Abwesenheitsantrags als Vertretung ausgewählt werden können."},{"name":"Vertretungsanfragen","description":"Anfragen an den authentifizierten Benutzer, während der Abwesenheit eines Kollegen als Vertretung zu fungieren. Der Benutzer kann jede Anfrage annehmen oder ablehnen."},{"name":"Stempeluhr","description":"Echtzeit-Stempeluhr. Startet, pausiert, setzt fort, stoppt oder bricht die laufende Uhr für den authentifizierten Benutzer ab. Der Dienstgangmodus wird ebenfalls hier gesteuert."},{"name":"Zeiterfassungsanträge","description":"Ausstehende Änderungs- oder Löschanträge für Zeiteinträge, die eine Genehmigung durch einen Vorgesetzten oder Administrator erfordern. Vorgesetzte und Administratoren können diese Anträge auflisten, genehmigen und ablehnen."},{"name":"Zeiteinträge","description":"Verwaltung einzelner Zeiterfassungseinträge. Vorgesetzte und Administratoren können sehen, wie viele Einträge auf Genehmigung warten; jeder Benutzer kann seine eigenen Einträge löschen (abhängig von Monatsabschluss- und Genehmigungsworkflow-Regeln)."},{"name":"Zeiterfassung","description":"Tagesübersicht zur Arbeits- und Abwesenheitszeit des authentifizierten Benutzers sowie ein Endpunkt zum direkten Einreichen abgeschlossener Zeiteinträge (Start-/Endzeitstempel) ohne die Echtzeit-Stempeluhr."},{"name":"Benutzerprofil","description":"Grundlegende Profilinformationen des authentifizierten Benutzers: Name, E-Mail, Rolle, Abteilung und Avatar-URL."}],"paths":{"/absence-requests/{id}/approve":{"post":{"tags":["Abwesenheitsanträge"],"summary":"Offenen Abwesenheitsantrag genehmigen","description":"Vermerkt die Genehmigung des Aufrufers auf einem Antrag im Zustand `pending`. Hat der Mitarbeiter mehrere Manager, wechselt der Antrag erst dann in `approved`, wenn alle Manager genehmigt haben (oder ein Administrator genehmigt). Der Aufrufer muss Administrator oder direkter Vorgesetzter des Antragstellers sein.","operationId":"approve","parameters":[{"name":"id","in":"path","description":"Interne ID des zu genehmigenden Abwesenheitsantrags.","required":true,"schema":{"type":"integer","format":"int32"},"example":12345}],"responses":{"200":{"description":"Antrag nach Vermerk der Genehmigung.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AbsenceRequestDto"}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig.","content":{"application/json":{}}},"403":{"description":"Der Aufrufer ist weder Manager noch Administrator oder darf diesen konkreten Antrag nicht bearbeiten.","content":{"application/json":{}}},"404":{"description":"Für das Unternehmen des Aufrufers existiert kein Antrag mit der angegebenen `id`.","content":{"application/json":{}}},"409":{"description":"Der Antrag ist nicht im Zustand `pending` (bereits genehmigt, abgelehnt oder gelöscht).","content":{"application/json":{}}},"500":{"description":"Unerwarteter Serverfehler beim Aktualisieren des Antrags.","content":{"application/json":{}}}},"security":[{"bearerAuth":[]}]}},"/absence-requests":{"get":{"tags":["Abwesenheitsanträge"],"summary":"Eigene Abwesenheitsanträge des Aufrufers auflisten (Alias)","description":"Alias-Endpunkt, der dasselbe Ergebnis wie `GET /my` liefert. In neuen Integrationen `/my` bevorzugen — dieser Pfad existiert aus Gründen der Abwärtskompatibilität.","operationId":"getRequests","responses":{"200":{"description":"Liste der Anträge des Aufrufers. Leeres Array, wenn keine vorhanden sind.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AbsenceRequestDto"}}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig.","content":{"application/json":{}}},"403":{"description":"Das Konto des Aufrufers ist inaktiv.","content":{"application/json":{}}},"500":{"description":"Unerwarteter Serverfehler.","content":{"application/json":{}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Abwesenheitsanträge"],"summary":"Neuen Abwesenheitsantrag erstellen","description":"Reicht einen neuen Abwesenheits- oder Urlaubsantrag für den Aufrufer ein. Der resultierende Status hängt von der Konfiguration des Abwesenheitstyps ab: Typen, die eine Genehmigung durch den Manager erfordern, starten mit `pending`, andernfalls gehen sie direkt auf `approved`. Datumsangaben sind ISO-8601 (`YYYY-MM-DD`). Bei Halbtagsanträgen `halfDay` auf `morning` oder `afternoon` setzen und denselben Wert für `startDate` und `endDate` verwenden.","operationId":"create","requestBody":{"description":"Felder des neuen Abwesenheitsantrags.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAbsenceRequest"}}},"required":true},"responses":{"200":{"description":"Erstellter Antrag, im vom Server zugewiesenen Zustand.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AbsenceRequestDto"}}}},"400":{"description":"Fehlende, fehlerhafte oder zueinander inkonsistente Felder (ungültige `absenceTypeId`, nicht parsbares Datum, `endDate < startDate`, ungültiger `halfDay`-Wert oder unbekannte `substituteEmployeeId`).","content":{"application/json":{}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig.","content":{"application/json":{}}},"403":{"description":"Die Rolle des Aufrufers darf diesen Abwesenheitstyp nicht eintragen.","content":{"application/json":{}}},"500":{"description":"Unerwarteter Serverfehler beim Speichern des Antrags.","content":{"application/json":{}}}},"security":[{"bearerAuth":[]}]}},"/absence-requests/{id}":{"put":{"tags":["Abwesenheitsanträge"],"summary":"Bestehenden Abwesenheitsantrag aktualisieren","description":"Aktualisiert einen Antrag, der sich noch im Zustand `pending` oder `edit` befindet. Nur die im Request-Body übermittelten Felder werden geändert; `null`-Felder bleiben unverändert. Nicht-Administratoren können nur ihre eigenen Anträge aktualisieren. Bereits genehmigte oder abgelehnte Anträge können nicht mehr aktualisiert werden und liefern `409 Conflict`.","operationId":"update","parameters":[{"name":"id","in":"path","description":"Interne ID des zu aktualisierenden Abwesenheitsantrags. Entspricht dem `id`-Feld aus `GET /my` oder `GET /team`.","required":true,"schema":{"type":"integer","format":"int32"},"example":12345}],"requestBody":{"description":"Zu aktualisierende Felder. Ein Feld weglassen (oder `null` senden), um den bestehenden Wert zu behalten.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAbsenceRequest"}}},"required":true},"responses":{"200":{"description":"Aktualisierter Antrag, wie gespeichert.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AbsenceRequestDto"}}}},"400":{"description":"Fehlerhafter Body oder ungültige Feldkombination.","content":{"application/json":{}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig.","content":{"application/json":{}}},"403":{"description":"Der Aufrufer ist weder Eigentümer dieses Antrags noch Administrator, oder der Abwesenheitstyp ist für die Rolle des Aufrufers gesperrt.","content":{"application/json":{}}},"404":{"description":"Für das Unternehmen des Aufrufers existiert kein Antrag mit der angegebenen `id`.","content":{"application/json":{}}},"409":{"description":"Der Antrag befindet sich in einem Zustand, der keine Bearbeitung mehr zulässt (bereits genehmigt, abgelehnt oder gelöscht).","content":{"application/json":{}}},"500":{"description":"Unerwarteter Serverfehler beim Speichern der Aktualisierung.","content":{"application/json":{}}}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["Abwesenheitsanträge"],"summary":"Abwesenheitsantrag löschen oder Stornierungsanfrage senden","description":"Das Verhalten hängt vom aktuellen Zustand des Antrags und den Rechten des Aufrufers ab: Darf der Aufrufer den Antrag direkt löschen, wird er entfernt und das Feld `result` der Antwort ist `\"deleted\"`. Andernfalls wird ein Stornierungsantrag erzeugt, den die Manager des Mitarbeiters dann genehmigen müssen, und die Antwort `result` ist `\"cancellation_requested\"`. Die Unternehmenskonfiguration kann eine Begründung erforderlich machen, wenn nur eine Stornierung möglich ist.","operationId":"delete","parameters":[{"name":"id","in":"path","description":"Interne ID des zu löschenden oder zu stornierenden Abwesenheitsantrags.","required":true,"schema":{"type":"integer","format":"int32"},"example":12345}],"requestBody":{"description":"Optionaler Body mit einem freien `reason`-Text. Erforderlich für Stornierungsanfragen, wenn die Unternehmenskonfiguration eine Begründung verlangt.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteAbsenceRequest"}}}},"responses":{"200":{"description":"Ergebnis des Lösch-/Stornierungsversuchs. Das Feld `result` gibt an, welcher Pfad gewählt wurde.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteResultResponse"}}}},"400":{"description":"Fehlender Stornierungsgrund, obwohl das Unternehmen einen verlangt.","content":{"application/json":{}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig.","content":{"application/json":{}}},"403":{"description":"Der Aufrufer darf diesen Antrag weder löschen noch stornieren (falsches Unternehmen, oder die Berechtigungen für den aktuellen Zustand lassen keine Aktion zu).","content":{"application/json":{}}},"404":{"description":"Für das Unternehmen des Aufrufers existiert kein Antrag mit der angegebenen `id`.","content":{"application/json":{}}},"500":{"description":"Unerwarteter Serverfehler beim Löschen oder Stornieren.","content":{"application/json":{}}}},"security":[{"bearerAuth":[]}]}},"/absence-requests/my":{"get":{"tags":["Abwesenheitsanträge"],"summary":"Eigene Abwesenheitsanträge des Aufrufers auflisten","description":"Liefert jeden Abwesenheits- und Urlaubsantrag des Aufrufers, in jedem Zustand (pending, approved, rejected, done). Anträge, die der Aufrufer für seine unterstellten Mitarbeiter verwaltet, sind nicht enthalten — dafür siehe `/team`.","operationId":"getMyRequests","responses":{"200":{"description":"Liste der Anträge des Aufrufers. Leeres Array, wenn keine vorhanden sind.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AbsenceRequestDto"}}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig.","content":{"application/json":{}}},"403":{"description":"Das Konto des Aufrufers ist inaktiv.","content":{"application/json":{}}},"500":{"description":"Unerwarteter Serverfehler.","content":{"application/json":{}}}},"security":[{"bearerAuth":[]}]}},"/absence-requests/pending-count":{"get":{"tags":["Abwesenheitsanträge"],"summary":"Offene Antragszahlen für das Team des Aufrufers","description":"Liefert zwei Zähler für die direkt unterstellten Mitarbeiter des Aufrufers: offene Abwesenheitsanträge, die auf seine Genehmigung warten, und offene Stornierungsanträge. Wird verwendet, um Benachrichtigungs-Badges im Manager-Dashboard anzuzeigen. Liefert Nullen, wenn der Aufrufer keine direkt unterstellten Mitarbeiter hat.","operationId":"getPendingCount","responses":{"200":{"description":"Zähler erfolgreich zurückgegeben.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AbsencePendingCountResponse"}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig.","content":{"application/json":{}}},"403":{"description":"Der Aufrufer ist weder Manager noch Administrator.","content":{"application/json":{}}},"500":{"description":"Unerwarteter Serverfehler.","content":{"application/json":{}}}},"security":[{"bearerAuth":[]}]}},"/absence-requests/team":{"get":{"tags":["Abwesenheitsanträge"],"summary":"Abwesenheitsanträge der unterstellten Mitarbeiter auflisten","description":"Liefert jeden Abwesenheitsantrag von Mitarbeitern, die der Aufrufer verwaltet (Administratoren sehen alle Unternehmensnutzer), über alle Zustände hinweg. Jeder Eintrag enthält den Anzeigenamen und die ID des Mitarbeiters, damit das Ergebnis als Teamkalender oder Prüfliste dargestellt werden kann. Leeres Array, wenn der Aufrufer keine unterstellten Mitarbeiter hat.","operationId":"getTeamRequests","responses":{"200":{"description":"Liste der Teamanträge. Leeres Array, falls keine vorhanden sind.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AbsenceRequestDto"}}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig.","content":{"application/json":{}}},"403":{"description":"Der Aufrufer ist weder Manager noch Administrator.","content":{"application/json":{}}},"500":{"description":"Unerwarteter Serverfehler.","content":{"application/json":{}}}},"security":[{"bearerAuth":[]}]}},"/absence-requests/{id}/reject":{"post":{"tags":["Abwesenheitsanträge"],"summary":"Offenen Abwesenheitsantrag ablehnen","description":"Markiert einen offenen Abwesenheitsantrag als abgelehnt. Ein optionaler Ablehnungsgrund kann im Body übergeben werden — er wird gespeichert und dem antragstellenden Mitarbeiter angezeigt. Der Aufrufer muss Administrator oder direkter Vorgesetzter des Antragstellers sein.","operationId":"reject","parameters":[{"name":"id","in":"path","description":"Interne ID des abzulehnenden Abwesenheitsantrags.","required":true,"schema":{"type":"integer","format":"int32"},"example":12345}],"requestBody":{"description":"Optionaler Body mit einem `reason`-String, der dem Mitarbeiter angezeigt wird.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RejectRequest"}}}},"responses":{"200":{"description":"Antrag nach Vermerk der Ablehnung.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AbsenceRequestDto"}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig.","content":{"application/json":{}}},"403":{"description":"Der Aufrufer ist weder Manager noch Administrator oder darf diesen konkreten Antrag nicht bearbeiten.","content":{"application/json":{}}},"404":{"description":"Für das Unternehmen des Aufrufers existiert kein Antrag mit der angegebenen `id`.","content":{"application/json":{}}},"409":{"description":"Der Antrag ist nicht im Zustand `pending`.","content":{"application/json":{}}},"500":{"description":"Unerwarteter Serverfehler beim Aktualisieren des Antrags.","content":{"application/json":{}}}},"security":[{"bearerAuth":[]}]}},"/absence-types":{"get":{"tags":["Abwesenheitsarten"],"summary":"Abwesenheitsarten auflisten","description":"Gibt alle aktiven Abwesenheitsarten zurück, die der authentifizierte Benutzer einreichen darf.","operationId":"getAbsenceTypes","responses":{"200":{"description":"Liste der Abwesenheitsarten.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AbsenceTypeDto"}}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/cancellation-requests/{id}/approve":{"post":{"tags":["Stornierungsanträge"],"summary":"Stornierungsantrag genehmigen","description":"Genehmigt den ausstehenden Stornierungsantrag für die angegebene Abwesenheit. Die Abwesenheit wird gelöscht und Benachrichtigungs-E-Mails werden versendet. Erfordert die Rolle `MANAGER` oder `ADMIN`.","operationId":"approve_1","parameters":[{"name":"id","in":"path","description":"ID der Abwesenheit.","required":true,"schema":{"type":"integer","format":"int32"},"example":42}],"responses":{"200":{"description":"Stornierung genehmigt; gibt den Snapshot der gelöschten Abwesenheit zurück.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancellationRequestDto"}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"403":{"description":"Der Aufrufer ist kein Vorgesetzter oder Administrator, oder ist nicht der Vorgesetzte des Antragsstellers."},"404":{"description":"Abwesenheit nicht gefunden."},"409":{"description":"Kein ausstehender Stornierungsantrag für diese Abwesenheit."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/cancellation-requests":{"get":{"tags":["Stornierungsanträge"],"summary":"Offene Stornierungsanträge auflisten","description":"Gibt alle offenen Stornierungsanträge zurück, die für den authentifizierten Vorgesetzten oder Administrator sichtbar sind. Erfordert die Rolle `MANAGER` oder `ADMIN`.","operationId":"getList","responses":{"200":{"description":"Liste offener Stornierungsanträge.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CancellationRequestDto"}}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"403":{"description":"Der Aufrufer ist kein Vorgesetzter oder Administrator."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/cancellation-requests/pending-count":{"get":{"tags":["Stornierungsanträge"],"summary":"Anzahl offener Stornierungsanträge abrufen","description":"Gibt die Anzahl der Stornierungsanträge zurück, die auf eine Entscheidung eines Vorgesetzten oder Administrators warten. Erfordert die Rolle `MANAGER` oder `ADMIN`.","operationId":"getPendingCount_1","responses":{"200":{"description":"Anzahl offener Anträge.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancellationPendingCountResponse"}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"403":{"description":"Der Aufrufer ist kein Vorgesetzter oder Administrator."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/cancellation-requests/{id}/reject":{"post":{"tags":["Stornierungsanträge"],"summary":"Stornierungsantrag ablehnen","description":"Lehnt den ausstehenden Stornierungsantrag ab. Die Abwesenheit bleibt bestehen; der Mitarbeiter wird per E-Mail benachrichtigt. Erfordert die Rolle `MANAGER` oder `ADMIN`.","operationId":"reject_1","parameters":[{"name":"id","in":"path","description":"ID der Abwesenheit.","required":true,"schema":{"type":"integer","format":"int32"},"example":42}],"responses":{"200":{"description":"Stornierung abgelehnt; gibt die aktualisierte Abwesenheit zurück.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancellationRequestDto"}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"403":{"description":"Der Aufrufer ist kein Vorgesetzter oder Administrator, oder ist nicht der Vorgesetzte des Antragsstellers."},"404":{"description":"Abwesenheit nicht gefunden."},"409":{"description":"Kein ausstehender Stornierungsantrag für diese Abwesenheit."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/categories":{"get":{"tags":["Kategorien"],"summary":"Kategorien auflisten","description":"Gibt alle aktiven Kategorien sowie die Standardkategorie des Benutzers und die Information zurück, ob eine Kategorie beim Speichern eines Zeiteintrags erforderlich ist.","operationId":"getCategories","responses":{"200":{"description":"Kategorieliste mit Standardwert und Pflichtfeld-Kennzeichnung.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoriesResponse"}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/oauth2/token":{"post":{"tags":["Authentifizierung"],"summary":"Token ausstellen oder erneuern","description":"Unterstützt fünf Grant-Typen:\n\n**`grant_type=password`** — Authentifizierung mit `username` und `password`. Bei Erfolg wird ein Zugriffstoken und ein Refresh-Token zurückgegeben. Falls das Konto 2FA aktiviert hat, wird stattdessen eine MFA-Anforderung zurückgegeben (`mfa_required=true`, `mfa_token`, `mfa_expires_in`) — den Ablauf mit `grant_type=mfa_totp` abschließen.\n\n**`grant_type=refresh_token`** — Tauscht ein gültiges `refresh_token` gegen ein neues Zugriffstoken aus und rotiert das Refresh-Token (jedes Refresh-Token kann nur einmal verwendet werden).\n\n**`grant_type=mfa_totp`** — Zweiter Schritt des 2FA-Ablaufs. Den `mfa_token` aus der Passwort-Grant-Anforderung zusammen mit dem aktuellen `totp_code` aus der Authenticator-App des Benutzers übermitteln. Bei Erfolg werden Zugriffstoken und Refresh-Token zurückgegeben.\n\n**`grant_type=sso_id_token`** — Single Sign-On mit einem ID-Token, das die mobile App bereits vom SSO-Anbieter erhalten hat. `provider=google` und das vom Anbieter ausgestellte `id_token` (ein JWT) übermitteln. Timebutler prüft Signatur, Aussteller und Zielgruppe des JWT und gibt für das verknüpfte Timebutler-Konto Zugriffs- und Refresh-Token zurück.\n\n**`grant_type=sso_code`** — Single Sign-On mit einem Authorization Code, den die mobile App vom Consent-Screen des SSO-Anbieters erhalten hat. `provider=microsoft` oder `provider=slack`, den `code` vom Anbieter sowie die in der Authorize-Anfrage verwendete `redirect_uri` übermitteln. Bei Microsoft-Public-Client-App-Registrierungen zusätzlich den PKCE-`code_verifier` senden, dessen SHA-256-Hash als `code_challenge` im Authorize-Schritt verwendet wurde. Timebutler tauscht den Code beim Anbieter ein, identifiziert den Benutzer und gibt das Token-Paar zurück.\n\nAlle Grant-Typen erfordern außerdem `client_id`. Verwende `client_id=public` zur Authentifizierung ohne Secret, oder übergib `client_id` und `client_secret` für einen registrierten vertraulichen Client.","operationId":"token","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"grant_type":{"type":"string","description":"OAuth-2.0-Grant-Typ. Unterstützte Werte: `password`, `refresh_token`, `mfa_totp`, `sso_id_token`, `sso_code`.","example":"password"},"username":{"type":"string","description":"E-Mail-Adresse des Benutzers. Erforderlich für `grant_type=password`.","example":"user@example.com"},"password":{"type":"string","description":"Passwort des Benutzers. Erforderlich für `grant_type=password`.","example":"s3cr3t"},"refresh_token":{"type":"string","description":"Refresh-Token aus einer vorherigen Token-Antwort. Erforderlich für `grant_type=refresh_token`.","example":"rt-a1b2c3d4e5f6..."},"client_id":{"type":"string","description":"Client-Bezeichner. Verwende `public` für nicht authentifizierte öffentliche Clients.","example":"public"},"client_secret":{"type":"string","description":"Client-Secret. Erforderlich für vertrauliche Clients; weglassen bei `client_id=public`.","example":"my-secret"},"mfa_token":{"type":"string","description":"MFA-Challenge-Token, das bei einem `password`-Grant zurückgegeben wird, wenn 2FA erforderlich ist. Erforderlich für `grant_type=mfa_totp`."},"totp_code":{"type":"string","description":"Aktueller TOTP-Code aus der Authenticator-App des Benutzers. Erforderlich für `grant_type=mfa_totp`."},"provider":{"type":"string","description":"SSO-Anbieter-Bezeichner. Erforderlich für `grant_type=sso_id_token` (`google`) und `grant_type=sso_code` (`microsoft`, `slack`).","example":"google"},"id_token":{"type":"string","description":"Vom Anbieter ausgestelltes SSO-ID-Token (z. B. ein Google-ID-Token-JWT). Erforderlich für `grant_type=sso_id_token`."},"code":{"type":"string","description":"Vom Authorize-Endpunkt des SSO-Anbieters zurückgegebener Authorization Code. Erforderlich für `grant_type=sso_code`."},"redirect_uri":{"type":"string","description":"Redirect-URI, die beim Abruf des Authorization Codes verwendet wurde. Muss mit dem beim SSO-Anbieter hinterlegten Wert übereinstimmen. Erforderlich für `grant_type=sso_code`.","example":"https://app.example.com/sso/callback"},"code_verifier":{"type":"string","description":"PKCE-Code-Verifier — der Klartextwert, dessen SHA-256-Hash als `code_challenge` in der Authorization-Anfrage gesendet wurde. Erforderlich für `grant_type=sso_code`, wenn die App-Registrierung des SSO-Anbieters ein öffentlicher Client ist."}}},"examples":{"password-grant":{"summary":"Passwort-Grant","value":"grant_type=password&username=user%40example.com&password=s3cr3t&client_id=public"},"refresh-token-grant":{"summary":"Refresh-Token-Grant","value":"grant_type=refresh_token&refresh_token=550e8400-e29b-41d4-a716-446655440000&client_id=public"},"mfa-totp-grant":{"summary":"MFA-TOTP-Grant (2FA zweiter Schritt)","value":"grant_type=mfa_totp&mfa_token=mfa-a1b2c3d4e5f6&totp_code=123456&client_id=public"}}}}},"responses":{"200":{"description":"Token erfolgreich ausgestellt, oder MFA-Anforderung zurückgegeben (Passwort-Grant mit aktivierter 2FA). Die Antwort ist eine `OauthTokenResponse` (Zugriffstoken + Refresh-Token) für `refresh_token`- und `mfa_totp`-Grants, oder eine `MfaChallengeResponse` (MFA-Anforderung) für einen `password`-Grant, wenn das Konto 2FA aktiviert hat.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/OauthTokenResponse"},{"$ref":"#/components/schemas/MfaChallengeResponse"}]}}}},"400":{"description":"Ungültige Anfrage, nicht unterstützter Grant-Typ oder ungültige Anmeldedaten.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OauthErrorResponse"}}}},"403":{"description":"Zugriff aufgrund einer IP-Adressbeschränkung verweigert.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OauthErrorResponse"}}}},"429":{"description":"Zu viele Anfragen von dieser IP-Adresse. Versuche es in 1 Minute erneut.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OauthErrorResponse"}}}},"500":{"description":"Unerwarteter Serverfehler.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OauthErrorResponse"}}}}}}},"/projects":{"get":{"tags":["Projekte"],"summary":"Projekte auflisten","description":"Gibt alle aktiven Projekte zurück. Die Favoriten des Benutzers erscheinen zuerst, gefolgt von allen weiteren aktiven Projekten. Enthält außerdem das Standardprojekt des Benutzers und die Information, ob ein Projekt Pflichtfeld ist.","operationId":"getProjects","responses":{"200":{"description":"Projektliste mit Standardwert und Pflichtfeld-Kennzeichnung.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectsResponse"}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/substitute-employees":{"get":{"tags":["Vertretungsmitarbeiter"],"summary":"Vertretungsmitarbeiter auflisten","description":"Gibt alle aktiven Mitarbeiter des Unternehmens zurück, die als Vertretung für den authentifizierten Benutzer fungieren können. Der authentifizierte Benutzer selbst ist nicht in der Liste enthalten.","operationId":"getSubstituteEmployees","responses":{"200":{"description":"Liste der Vertretungsmitarbeiter.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubstituteEmployeeDto"}}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/substitute-requests/{id}/accept":{"post":{"tags":["Vertretungsanfragen"],"summary":"Vertretungsanfrage annehmen","description":"Nimmt die Vertretungsanfrage für die angegebene Abwesenheit an. Nur die vorgesehene Vertretung darf diesen Endpunkt aufrufen.","operationId":"accept","parameters":[{"name":"id","in":"path","description":"ID der Abwesenheit.","required":true,"schema":{"type":"integer","format":"int32"},"example":42}],"responses":{"200":{"description":"Anfrage angenommen; gibt die aktualisierte Abwesenheit zurück.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubstituteRequestDto"}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"403":{"description":"Der Aufrufer ist nicht die vorgesehene Vertretung."},"404":{"description":"Abwesenheit nicht gefunden."},"409":{"description":"Die Anfrage kann im aktuellen Status nicht angenommen werden (bereits angenommen, abgelehnt oder keine Annahme erforderlich)."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/substitute-requests/pending-count":{"get":{"tags":["Vertretungsanfragen"],"summary":"Anzahl offener Vertretungsanfragen abrufen","description":"Gibt die Anzahl der Vertretungsanfragen zurück, die noch auf die Antwort des authentifizierten Benutzers warten.","operationId":"getPendingCount_2","responses":{"200":{"description":"Anzahl offener Anfragen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubstitutePendingCountResponse"}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/substitute-requests":{"get":{"tags":["Vertretungsanfragen"],"summary":"Vertretungsanfragen auflisten","description":"Gibt alle Vertretungsanfragen zurück, die an den authentifizierten Benutzer gerichtet sind, unabhängig von deren Status (ausstehend, angenommen, abgelehnt).","operationId":"getSubstituteRequests","responses":{"200":{"description":"Liste der Vertretungsanfragen.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SubstituteRequestDto"}}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/substitute-requests/{id}/reject":{"post":{"tags":["Vertretungsanfragen"],"summary":"Vertretungsanfrage ablehnen","description":"Lehnt die Vertretungsanfrage für die angegebene Abwesenheit ab. Nur die vorgesehene Vertretung darf diesen Endpunkt aufrufen.","operationId":"reject_2","parameters":[{"name":"id","in":"path","description":"ID der Abwesenheit.","required":true,"schema":{"type":"integer","format":"int32"},"example":42}],"responses":{"200":{"description":"Anfrage abgelehnt; gibt die aktualisierte Abwesenheit zurück.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubstituteRequestDto"}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"403":{"description":"Der Aufrufer ist nicht die vorgesehene Vertretung."},"404":{"description":"Abwesenheit nicht gefunden."},"409":{"description":"Die Anfrage kann im aktuellen Status nicht abgelehnt werden."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/time-clock/business-trip-end":{"post":{"tags":["Stempeluhr"],"summary":"Dienstgang beenden","description":"Markiert das Ende eines aktiven Dienstgangs auf der aktuellen Stempeluhr. Gibt 409 zurück, wenn kein Dienstgang aktiv ist.","operationId":"businessTripEnd","responses":{"200":{"description":"Dienstgang beendet; gibt den aktuellen Status zurück.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeClockStatusDto"}}}},"400":{"description":"Dienstgänge nicht aktiviert oder Zeiterfassung nicht aktiv."},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"409":{"description":"Kein Dienstgang ist derzeit aktiv."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/time-clock/business-trip-start":{"post":{"tags":["Stempeluhr"],"summary":"Dienstgang starten","description":"Markiert den Beginn eines Dienstgangs auf der aktuell laufenden oder pausierten Stempeluhr. Gibt 400 zurück, wenn Dienstgänge für dieses Unternehmen nicht aktiviert sind; gibt 409 zurück, wenn die Stempeluhr nicht aktiv ist oder ein Dienstgang bereits läuft.","operationId":"businessTripStart","responses":{"200":{"description":"Dienstgang gestartet; gibt den aktuellen Status zurück.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeClockStatusDto"}}}},"400":{"description":"Dienstgänge nicht aktiviert oder Zeiterfassung nicht aktiv."},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"409":{"description":"Stempeluhr läuft nicht/ist nicht pausiert oder ein Dienstgang ist bereits aktiv."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/time-clock/cancel":{"post":{"tags":["Stempeluhr"],"summary":"Stempeluhr abbrechen","description":"Verwirft die aktuelle Stempeluhr-Sitzung ohne einen Zeiteintrag zu speichern. Gibt 409 zurück, wenn die Stempeluhr bereits `idle` ist.","operationId":"cancel","responses":{"200":{"description":"Stempeluhr abgebrochen; gibt den Status `idle` zurück.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeClockStatusDto"}}}},"400":{"description":"Zeiterfassung für dieses Unternehmen nicht aktiv."},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"409":{"description":"Die Stempeluhr ist bereits `idle`."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/time-clock/status":{"get":{"tags":["Stempeluhr"],"summary":"Stempeluhr-Status abrufen","description":"Gibt den aktuellen Status der Stempeluhr zurück: `idle`, `running`, `paused` oder `waiting`. Enthält außerdem die verstrichene Zeit, die Pausenzeit und den Dienstgang-Status.","operationId":"getStatus","responses":{"200":{"description":"Aktueller Stempeluhr-Status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeClockStatusDto"}}}},"400":{"description":"Zeiterfassung für dieses Unternehmen nicht aktiv."},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/time-clock/pause":{"post":{"tags":["Stempeluhr"],"summary":"Stempeluhr pausieren","description":"Pausiert eine laufende Stempeluhr. Gibt 409 zurück, wenn die Stempeluhr nicht im Status `running` ist.","operationId":"pause","responses":{"200":{"description":"Stempeluhr pausiert; gibt den aktuellen Status zurück.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeClockStatusDto"}}}},"400":{"description":"Zeiterfassung für dieses Unternehmen nicht aktiv."},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"409":{"description":"Die Stempeluhr läuft nicht."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/time-clock/resume":{"post":{"tags":["Stempeluhr"],"summary":"Stempeluhr fortsetzen","description":"Setzt eine pausierte Stempeluhr fort. Gibt 409 zurück, wenn die Stempeluhr nicht im Status `paused` ist.","operationId":"resume","responses":{"200":{"description":"Stempeluhr fortgesetzt; gibt den aktuellen Status zurück.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeClockStatusDto"}}}},"400":{"description":"Zeiterfassung für dieses Unternehmen nicht aktiv."},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"409":{"description":"Die Stempeluhr ist nicht pausiert."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/time-clock/start":{"post":{"tags":["Stempeluhr"],"summary":"Stempeluhr starten","description":"Startet die Stempeluhr für den authentifizierten Benutzer. Gibt 409 zurück, wenn die Stempeluhr bereits läuft. Die Uhr kann den Status `waiting` annehmen, wenn ein Mindest-Startintervall konfiguriert ist.","operationId":"start","responses":{"200":{"description":"Stempeluhr gestartet; gibt den aktuellen Status zurück.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeClockStatusDto"}}}},"400":{"description":"Zeiterfassung für dieses Unternehmen nicht aktiv."},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"403":{"description":"Der Benutzer ist nicht berechtigt, Zeiteinträge zu erstellen."},"409":{"description":"Die Stempeluhr läuft bereits oder die Startbedingungen sind nicht erfüllt."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/time-clock/stop":{"post":{"tags":["Stempeluhr"],"summary":"Stempeluhr stoppen und speichern","description":"Stoppt die laufende oder pausierte Stempeluhr und speichert den resultierenden Zeiteintrag. Optional können eine Projekt-ID und Anmerkungen angegeben werden. Befindet sich die Uhr im Status `waiting`, wird sie verworfen und 204 zurückgegeben. Gibt 409 zurück, wenn die Gesamtdauer zu kurz ist (< 1 Minute) oder ein doppelter Eintrag erkannt wird.\n\nVerletzt die Sitzung die Pausenregelung des Unternehmens und wurde keine `breakRegulation`-Entscheidung gesendet, wird der Eintrag **nicht** gespeichert: Die Antwort enthält `outcome: confirmationRequired` und ein `breakRegulation`-Objekt mit der erforderlichen Änderung. Den Stopp mit `breakRegulation: apply` (Korrektur übernehmen) oder `breakRegulation: ignore` (unverändert speichern) erneut senden. Clients sollten `ignore` nur bei nicht verpflichtender Regelung anbieten (`breakRegulation.obligatory` in der Antwort); der Server erzwingt dies derzeit nicht. Ist Auto-Übernahme konfiguriert, wird die Korrektur bereits beim ersten Stopp ohne Entscheidung angewendet.","operationId":"stop","requestBody":{"description":"Optionale Projekt-ID, Kategorie-ID und Anmerkungen für den gespeicherten Eintrag.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeClockStopRequest"}}}},"responses":{"200":{"description":"Entweder die gespeicherten Eintragsdetails (`outcome: saved`) oder eine Bestätigungsanforderung zur Pausenregelung (`outcome: confirmationRequired`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeClockStopResponse"}}}},"204":{"description":"Stempeluhr war im Status `waiting` und wurde verworfen, ohne zu speichern."},"400":{"description":"Zeiterfassung für dieses Unternehmen nicht aktiv."},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"409":{"description":"Stempeluhr ist nicht aktiv, Dauer zu kurz oder doppelter Eintrag erkannt."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/time-entry-requests/{id}/approve":{"post":{"tags":["Zeiterfassungsanträge"],"summary":"Zeiterfassungsantrag genehmigen","description":"Genehmigt den ausstehenden Änderungs- oder Löschantrag für den angegebenen Zeiteintrag. Bei Löschanträgen wird der Eintrag entfernt; bei Änderungsanträgen wird der Eintrag auf die vorgeschlagenen Werte aktualisiert. Erfordert die Rolle `MANAGER` oder `ADMIN`.","operationId":"approve_2","parameters":[{"name":"id","in":"path","description":"ID des Zeiteintrags.","required":true,"schema":{"type":"integer","format":"int32"},"example":123}],"responses":{"200":{"description":"Antrag genehmigt; gibt den aktualisierten Zeiteintrag zurück.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntryRequestDto"}}}},"400":{"description":"Zeiterfassung für dieses Unternehmen nicht aktiv."},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"403":{"description":"Der Aufrufer ist kein Vorgesetzter oder Administrator, oder nur Administratoren dürfen in diesem Unternehmen genehmigen."},"404":{"description":"Zeiteintrag nicht gefunden."},"409":{"description":"Kein ausstehender Antrag oder der vorgeschlagene Änderung hat einen Terminkonflikt."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/time-entry-requests":{"get":{"tags":["Zeiterfassungsanträge"],"summary":"Ausstehende Zeiterfassungsanträge auflisten","description":"Gibt alle ausstehenden Zeiterfassungsanträge im Team des Vorgesetzten zurück, die auf Genehmigung warten. Erfordert die Rolle `MANAGER` oder `ADMIN`. Gibt eine leere Liste zurück, wenn die Zeiterfassung nicht aktiv ist oder der Aufrufer ein Vorgesetzter in einem Unternehmen mit ausschließlicher Admin-Genehmigung ist.","operationId":"getList_1","responses":{"200":{"description":"Liste ausstehender Zeiterfassungsanträge.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TimeEntryRequestDto"}}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"403":{"description":"Der Aufrufer ist kein Vorgesetzter oder Administrator."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/time-entry-requests/{id}/reject":{"post":{"tags":["Zeiterfassungsanträge"],"summary":"Zeiterfassungsantrag ablehnen","description":"Lehnt den ausstehenden Änderungs- oder Löschantrag für den angegebenen Zeiteintrag ab. Ein optionaler Ablehnungsgrund kann im Anfragetext angegeben werden. Der Mitarbeiter wird per E-Mail benachrichtigt. Erfordert die Rolle `MANAGER` oder `ADMIN`.","operationId":"reject_3","parameters":[{"name":"id","in":"path","description":"ID des Zeiteintrags.","required":true,"schema":{"type":"integer","format":"int32"},"example":123}],"requestBody":{"description":"Optionaler Ablehnungsgrund.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RejectRequest"}}}},"responses":{"200":{"description":"Antrag abgelehnt; gibt den Zeiteintrag im aktuellen Status zurück.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntryRequestDto"}}}},"400":{"description":"Zeiterfassung für dieses Unternehmen nicht aktiv."},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"403":{"description":"Der Aufrufer ist kein Vorgesetzter oder Administrator, oder nur Administratoren dürfen in diesem Unternehmen ablehnen."},"404":{"description":"Zeiteintrag nicht gefunden."},"409":{"description":"Kein ausstehender Antrag für diesen Eintrag."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/time-entries/{id}":{"delete":{"tags":["Zeiteinträge"],"summary":"Zeiteintrag löschen","description":"Löscht den angegebenen Zeiteintrag. Wenn die Zeiterfassungseinstellungen des Unternehmens eine Genehmigung durch den Vorgesetzten für Löschungen erfordern, wird stattdessen ein Löschantrag gestellt. Das Antwortfeld `result` ist entweder `deleted` oder `deletion_requested`.","operationId":"delete_1","parameters":[{"name":"id","in":"path","description":"ID des Zeiteintrags.","required":true,"schema":{"type":"integer","format":"int32"},"example":123}],"responses":{"200":{"description":"Eintrag gelöscht oder Löschantrag gestellt.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteResultResponse"}}}},"400":{"description":"Ungültige ID oder Zeiterfassung ist für dieses Unternehmen nicht aktiv."},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"403":{"description":"Der Aufrufer darf diesen Eintrag nicht löschen."},"404":{"description":"Zeiteintrag nicht gefunden."},"409":{"description":"Der Eintrag ist durch den Monatsabschluss gesperrt."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/time-entries/pending-count":{"get":{"tags":["Zeiteinträge"],"summary":"Anzahl ausstehender Zeiteinträge abrufen","description":"Gibt die Anzahl der Zeiteinträge im Team des Vorgesetzten zurück, die auf Genehmigung warten. Erfordert die Rolle `MANAGER` oder `ADMIN`.","operationId":"getPendingCount_3","responses":{"200":{"description":"Anzahl ausstehender Einträge.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntryPendingCountResponse"}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"403":{"description":"Der Aufrufer ist kein Vorgesetzter oder Administrator."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/time-tracking/clock-entry":{"post":{"tags":["Zeiterfassung"],"summary":"Abgeschlossenen Zeiteintrag einreichen","description":"Erstellt einen Zeiteintrag aus expliziten Start- und End-Unix-Zeitstempeln (Millisekunden). Start und Ende müssen am selben Kalendertag liegen. Wenn das Unternehmen eine Genehmigung durch den Vorgesetzten für neue Einträge erfordert, wird der Eintrag mit dem Status `pending` erstellt; andernfalls ist er sofort `done`.","operationId":"createClockEntry","requestBody":{"description":"Zeiteintrag mit Start-/Endzeitstempeln sowie optionalem Projekt und Anmerkungen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClockEntryRequest"}}},"required":true},"responses":{"200":{"description":"Eintrag erstellt; gibt die Eintrags-ID sowie die geleisteten und Pausenminuten zurück.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClockEntryResponse"}}}},"400":{"description":"Fehlender oder ungültiger Anfragetext, ungültige Zeitstempel oder Zeiterfassung nicht aktiv."},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"403":{"description":"Der Benutzer darf keine Zeiteinträge erstellen."},"422":{"description":"Die Anmerkungen überschreiten die maximale Länge."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/time-tracking/time-off-summary":{"get":{"tags":["Zeiterfassung"],"summary":"Abwesenheitszusammenfassung abrufen","description":"Gibt die verbleibenden und die Gesamtanzahl der Urlaubstage des authentifizierten Benutzers für das laufende Jahr sowie die Anzahl der genommenen Krankheitstage zurück.","operationId":"getTimeOffSummary","responses":{"200":{"description":"Abwesenheitszusammenfassung.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeOffSummaryResponse"}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/time-tracking/work-summary":{"get":{"tags":["Zeiterfassung"],"summary":"Heutige Arbeitszusammenfassung abrufen","description":"Gibt die Sollarbeitszeit, die geleisteten Minuten, die verbleibenden Minuten, den Fertigstellungsgrad und die Pausenminuten des authentifizierten Benutzers für den heutigen Tag zurück.","operationId":"getWorkSummary","responses":{"200":{"description":"Heutige Arbeitszusammenfassung.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkSummaryResponse"}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}},"/user/profile":{"get":{"tags":["Benutzerprofil"],"summary":"Profil abrufen","description":"Gibt Anzeigename, E-Mail-Adresse, Rolle (`USER`, `MANAGER`, `ADMIN`), Abteilung, Niederlassung und Avatar-URL des authentifizierten Benutzers zurück.","operationId":"getProfile","responses":{"200":{"description":"Profildaten.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserProfileResponse"}}}},"401":{"description":"Das Inhaber-Token fehlt oder ist ungültig."},"500":{"description":"Unerwarteter Serverfehler."}},"security":[{"bearerAuth":[]}]}}},"components":{"schemas":{"AbsenceRequestDto":{"type":"object","properties":{"id":{"type":"string","description":"Interne ID des Abwesenheitsantrags.","example":"12345"},"type":{"type":"string","description":"Abwesenheitstyp-Code (3 Zeichen), z. B. `\"URL\"` für Urlaub.","example":"URL"},"startDate":{"type":"string","description":"Erster Tag der Abwesenheit im ISO-8601-Format (`YYYY-MM-DD`).","example":"2025-08-01"},"endDate":{"type":"string","description":"Letzter Tag der Abwesenheit im ISO-8601-Format (`YYYY-MM-DD`).","example":"2025-08-05"},"days":{"type":"number","description":"Anzahl der Werktage, die die Abwesenheit umfasst, berechnet gemäß dem Arbeitszeitplan des Mitarbeiters.","format":"double","example":4.0},"status":{"type":"string","description":"Aktueller Status des Antrags. Einer von `pending` (wartet auf Genehmigung), `approved` oder `rejected`.","example":"pending","enum":["pending","approved","rejected"]},"halfDay":{"type":"string","description":"Halbtags-Indikator. `\"morning\"` oder `\"afternoon\"` für Halbtagsanträge; `null` für ganztägige Abwesenheiten.","nullable":true,"example":"morning","enum":["morning","afternoon"]},"substitutePending":{"type":"boolean","description":"Ob die Bestätigung des Vertreters noch aussteht. `null`, wenn kein Vertreter gesetzt wurde.","nullable":true,"example":false},"medicalCertificateProvided":{"type":"boolean","description":"Ob ein Attest für diese Abwesenheit hochgeladen wurde. Nur vorhanden bei Abwesenheitstypen, die ein Attest erfordern; sonst `null`.","nullable":true,"example":false},"employeeName":{"type":"string","description":"Anzeigename des Mitarbeiters, der den Antrag gestellt hat. Nur in Antworten von `GET /team` vorhanden; `null` in `GET /my`-Antworten.","nullable":true,"example":"Maria Müller"},"employeeId":{"type":"string","description":"Interne ID des Mitarbeiters, der den Antrag gestellt hat. Nur in Antworten von `GET /team` vorhanden; `null` in `GET /my`-Antworten.","nullable":true,"example":"42"}}},"CreateAbsenceRequest":{"required":["absenceTypeId","endDate","startDate"],"type":"object","properties":{"absenceTypeId":{"type":"string","description":"Abwesenheitstyp-Code (3 Zeichen), z. B. `\"URL\"` für Urlaub. Muss einem aktiven Typ aus `GET /absence-types` entsprechen.","example":"URL"},"startDate":{"type":"string","description":"Erster Tag der Abwesenheit im ISO-8601-Format (`YYYY-MM-DD`).","example":"2025-08-01"},"endDate":{"type":"string","description":"Letzter Tag der Abwesenheit im ISO-8601-Format (`YYYY-MM-DD`). Muss gleich oder nach `startDate` liegen. Bei Halbtagsanträgen muss es gleich `startDate` sein.","example":"2025-08-05"},"halfDay":{"type":"string","description":"Auf `\"morning\"` oder `\"afternoon\"` setzen, um einen einzelnen Halbtag zu beantragen. Wenn gesetzt, müssen `startDate` und `endDate` denselben Tag haben. Weglassen oder `null` senden für eine ganztägige Abwesenheit.","example":"morning","enum":["morning","afternoon"]},"comments":{"type":"string","description":"Optionaler Freitextkommentar, der den Managern des Mitarbeiters angezeigt wird.","example":"Holiday trip"},"substituteEmployeeId":{"type":"string","description":"Mitarbeiter-ID des designierten Vertreters. Muss zum selben Unternehmen gehören. Weglassen oder `null` senden, wenn kein Vertreter benötigt wird.","example":"42"}}},"DeleteAbsenceRequest":{"type":"object","properties":{"reason":{"type":"string","description":"Optionaler Freitextgrund für die Stornierung. Erforderlich, wenn die Unternehmenskonfiguration einen Grund für Stornierungsanträge vorschreibt.","example":"Plans changed"}}},"DeleteResultResponse":{"type":"object","properties":{"id":{"type":"string","description":"Interne ID des betroffenen Eintrags.","example":"12345"},"result":{"type":"string","description":"Ergebnis der Operation. `\"deleted\"`, wenn der Eintrag direkt gelöscht wurde; `\"cancellation_requested\"`, wenn stattdessen ein Stornierungsantrag erstellt wurde; `\"deletion_requested\"`, wenn ein Löschantrag erstellt wurde (für Zeiteinträge, die genehmigt werden müssen).","example":"deleted","enum":["deleted","cancellation_requested","deletion_requested"]}}},"AbsencePendingCountResponse":{"type":"object","properties":{"numberOfAbsenceRequests":{"type":"integer","description":"Anzahl der Abwesenheitsanträge im Team des Aufrufers, die auf die Genehmigung des Aufrufers warten.","format":"int32","example":3},"numberOfAbsenceRequestCancellations":{"type":"integer","description":"Anzahl der Stornierungsanträge im Team des Aufrufers, die auf eine Genehmigung warten.","format":"int32","example":1}}},"RejectRequest":{"type":"object","properties":{"reason":{"type":"string","description":"Optionaler Freitextgrund für die Ablehnung. Wird am Antrag gespeichert und dem Mitarbeiter angezeigt.","example":"Insufficient staffing during that period"}}},"UpdateAbsenceRequest":{"type":"object","properties":{"absenceTypeId":{"type":"string","description":"Neuer Abwesenheitstyp-Code (3 Zeichen). `null` senden, um den bestehenden Typ zu behalten.","example":"URL"},"startDate":{"type":"string","description":"Neues Startdatum im ISO-8601-Format (`YYYY-MM-DD`). `null` senden, um den bestehenden Wert zu behalten.","example":"2025-08-01"},"endDate":{"type":"string","description":"Neues Enddatum im ISO-8601-Format (`YYYY-MM-DD`). Muss gleich oder nach `startDate` liegen. `null` senden, um den bestehenden Wert zu behalten.","example":"2025-08-05"},"halfDay":{"type":"string","description":"Neue Halbtags-Einstellung. `\"morning\"` oder `\"afternoon\"` um auf einen Halbtag zu wechseln; leerer String `\"\"` um den Halbtag zu entfernen und auf ganztägig zu stellen; `null` um den bestehenden Wert zu behalten.","example":"morning","enum":["morning","afternoon",""]},"comments":{"type":"string","description":"Neuer Freitextkommentar. `null` senden, um den bestehenden Kommentar zu behalten; leeren String senden, um ihn zu löschen.","example":"Rescheduled trip"},"substituteEmployeeId":{"type":"string","description":"Neue Vertreter-Mitarbeiter-ID. `null` senden, um den bestehenden Vertreter zu behalten; `\"0\"` oder einen leeren String senden, um den Vertreter zu entfernen.","example":"42"}}},"AbsenceTypeDto":{"type":"object","properties":{"id":{"type":"string","description":"Abwesenheitstyp-Code (3 Zeichen), der beim Erstellen oder Aktualisieren eines Abwesenheitsantrags verwendet wird.","example":"URL"},"name":{"type":"string","description":"Lesbarer Name der Abwesenheitsart in der Sprache des Benutzers.","example":"Urlaub"},"needsApproval":{"type":"boolean","description":"Ob Anträge dieses Typs eine Genehmigung durch den Manager erfordern, bevor sie als genehmigt gelten. Bei `false` wird der Antrag sofort bei der Erstellung genehmigt.","example":true},"requiresMedicalCertificate":{"type":"boolean","description":"Ob für Abwesenheiten dieses Typs ein Attest hochgeladen werden muss.","example":false}}},"CancellationRequestDto":{"type":"object","properties":{"id":{"type":"string","description":"Interne ID des zu stornierenden Abwesenheitsantrags.","example":"12345"},"employeeId":{"type":"string","description":"Interne ID des Mitarbeiters, der den ursprünglichen Abwesenheitsantrag gestellt hat.","example":"42"},"employeeName":{"type":"string","description":"Anzeigename des Mitarbeiters, der den ursprünglichen Abwesenheitsantrag gestellt hat.","example":"Maria Müller"},"absenceType":{"type":"string","description":"Abwesenheitstyp-Code (3 Zeichen) des zu stornierenden Antrags.","example":"URL"},"startDate":{"type":"string","description":"Startdatum der zu stornierenden Abwesenheit im ISO-8601-Format (`YYYY-MM-DD`).","example":"2025-08-01"},"endDate":{"type":"string","description":"Enddatum der zu stornierenden Abwesenheit im ISO-8601-Format (`YYYY-MM-DD`).","example":"2025-08-05"},"days":{"type":"number","description":"Anzahl der Werktage, die die Abwesenheit umfasst.","format":"double","example":4.0},"status":{"type":"string","description":"Aktueller Status des Stornierungsantrags. Einer von `pending` (wartet auf Genehmigung) oder `approved`.","example":"pending","enum":["pending","approved"]},"cancelComments":{"type":"string","description":"Optionaler Freitextgrund, den der Mitarbeiter beim Stellen des Stornierungsantrags angegeben hat.","nullable":true,"example":"Plans changed"}}},"CancellationPendingCountResponse":{"type":"object","properties":{"numberOfCancellationRequests":{"type":"integer","description":"Anzahl der Stornierungsanträge im Team des Aufrufers, die auf eine Genehmigung warten.","format":"int32","example":2}}},"CategoriesResponse":{"type":"object","properties":{"categories":{"type":"array","description":"Liste der Kostenstellen/Kategorien, die für das Unternehmen des authentifizierten Benutzers verfügbar sind.","items":{"$ref":"#/components/schemas/CategoryDto"}},"defaultCategoryId":{"type":"string","description":"ID der Kategorie, die beim Erstellen eines Zeiteintrags standardmäßig vorausgewählt ist. `null`, wenn kein Standard konfiguriert ist.","nullable":true,"example":"7"},"isCategoryMandatory":{"type":"boolean","description":"Ob der Mitarbeiter beim Erstellen eines Zeiteintrags eine Kategorie auswählen muss.","example":false}}},"CategoryDto":{"type":"object","properties":{"id":{"type":"string","description":"Interne ID der Kategorie, die beim Einreichen eines Zeiteintrags verwendet wird.","example":"7"},"name":{"type":"string","description":"Lesbarer Name der Kategorie.","example":"Internal project"}},"description":"Liste der Kostenstellen/Kategorien, die für das Unternehmen des authentifizierten Benutzers verfügbar sind."},"OauthTokenResponse":{"type":"object","properties":{"access_token":{"type":"string","description":"JWT-Zugriffstoken. Bei nachfolgenden Anfragen als `Authorization: Bearer <token>` übergeben.","example":"eyJhbGciOiJSUzI1NiJ9..."},"refresh_token":{"type":"string","description":"Undurchsichtiger Refresh-Token. Gegen ein neues Zugriffstoken über `grant_type=refresh_token` eintauschen. Jeder Refresh-Token kann nur einmal verwendet werden.","example":"rt-a1b2c3d4e5f6..."},"token_type":{"type":"string","description":"Token-Typ. Immer `Bearer`.","example":"Bearer"},"expires_in":{"type":"integer","description":"Gültigkeitsdauer des Zugriffstokens in Sekunden.","format":"int32","example":3600},"scope":{"type":"string","description":"Gewährter Geltungsbereich.","example":"read"}}},"MfaChallengeResponse":{"type":"object","properties":{"mfa_required":{"type":"boolean","description":"Immer `true` — zeigt an, dass ein 2FA-Schritt erforderlich ist.","example":true},"mfa_token":{"type":"string","description":"Kurzlebiger Token, der diese MFA-Anforderung identifiziert. Als `mfa_token` mit `grant_type=mfa_totp` übergeben.","example":"mfa-a1b2c3d4e5f6..."},"mfa_expires_in":{"type":"integer","description":"Gültigkeitsdauer des MFA-Tokens in Sekunden.","format":"int32","example":300}},"description":"Wird von `grant_type=password` zurückgegeben, wenn das Konto 2FA aktiviert hat. Den `mfa_token` gegen ein vollständiges Token-Paar eintauschen, indem dieser Endpunkt erneut mit `grant_type=mfa_totp` aufgerufen wird."},"OauthErrorResponse":{"type":"object","properties":{"error":{"type":"string","description":"OAuth-2.0-Fehlercode (z. B. `invalid_grant`, `invalid_client`).","example":"invalid_grant"},"error_description":{"type":"string","description":"Menschenlesbare Fehlerbeschreibung.","example":"Invalid username or password"}}},"ProjectDto":{"type":"object","properties":{"id":{"type":"string","description":"Interne ID des Projekts, die beim Einreichen eines Zeiteintrags verwendet wird.","example":"5"},"name":{"type":"string","description":"Lesbarer Name des Projekts.","example":"Website Relaunch"},"isFavorite":{"type":"boolean","description":"Ob dieses Projekt vom authentifizierten Benutzer als Favorit markiert wurde.","example":true}},"description":"Liste der Projekte, die für das Unternehmen des authentifizierten Benutzers verfügbar sind."},"ProjectsResponse":{"type":"object","properties":{"projects":{"type":"array","description":"Liste der Projekte, die für das Unternehmen des authentifizierten Benutzers verfügbar sind.","items":{"$ref":"#/components/schemas/ProjectDto"}},"defaultProjectId":{"type":"string","description":"ID des Projekts, das beim Erstellen eines Zeiteintrags standardmäßig vorausgewählt ist. `null`, wenn kein Standard konfiguriert ist.","nullable":true,"example":"5"},"isProjectMandatory":{"type":"boolean","description":"Ob der Mitarbeiter beim Erstellen eines Zeiteintrags ein Projekt auswählen muss.","example":true}}},"SubstituteEmployeeDto":{"type":"object","properties":{"id":{"type":"string","description":"Interne Mitarbeiter-ID, die als `substituteEmployeeId` beim Erstellen eines Abwesenheitsantrags verwendet wird.","example":"42"},"name":{"type":"string","description":"Anzeigename des Mitarbeiters.","example":"Thomas Schmidt"},"department":{"type":"string","description":"Abteilung, der der Mitarbeiter angehört. `null`, wenn das Unternehmen keine Abteilungen verwendet.","nullable":true,"example":"Engineering"}}},"SubstituteRequestDto":{"type":"object","properties":{"id":{"type":"string","description":"Interne ID des Abwesenheitsantrags, für den die Vertretung angefragt wurde.","example":"12345"},"employeeId":{"type":"string","description":"Interne ID des abwesenden Mitarbeiters, der eine Vertretung benötigt.","example":"7"},"employeeName":{"type":"string","description":"Anzeigename des abwesenden Mitarbeiters.","example":"Anna Bauer"},"absenceType":{"type":"string","description":"Abwesenheitstyp-Code (3 Zeichen) der Abwesenheit, für die diese Vertretung angefragt wurde.","example":"URL"},"startDate":{"type":"string","description":"Startdatum der Abwesenheit im ISO-8601-Format (`YYYY-MM-DD`).","example":"2025-08-01"},"endDate":{"type":"string","description":"Enddatum der Abwesenheit im ISO-8601-Format (`YYYY-MM-DD`).","example":"2025-08-05"},"days":{"type":"number","description":"Anzahl der Werktage, die die Abwesenheit umfasst.","format":"double","example":4.0},"status":{"type":"string","description":"Aktueller Status der Vertretungsbestätigung. Einer von `pending` (wartet auf Bestätigung des Vertreters) oder `confirmed`.","example":"pending","enum":["pending","confirmed"]}}},"SubstitutePendingCountResponse":{"type":"object","properties":{"numberOfSubstituteRequests":{"type":"integer","description":"Anzahl der Vertretungsanfragen, die auf die Bestätigung des Aufrufers warten.","format":"int32","example":1}}},"TimeClockStatusDto":{"type":"object","properties":{"status":{"type":"string","description":"Aktueller Zustand der Zeiterfassung. Einer von `idle` (nicht aktiv), `running` (eingestempelt), `paused` oder `waiting` (gestartet, aber bis zum Ablauf eines Mindestintervalls gehalten).","example":"running","enum":["idle","running","paused","waiting"]},"startTimestamp":{"type":"integer","description":"Unix-Zeitstempel in Millisekunden, wann die aktuelle Einstempelung begonnen hat. `null`, wenn `status` `idle` ist.","format":"int64","nullable":true,"example":1753700400000},"pauseTimestamp":{"type":"integer","description":"Unix-Zeitstempel in Millisekunden, wann die aktuelle Pause begonnen hat. `null`, wenn die Uhr nicht pausiert ist.","format":"int64","nullable":true,"example":1753714800000},"workTimeElapsedSeconds":{"type":"integer","description":"Gesamte bisher vergangene Arbeitszeit in der aktuellen Sitzung, in Sekunden (ohne Pausen).","format":"int32","example":14400},"breakElapsedSeconds":{"type":"integer","description":"Dauer der aktuell laufenden Pause, in Sekunden. `0`, wenn die Uhr nicht pausiert ist.","format":"int32","example":0},"accumulatedBreakSeconds":{"type":"integer","description":"Gesamte aufgelaufene Pausenzeit in der aktuellen Sitzung, in Sekunden (einschließlich abgeschlossener Pausen).","format":"int32","example":1800},"waitSeconds":{"type":"integer","description":"Mindestanzahl verbleibender Sekunden, bevor der Mitarbeiter gemäß Unternehmensrichtlinie ausstempeln darf. `null`, wenn kein Minimum gilt.","format":"int32","nullable":true,"example":0},"isBusinessTripActive":{"type":"boolean","description":"Ob für diese Sitzung aktuell eine Dienstreise aktiv ist. `false`, wenn keine Dienstreise aktiv ist oder die Funktion nicht aktiviert ist.","example":false}}},"TimeClockBreakRegulationInfo":{"type":"object","properties":{"hasNoRemedy":{"type":"boolean","description":"`true`, wenn die Verletzung nicht automatisch behoben werden kann (z. B. die maximale tägliche Arbeitszeit ist bereits überschritten). Die Sitzung kann dann nur verworfen oder, sofern die Regelung nicht verpflichtend ist, mit `ignore` unverändert gespeichert werden.","example":false},"obligatory":{"type":"boolean","description":"`true`, wenn die Pausenregelung für dieses Unternehmen verpflichtend ist. Ist sie verpflichtend, darf die Sitzung nicht unverändert gespeichert werden; der Client blendet die Option `ignore` aus.","example":false},"correctionWorkMinutes":{"type":"integer","description":"Minuten, die bei Übernahme der Korrektur von der Arbeitszeit abgezogen würden.","format":"int32","example":30},"correctionBreakMinutes":{"type":"integer","description":"Minuten, die bei Übernahme der Korrektur zur Pausenzeit hinzugefügt würden.","format":"int32","example":30},"resultingWorkMinutes":{"type":"integer","description":"Resultierende Nettoarbeitszeit in Minuten nach Übernahme der Korrektur.","format":"int32","example":450},"resultingBreakMinutes":{"type":"integer","description":"Resultierende Gesamtpausenzeit in Minuten nach Übernahme der Korrektur.","format":"int32","example":30},"detailText":{"type":"string","description":"Menschenlesbare, lokalisierte Zusammenfassung der erforderlichen Änderung zur Anzeige.","example":"Required change: working time -0:30, break +0:30"}},"description":"Details zur Verletzung der Pausenregelung. Nur vorhanden, wenn `outcome` `confirmationRequired` ist; andernfalls null."},"TimeClockStopResponse":{"type":"object","properties":{"outcome":{"type":"string","description":"`saved`, wenn der Eintrag gespeichert wurde, oder `confirmationRequired`, wenn vor dem Speichern eine Entscheidung zur Pausenregelung erforderlich ist.","example":"saved","enum":["saved","confirmationRequired"]},"id":{"type":"string","description":"Interne ID des Zeiteintrags, der beim Ausstempeln erstellt wurde. Null, wenn `outcome` `confirmationRequired` ist.","example":"98765"},"workedMinutes":{"type":"integer","description":"Erfasste Nettoarbeitszeit der Sitzung, in Minuten (Gesamtdauer minus Pausen). Null, wenn `outcome` `confirmationRequired` ist.","format":"int32","example":450},"breakMinutes":{"type":"integer","description":"Erfasste Gesamtpausenzeit der Sitzung, in Minuten. Null, wenn `outcome` `confirmationRequired` ist.","format":"int32","example":30},"breakRegulationApplied":{"type":"boolean","description":"`true`, wenn beim gespeicherten Eintrag eine Pausenregelungskorrektur angewendet wurde (Arbeitszeit gekürzt, Pause erhöht).","example":true},"breakRegulationIgnored":{"type":"boolean","description":"`true`, wenn der Eintrag trotz Verletzung der Pausenregelung unverändert gespeichert wurde (der Benutzer hat sie ignoriert).","example":false},"breakRegulation":{"$ref":"#/components/schemas/TimeClockBreakRegulationInfo"}}},"TimeClockStopRequest":{"type":"object","properties":{"projectId":{"type":"integer","description":"ID des Projekts, dem die abgeschlossene Sitzung zugeordnet werden soll. `0` senden, wenn keine Projektzuordnung benötigt wird.","format":"int32","example":5},"categoryId":{"type":"integer","description":"ID der Kategorie, der die abgeschlossene Sitzung zugeordnet werden soll. `0` senden, wenn keine Kategoriezuordnung benötigt wird.","format":"int32","example":7},"remarks":{"type":"string","description":"Optionaler Freitextkommentar für den abgeschlossenen Zeiteintrag.","example":"Code review"},"breakRegulation":{"type":"string","description":"Entscheidung, wie eine Verletzung der Pausenregelung behandelt wird. Beim ersten Stopp weglassen: Verletzt die Sitzung die Pausenregelung, speichert der Server nicht, sondern liefert `outcome: confirmationRequired` mit der erforderlichen Änderung. Den Stopp mit `apply` erneut senden, um die Korrektur zu übernehmen (Arbeitszeit kürzen und fehlende Pause ergänzen), oder mit `ignore`, um die Sitzung unverändert zu speichern. Clients sollten `ignore` nur bei nicht verpflichtender Regelung anbieten (`obligatory` in der Bestätigungsantwort); der Server erzwingt dies derzeit nicht.","example":"apply","enum":["apply","ignore"]}}},"TimeEntryRequestDto":{"type":"object","properties":{"id":{"type":"string","description":"Interne ID des Zeiteintrags.","example":"98765"},"employeeId":{"type":"string","description":"Interne ID des Mitarbeiters, der den Zeiteintrag eingereicht hat. Nur in Team-/Manageransichten vorhanden.","nullable":true,"example":"42"},"employeeName":{"type":"string","description":"Anzeigename des Mitarbeiters, der den Zeiteintrag eingereicht hat. Nur in Team-/Manageransichten vorhanden.","nullable":true,"example":"Maria Müller"},"date":{"type":"string","description":"Datum des Zeiteintrags im ISO-8601-Format (`YYYY-MM-DD`).","example":"2025-07-28"},"startTime":{"type":"string","description":"Einstempelzeit im Format `HH:mm`.","example":"08:00"},"endTime":{"type":"string","description":"Ausstempelzeit im Format `HH:mm`.","example":"17:00"},"workedMinutes":{"type":"integer","description":"Nettoarbeitszeit für diesen Eintrag, in Minuten (Gesamtdauer minus Pausen).","format":"int32","example":480},"breakMinutes":{"type":"integer","description":"Gesamte Pausenzeit für diesen Eintrag, in Minuten.","format":"int32","example":60},"project":{"type":"string","description":"Name des Projekts, das diesem Eintrag zugeordnet ist. `null`, wenn kein Projekt zugeordnet wurde.","nullable":true,"example":"Website Relaunch"},"category":{"type":"string","description":"Name der Kategorie, die diesem Eintrag zugeordnet ist. `null`, wenn keine Kategorie zugeordnet wurde.","nullable":true,"example":"Internal project"},"remarks":{"type":"string","description":"Freitextkommentar, der diesem Eintrag beigefügt ist. `null`, wenn kein Kommentar eingegeben wurde.","nullable":true,"example":"Sprint planning"},"requestType":{"type":"string","description":"Typ der Anfrage, die diesen Eintrag erzeugt hat. Einer von `manual` (manuell eingegeben) oder `clock` (durch Ausstempeln erstellt).","example":"manual","enum":["manual","clock"]},"status":{"type":"string","description":"Genehmigungsstatus dieses Zeiteintrags. Einer von `pending` (wartet auf Genehmigung) oder `approved`.","example":"pending","enum":["pending","approved"]}}},"TimeEntryPendingCountResponse":{"type":"object","properties":{"numberOfTimeEntries":{"type":"integer","description":"Anzahl der Zeiteinträge im Team des Aufrufers, die auf eine Genehmigung warten.","format":"int32","example":5}}},"ClockEntryResponse":{"type":"object","properties":{"id":{"type":"string","description":"Interne ID des erstellten Zeiteintrags.","example":"98765"},"workedMinutes":{"type":"integer","description":"Erfasste Nettoarbeitszeit für diesen Eintrag, in Minuten (Gesamtdauer minus Pausen).","format":"int32","example":480},"breakMinutes":{"type":"integer","description":"Erfasste Gesamtpausenzeit für diesen Eintrag, in Minuten.","format":"int32","example":30}}},"ClockEntryRequest":{"required":["endTimestamp","startTimestamp"],"type":"object","properties":{"startTimestamp":{"type":"integer","description":"Beginn des Arbeitszeitraums als Unix-Zeitstempel in Millisekunden.","format":"int64","example":1753700400000},"endTimestamp":{"type":"integer","description":"Ende des Arbeitszeitraums als Unix-Zeitstempel in Millisekunden.","format":"int64","example":1753729200000},"pauseSeconds":{"type":"integer","description":"Gesamte Pausendauer innerhalb des Arbeitszeitraums, in Sekunden.","format":"int32","example":1800},"projectId":{"type":"integer","description":"ID des Projekts, dem dieser Eintrag zugeordnet werden soll. `0` senden, wenn keine Projektzuordnung benötigt wird.","format":"int32","example":5},"remarks":{"type":"string","description":"Optionaler Freitextkommentar für diesen Zeiteintrag.","example":"Sprint planning"}}},"TimeOffSummaryResponse":{"type":"object","properties":{"vacationDaysRemaining":{"type":"number","description":"Anzahl der Urlaubstage, die dem Mitarbeiter im laufenden Jahr noch zur Verfügung stehen, nach Berücksichtigung genehmigter und ausstehender Anträge.","format":"double","example":12.5},"vacationDaysTotal":{"type":"number","description":"Gesamter Urlaubsanspruch des Mitarbeiters für das laufende Jahr.","format":"double","example":30.0},"illnessDaysTaken":{"type":"number","description":"Anzahl der in diesem Jahr genommenen Krankheitstage.","format":"double","example":3.0}}},"WorkSummaryResponse":{"type":"object","properties":{"commitmentMinutes":{"type":"integer","description":"Gesamte Arbeitszeit, die der Mitarbeiter im abgefragten Zeitraum vertraglich leisten muss, in Minuten.","format":"int32","example":2400},"workedMinutes":{"type":"integer","description":"Gesamte Arbeitszeit, die der Mitarbeiter im abgefragten Zeitraum tatsächlich geleistet hat, in Minuten.","format":"int32","example":2280},"remainingMinutes":{"type":"integer","description":"Verbleibende Arbeitszeit, die im abgefragten Zeitraum noch geleistet werden muss, in Minuten. Negative Werte bedeuten Überstunden.","format":"int32","example":120},"percentDone":{"type":"integer","description":"Prozentsatz der vertraglichen Arbeitszeit, der bisher geleistet wurde, auf die nächste ganze Zahl gerundet.","format":"int32","example":95},"breakMinutes":{"type":"integer","description":"Gesamte im abgefragten Zeitraum erfasste Pausenzeit, in Minuten.","format":"int32","example":300}}},"UserProfileResponse":{"type":"object","properties":{"id":{"type":"string","description":"Interne ID des authentifizierten Benutzers.","example":"42"},"name":{"type":"string","description":"Vollständiger Anzeigename des Benutzers.","example":"Maria Müller"},"email":{"type":"string","description":"E-Mail-Adresse des Benutzers.","example":"maria.mueller@example.com"},"role":{"type":"string","description":"Rolle des Benutzers im Unternehmen. Einer von `USER`, `MANAGER` oder `ADMIN`.","example":"MANAGER","enum":["USER","MANAGER","ADMIN"]},"department":{"type":"string","description":"Name der Abteilung, der der Benutzer angehört. `null`, wenn das Unternehmen keine Abteilungen verwendet.","nullable":true,"example":"Engineering"},"branchOffice":{"type":"string","description":"Name der Niederlassung, der der Benutzer zugeordnet ist. `null`, wenn das Unternehmen keine Niederlassungen verwendet.","nullable":true,"example":"Berlin"},"country":{"type":"object","description":"Land des Benutzers, wie in seinem Profil konfiguriert. Die Struktur hängt von der Unternehmenskonfiguration ab.","nullable":true},"avatar":{"type":"string","description":"URL des Profilbilds des Benutzers. `null`, wenn kein Profilbild hochgeladen wurde.","nullable":true,"example":"/images/avatar/42/abc123.jpg?v=5"},"isBusinessTripEnabled":{"type":"boolean","description":"Ob die Dienstreisefunktion für diesen Benutzer aktiviert ist. `null`, wenn die Funktion im Unternehmensplan nicht verfügbar ist.","nullable":true,"example":true},"isTimeTrackingEnabled":{"type":"boolean","description":"Ob die Zeiterfassung für das Unternehmen des Benutzers aktiv ist.","example":true}}}},"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}}}