{
  "openapi": "3.1.0",
  "info": {
    "title": "KostenKlar API",
    "version": "1.3.0",
    "description": "Öffentliche REST-API von KostenKlar. Authentifizierung per API-Schlüssel, Antworten als JSON. Die ausführliche Anleitung steht in der Entwickler-Dokumentation.",
    "contact": {
      "name": "zirke.digital",
      "url": "https://zirke-digital.de"
    }
  },
  "servers": [
    {
      "url": "https://api.kosten-klar.de"
    }
  ],
  "tags": [
    {
      "name": "Konto"
    },
    {
      "name": "Kunden"
    },
    {
      "name": "Rechnungen"
    },
    {
      "name": "Angebote"
    },
    {
      "name": "Gutschriften"
    },
    {
      "name": "Lieferscheine"
    },
    {
      "name": "Mahnungen"
    },
    {
      "name": "Verträge"
    },
    {
      "name": "Zahlungen"
    },
    {
      "name": "Ausgaben"
    },
    {
      "name": "Produkte"
    },
    {
      "name": "Projekte"
    },
    {
      "name": "Aufgaben"
    },
    {
      "name": "Zeiterfassung"
    },
    {
      "name": "Kalender"
    },
    {
      "name": "Webhooks"
    }
  ],
  "security": [
    {
      "ApiKey": []
    }
  ],
  "paths": {
    "/v1/me": {
      "get": {
        "tags": [
          "Konto"
        ],
        "summary": "Verbindungstest",
        "description": "Zeigt, mit welchem Schlüssel Sie sprechen und welche Berechtigungen er hat. Braucht keine bestimmte Berechtigung und eignet sich als erster Aufruf.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Der Schlüssel ist gültig.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/customers": {
      "get": {
        "tags": [
          "Kunden"
        ],
        "summary": "Kunden auflisten",
        "description": "Seitenweise, aufsteigend nach `id`. Für eine Synchronisation den Filter `updatedSince` nutzen.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "customers:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          }
        ],
        "responses": {
          "200": {
            "description": "Eine Seite mit Datensätzen.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Customer"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Wert für den Parameter `cursor`, um die nächste Seite zu holen. `null` auf der letzten Seite."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Kunden"
        ],
        "summary": "Kunden anlegen",
        "description": "Pflicht sind ein Name (`company` oder `firstName` plus `lastName`), `street`, `postalCode` (genau 5 Ziffern), `city` und `email`. Bei `type: business` ist `company` Pflicht. Ohne `customerNumber` vergibt KostenKlar die nächste freie Nummer.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "customers:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomerInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Der Kunde wurde angelegt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/customers/{id}": {
      "get": {
        "tags": [
          "Kunden"
        ],
        "summary": "Einen Kunden abrufen",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "customers:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Der Kunde.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "tags": [
          "Kunden"
        ],
        "summary": "Kunden ändern",
        "description": "Geändert werden nur die gesendeten Felder. Dasselbe Schema wie beim Anlegen, aber alle Felder sind optional. Die Pflichtfelder müssen nach der Änderung weiterhin gefüllt sein.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "customers:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomerInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Der geänderte Kunde.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/invoices": {
      "get": {
        "tags": [
          "Rechnungen"
        ],
        "summary": "Rechnungen auflisten",
        "description": "Seitenweise, aufsteigend nach `id`, mit Positionen.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "invoices:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Nur Rechnungen mit diesem Status.",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "sent",
                "paid",
                "overdue",
                "cancelled",
                "archived",
                "cancellation",
                "payment_plan",
                "partially_paid",
                "dunning_level_1",
                "dunning_level_2",
                "dunning_level_3"
              ]
            }
          },
          {
            "name": "customerId",
            "in": "query",
            "required": false,
            "description": "Nur Rechnungen dieses Kunden.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Eine Seite mit Datensätzen.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Invoice"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Wert für den Parameter `cursor`, um die nächste Seite zu holen. `null` auf der letzten Seite."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Rechnungen"
        ],
        "summary": "Rechnungsentwurf anlegen",
        "description": "Legt ausschließlich einen **Entwurf** an (`status: draft`). Rechnungsnummer, Versand und Festschreiben bleiben in KostenKlar, damit die lückenlose Nummernfolge erhalten bleibt. Summen berechnet KostenKlar selbst.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "invoices:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoiceInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Der Entwurf wurde angelegt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/invoices/{id}": {
      "get": {
        "tags": [
          "Rechnungen"
        ],
        "summary": "Eine Rechnung abrufen",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "invoices:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Die Rechnung mit Positionen.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "tags": [
          "Rechnungen"
        ],
        "summary": "Rechnungsentwurf ändern",
        "description": "Ändert nur die gesendeten Felder; `items` ersetzt alle Positionen. Nur Entwürfe: ausgestellte Rechnungen antworten mit 403 `INVOICE_CANNOT_EDIT_NON_DRAFT`. Summen berechnet KostenKlar neu.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "invoices:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoiceUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Der geänderte Entwurf.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnknownReference"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/invoices/{id}/payments": {
      "post": {
        "tags": [
          "Zahlungen"
        ],
        "summary": "Zahlungseingang zu einer Rechnung erfassen",
        "description": "Wie „Zahlung erfassen“ in KostenKlar: Teil- oder Vollzahlung bis zum offenen Betrag. Bei Vollzahlung wird die Rechnung bezahlt (Webhook `invoice.paid`). Die Zahlung landet in der EÜR (Webhook `payment.received`). Nicht möglich bei Entwürfen, stornierten oder bereits bezahlten Rechnungen (403) und in festgeschriebenen Monaten.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "payments:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PaymentInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Die Rechnung mit neuem Zahlstand.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/invoices/{id}/pdf": {
      "get": {
        "tags": [
          "Rechnungen"
        ],
        "summary": "Rechnung als PDF herunterladen",
        "description": "Festgeschriebene Rechnungen liefern das unveränderliche PDF, Entwürfe werden beim Abruf frisch erzeugt. Dateiname `Rechnung_<Nummer>.pdf`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "invoices:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Das PDF (`Content-Disposition: attachment`).",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/quotes": {
      "get": {
        "tags": [
          "Angebote"
        ],
        "summary": "Angebote auflisten",
        "description": "Seitenweise, aufsteigend nach `id`, mit Positionen.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "quotes:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Nur Angebote mit diesem Status.",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "sent",
                "accepted",
                "rejected",
                "archived"
              ]
            }
          },
          {
            "name": "customerId",
            "in": "query",
            "required": false,
            "description": "Nur Angebote dieses Kunden.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Eine Seite mit Datensätzen.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Quote"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Wert für den Parameter `cursor`, um die nächste Seite zu holen. `null` auf der letzten Seite."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Angebote"
        ],
        "summary": "Angebot anlegen",
        "description": "Legt ein Angebot mit Angebotsnummer an, standardmäßig als Entwurf. Mit `markSent: true` wird es sofort als versendet markiert (`status: sent`, Versanddatum gesetzt), **ohne** E-Mail an den Kunden; das PDF liefert `GET /v1/quotes/{id}/pdf`. Summen berechnet KostenKlar. Bei `priceMode: gross` werden die Einzelpreise mit `taxRate` in Nettopreise umgerechnet (4 Nachkommastellen), Angebote speichern immer netto. Unbekannte Kunden antworten mit 422 und `details.field: customerId`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "quotes:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Das Angebot wurde angelegt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/quotes/{id}": {
      "get": {
        "tags": [
          "Angebote"
        ],
        "summary": "Ein Angebot abrufen",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "quotes:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Das Angebot mit Positionen.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "tags": [
          "Angebote"
        ],
        "summary": "Angebotsentwurf ändern",
        "description": "Ändert nur die gesendeten Felder; `items` ersetzt alle Positionen. Nur Entwürfe: versendete Angebote antworten mit 403 `QUOTE_CANNOT_EDIT_NON_DRAFT`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "quotes:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Der geänderte Entwurf.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnknownReference"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/quotes/{id}/accept": {
      "post": {
        "tags": [
          "Angebote"
        ],
        "summary": "Angebot annehmen",
        "description": "Nur aus `status: sent`. Ist das Angebot bereits angenommen, kommt es unverändert zurück (200). Andere Status antworten mit 409 `QUOTE_INVALID_STATUS_TRANSITION`, ein abgelaufenes Angebot (`validUntil` überschritten) mit 403 `QUOTE_EXPIRED`. Die Annahme läuft wie in der App: Webhook `quote.accepted`, Workflows und, falls eingestellt, die automatische Rechnung.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "quotes:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Das angenommene Angebot.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/StatusConflict"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/quotes/{id}/reject": {
      "post": {
        "tags": [
          "Angebote"
        ],
        "summary": "Angebot ablehnen",
        "description": "Nur aus `status: sent`. Ist das Angebot bereits abgelehnt, kommt es unverändert zurück (200). Andere Status antworten mit 409 `QUOTE_INVALID_STATUS_TRANSITION`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "quotes:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Das abgelehnte Angebot.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/StatusConflict"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/quotes/{id}/pdf": {
      "get": {
        "tags": [
          "Angebote"
        ],
        "summary": "Angebot als PDF herunterladen",
        "description": "Das PDF wird beim Abruf mit dem aktuellen Stand und Layout erzeugt, wie in der App. Dateiname `Angebot_<Nummer>.pdf`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "quotes:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Das PDF (`Content-Disposition: attachment`).",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/credit-notes": {
      "get": {
        "tags": [
          "Gutschriften"
        ],
        "summary": "Gutschriften auflisten",
        "description": "Seitenweise, aufsteigend nach `id`, mit Positionen. Alle Status einschließlich Entwürfen.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "credit-notes:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Nur Gutschriften mit diesem Status.",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "issued",
                "applied",
                "refunded",
                "cancelled"
              ]
            }
          },
          {
            "name": "customerId",
            "in": "query",
            "required": false,
            "description": "Nur Gutschriften dieses Kunden.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Eine Seite mit Datensätzen.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CreditNote"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Wert für den Parameter `cursor`, um die nächste Seite zu holen. `null` auf der letzten Seite."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Gutschriften"
        ],
        "summary": "Gutschriftentwurf zu einer Rechnung anlegen",
        "description": "Rechnungskorrektur zu einer ausgestellten, nicht stornierten Rechnung, immer als Entwurf. Kunde und Anschrift kommen von der Rechnung, die Nummer vergibt KostenKlar. Ausstellen, Verrechnen und Erstatten bleiben in KostenKlar. Webhook `credit-note.created`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "credit-notes:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreditNoteInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Der Gutschriftentwurf.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreditNote"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/credit-notes/{id}": {
      "get": {
        "tags": [
          "Gutschriften"
        ],
        "summary": "Eine Gutschrift abrufen",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "credit-notes:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Die Gutschrift mit Positionen.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreditNote"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/credit-notes/{id}/pdf": {
      "get": {
        "tags": [
          "Gutschriften"
        ],
        "summary": "Gutschrift als PDF herunterladen",
        "description": "Das PDF wird beim Abruf erzeugt, wie in der App. Dateiname `Gutschrift_<Nummer>.pdf`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "credit-notes:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Das PDF (`Content-Disposition: attachment`).",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/contracts": {
      "get": {
        "tags": [
          "Verträge"
        ],
        "summary": "Verträge auflisten",
        "description": "Seitenweise, aufsteigend nach `id`, mit Positionen. Alle Status einschließlich Entwürfen; filtern Sie bei Bedarf selbst oder mit `status`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "contracts:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Nur Verträge mit diesem Status.",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "sent",
                "signed",
                "active",
                "expired",
                "cancelled"
              ]
            }
          },
          {
            "name": "customerId",
            "in": "query",
            "required": false,
            "description": "Nur Verträge dieses Kunden.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Eine Seite mit Datensätzen.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Contract"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Wert für den Parameter `cursor`, um die nächste Seite zu holen. `null` auf der letzten Seite."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/contracts/{id}": {
      "get": {
        "tags": [
          "Verträge"
        ],
        "summary": "Einen Vertrag abrufen",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "contracts:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Der Vertrag mit Positionen.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contract"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/contracts/{id}/usage": {
      "get": {
        "tags": [
          "Verträge"
        ],
        "summary": "Zeitverbrauch eines Vertrags",
        "description": "Inklusivzeit und gebuchte Zeit im aktuellen Abrechnungszeitraum, z. B. für Wartungsverträge. Der Zeitraum endet mit der nächsten Rechnung (`nextInvoiceDate`); bei einmaligen Verträgen oder ohne nächstes Rechnungsdatum ist es die gesamte Laufzeit. Gezählt werden alle Zeitbuchungen für den Kunden des Vertrags im Zeitraum, bei mehreren gleichzeitig laufenden Verträgen desselben Kunden also auch Zeit für die anderen. Enthält bewusst keine Beträge oder Stundensätze.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "contracts:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Der Zeitverbrauch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContractUsage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/contracts/{id}/pdf": {
      "get": {
        "tags": [
          "Verträge"
        ],
        "summary": "Vertrag als PDF herunterladen",
        "description": "Das PDF mit Vertragstext und, falls vorhanden, digitaler Unterschrift wird beim Abruf erzeugt. Dateiname `Vertrag_<Nummer>.pdf`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "contracts:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Das PDF (`Content-Disposition: attachment`).",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/products": {
      "get": {
        "tags": [
          "Produkte"
        ],
        "summary": "Produkte und Leistungen auflisten",
        "description": "Seitenweise, nur nicht archivierte Einträge.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "products:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          }
        ],
        "responses": {
          "200": {
            "description": "Eine Seite mit Datensätzen.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Product"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Wert für den Parameter `cursor`, um die nächste Seite zu holen. `null` auf der letzten Seite."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Produkte"
        ],
        "summary": "Produkt anlegen",
        "description": "Wie im Produktkatalog: `code` ist je Konto eindeutig. Ohne `type` ist es eine Leistung (`service`, 60 Minuten je Einheit). Mit `trackStock` wird `stockQuantity` als Anfangsbestand gebucht. Schreiben verlangt das Modul Produktkatalog (403 `FEATURE_DISABLED`).",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "products:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProductInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Das angelegte Produkt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/products/{id}": {
      "get": {
        "tags": [
          "Produkte"
        ],
        "summary": "Ein Produkt abrufen",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "products:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Das Produkt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "tags": [
          "Produkte"
        ],
        "summary": "Produkt ändern",
        "description": "Ändert nur die gesendeten Felder. Den Bestand ändern Sie über `POST /v1/products/{id}/stock-movements`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "products:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProductUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Das geänderte Produkt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnknownReference"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/products/{id}/stock-movements": {
      "post": {
        "tags": [
          "Produkte"
        ],
        "summary": "Lagerbewegung buchen",
        "description": "Zugang (`in`), Abgang (`out`) oder Inventur (`correction`: `quantity` ist der gezählte Bestand, gebucht wird die Differenz). Nur bei Produkten mit `trackStock`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "products:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StockMovementInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Das Produkt mit neuem Bestand.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/projects": {
      "get": {
        "tags": [
          "Projekte"
        ],
        "summary": "Projekte auflisten",
        "description": "Seitenweise, aufsteigend nach `id`. Projekte gehören zum Modul Zeiterfassung; ist es im Konto aus, antworten alle Projekt-Endpunkte mit 403 `FEATURE_DISABLED`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "projects:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Nur Projekte mit diesem Status.",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "completed",
                "archived"
              ]
            }
          },
          {
            "name": "customerId",
            "in": "query",
            "required": false,
            "description": "Nur Projekte dieses Kunden.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Eine Seite mit Datensätzen.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Project"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Wert für den Parameter `cursor`, um die nächste Seite zu holen. `null` auf der letzten Seite."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Projekte"
        ],
        "summary": "Projekt anlegen",
        "description": "Neue Projekte starten mit `status: active`. Der Kunde muss bereits existieren.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "projects:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Das Projekt wurde angelegt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/projects/{id}": {
      "get": {
        "tags": [
          "Projekte"
        ],
        "summary": "Ein Projekt abrufen",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "projects:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Das Projekt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "tags": [
          "Projekte"
        ],
        "summary": "Projekt ändern",
        "description": "Geändert werden nur die gesendeten Felder. Abschließen mit `status: completed`, statt Löschen `status: archived`. `null` entfernt Datum oder Budget.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "projects:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Das geänderte Projekt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnknownReference"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/projects/{id}/milestones": {
      "get": {
        "tags": [
          "Projekte"
        ],
        "summary": "Meilensteine eines Projekts",
        "description": "Meilensteine bzw. Abschläge in ihrer Reihenfolge, mit Betrag und Abrechnungsstand.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "projects:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Die Meilensteine.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Milestone"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/projects/{id}/summary": {
      "get": {
        "tags": [
          "Projekte"
        ],
        "summary": "Fortschritt eines Projekts",
        "description": "Budget, erfasste Zeiten, abgerechneter Betrag, Meilensteine und Aufgaben auf einen Blick. Enthält bewusst keine Kosten, Margen oder Stundensätze.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "projects:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Der Projektfortschritt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectSummary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/projects/{id}/acceptances": {
      "get": {
        "tags": [
          "Projekte"
        ],
        "summary": "Abnahmeprotokolle eines Projekts",
        "description": "Alle Abnahmeprotokolle, aufsteigend nach `protocolDate`. Ohne Unterschriftsbilder; das unterschriebene Dokument liefert der PDF-Endpunkt.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "projects:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Die Abnahmeprotokolle.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Acceptance"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/projects/{id}/acceptances/{acceptanceId}/pdf": {
      "get": {
        "tags": [
          "Projekte"
        ],
        "summary": "Abnahmeprotokoll als PDF herunterladen",
        "description": "Protokoll mit Mängelliste und Unterschriften. Dateiname `Abnahme_<protocolDate>.pdf`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "projects:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          },
          {
            "$ref": "#/components/parameters/AcceptanceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Das PDF (`Content-Disposition: attachment`).",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/time-entries": {
      "get": {
        "tags": [
          "Zeiterfassung"
        ],
        "summary": "Zeitbuchungen auflisten",
        "description": "Seitenweise, aufsteigend nach `id`. Gelöschte Buchungen fehlen. Ohne Stundensätze, Kosten und Mitarbeiter. Zeitbuchungen gehören zum Modul Zeiterfassung; ist es im Konto aus, antworten die Endpunkte mit 403 `FEATURE_DISABLED`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "time-entries:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "name": "projectId",
            "in": "query",
            "required": false,
            "description": "Nur Buchungen dieses Projekts.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "customerId",
            "in": "query",
            "required": false,
            "description": "Nur Buchungen dieses Kunden.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Erster Tag (einschließlich), bezogen auf den Beginn der Buchung in Europe/Berlin.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Letzter Tag (einschließlich).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Eine Seite mit Datensätzen.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TimeEntry"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Wert für den Parameter `cursor`, um die nächste Seite zu holen. `null` auf der letzten Seite."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Zeiterfassung"
        ],
        "summary": "Zeitbuchung anlegen",
        "description": "Bucht für den Kontoinhaber (sein Mitarbeiterprofil) mit denselben Prüfungen wie die App: Beginn nicht in der Zukunft, keine Überschneidung, Woche nicht eingereicht, höchstens 16 Stunden. Stundensatz und Kosten kommen aus dem Mitarbeiterprofil. Ohne Mitarbeiterprofil: 422. Webhook `time-entry.created`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "time-entries:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TimeEntryInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Die angelegte Buchung.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TimeEntry"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/time-entries/{id}": {
      "get": {
        "tags": [
          "Zeiterfassung"
        ],
        "summary": "Eine Zeitbuchung abrufen",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "time-entries:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Die Zeitbuchung.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TimeEntry"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "tags": [
          "Zeiterfassung"
        ],
        "summary": "Zeitbuchung ändern",
        "description": "Nur offene Buchungen (`editable: true`). Ändert nur die gesendeten Felder; das Ende rechnet KostenKlar aus Beginn, Dauer und Pause neu.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "time-entries:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TimeEntryUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Die geänderte Buchung.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TimeEntry"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnknownReference"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Zeiterfassung"
        ],
        "summary": "Zeitbuchung löschen",
        "description": "Nur offene Buchungen (`editable: true`).",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "time-entries:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "204": {
            "description": "Gelöscht."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/calendar/busy": {
      "get": {
        "tags": [
          "Kalender"
        ],
        "summary": "Belegte Zeiten",
        "description": "Alle eigenen Termine des Kontos, die den Zeitraum berühren, ohne abgesagte (`cancelled`). Serien sind in einzelne Termine ausgeklappt. Fristen aus Rechnungen, Aufgaben usw. zählen nicht. Bewusst ohne Titel und Inhalte. Ganztägige Termine reichen von 00:00 Uhr des ersten bis 00:00 Uhr nach dem letzten Tag in Europe/Berlin. Sortiert nach Beginn. Der Zeitraum darf höchstens 62 Tage umfassen. Kalender ist ein Modul; ist es im Konto aus, antworten alle Kalender-Endpunkte mit 403 `FEATURE_DISABLED`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "calendar:read",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": true,
            "description": "Beginn des Zeitraums (ISO 8601 mit Uhrzeit).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "description": "Ende des Zeitraums (ausschließlich), nach `from`, höchstens 62 Tage später.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Die belegten Zeiten.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BusySlot"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/calendar/events": {
      "post": {
        "tags": [
          "Kalender"
        ],
        "summary": "Termin anlegen",
        "description": "Legt einen Termin mit Uhrzeit an (keine Serie, nicht ganztägig), standardmäßig als `tentative`. Das Ende muss nach dem Beginn liegen, höchstens 12 Stunden. Es gelten die Grenzen des Tarifs (Free-Plan: Zahl künftiger Termine, 403 `FREE_PLAN_CALENDAR_LIMIT_REACHED`). Eine Verknüpfung (`linkType` und `linkId` nur gemeinsam) muss auf einen eigenen Kunden bzw. ein eigenes Projekt zeigen, sonst 422 mit `details.field: linkId`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "calendar:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CalendarEventInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Der Termin wurde angelegt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalendarEvent"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/calendar/events/{id}": {
      "get": {
        "tags": [
          "Kalender"
        ],
        "summary": "Einen Termin abrufen",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "calendar:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Der Termin. Bei Serien die Zeiten des ersten Termins.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalendarEvent"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "tags": [
          "Kalender"
        ],
        "summary": "Termin ändern",
        "description": "Geändert werden nur die gesendeten Felder. Absagen mit `status: cancelled`. Wird nur `start` gesendet, bleibt die Dauer erhalten. Höchstens 12 Stunden. Serien und das Verschieben ganztägiger Termine antworten mit 400.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "calendar:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CalendarEventUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Der geänderte Termin.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalendarEvent"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnknownReference"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Kalender"
        ],
        "summary": "Termin löschen",
        "description": "Löscht den Termin endgültig. Serien antworten mit 400; zum Absagen ohne Löschen `PATCH` mit `status: cancelled`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "calendar:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "204": {
            "description": "Gelöscht."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/tasks": {
      "get": {
        "tags": [
          "Aufgaben"
        ],
        "summary": "Aufgaben auflisten",
        "description": "Seitenweise, aufsteigend nach `id`, mit Teilschritten. Ohne `status` kommen offene und erledigte Aufgaben.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "tasks:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "`open`, `done` oder `all` (Standard).",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "done",
                "all"
              ]
            }
          },
          {
            "name": "customerId",
            "in": "query",
            "required": false,
            "description": "Nur Aufgaben dieses Kunden.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "projectId",
            "in": "query",
            "required": false,
            "description": "Nur Aufgaben dieses Projekts.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Eine Seite mit Datensätzen.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Task"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Wert für den Parameter `cursor`, um die nächste Seite zu holen. `null` auf der letzten Seite."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Aufgaben"
        ],
        "summary": "Aufgabe anlegen",
        "description": "Optional einem Kunden, Projekt und einer Person zugeordnet. Unbekannte Verweise antworten mit 422 und `details.field`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "tasks:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaskInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Die Aufgabe wurde angelegt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/tasks/{id}": {
      "get": {
        "tags": [
          "Aufgaben"
        ],
        "summary": "Eine Aufgabe abrufen",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "tasks:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Die Aufgabe mit Teilschritten.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "tags": [
          "Aufgaben"
        ],
        "summary": "Aufgabe ändern",
        "description": "Geändert werden nur die gesendeten Felder. `status: done` erledigt die Aufgabe; bei einer Wiederholung legt KostenKlar die nächste an. `null` löst Zuordnungen bzw. entfernt Datum und Aufwand.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "tasks:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaskUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Die geänderte Aufgabe.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnknownReference"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Aufgaben"
        ],
        "summary": "Aufgabe löschen",
        "description": "Löscht die Aufgabe mit ihren Teilschritten. Auf die Aufgabe gebuchte Zeiten bleiben erhalten.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "tasks:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "204": {
            "description": "Gelöscht."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/tasks/{id}/checklist": {
      "post": {
        "tags": [
          "Aufgaben"
        ],
        "summary": "Teilschritt hinzufügen",
        "description": "Hängt einen Teilschritt am Ende an.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "tasks:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChecklistItemInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Der Teilschritt wurde angelegt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChecklistItem"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/tasks/{id}/checklist/{itemId}": {
      "patch": {
        "tags": [
          "Aufgaben"
        ],
        "summary": "Teilschritt ändern",
        "description": "Titel ändern oder mit `done` abhaken.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "tasks:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          },
          {
            "$ref": "#/components/parameters/ItemId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChecklistItemUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Der geänderte Teilschritt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChecklistItem"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnknownReference"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Aufgaben"
        ],
        "summary": "Teilschritt löschen",
        "description": "Entfernt einen Teilschritt.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "tasks:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          },
          {
            "$ref": "#/components/parameters/ItemId"
          }
        ],
        "responses": {
          "204": {
            "description": "Gelöscht."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/delivery-notes": {
      "get": {
        "tags": [
          "Lieferscheine"
        ],
        "summary": "Lieferscheine auflisten",
        "description": "Seitenweise, mit Positionen. Modul Lieferscheine, sonst 403 `FEATURE_DISABLED`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "delivery-notes:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "issued",
                "cancelled"
              ]
            }
          },
          {
            "name": "customerId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Eine Seite mit Datensätzen.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DeliveryNote"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Wert für den Parameter `cursor`, um die nächste Seite zu holen. `null` auf der letzten Seite."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/delivery-notes/{id}": {
      "get": {
        "tags": [
          "Lieferscheine"
        ],
        "summary": "Einen Lieferschein abrufen",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "delivery-notes:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Der Lieferschein mit Positionen.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryNote"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/delivery-notes/{id}/pdf": {
      "get": {
        "tags": [
          "Lieferscheine"
        ],
        "summary": "Lieferschein als PDF herunterladen",
        "description": "Dateiname `Lieferschein_<Nummer>.pdf`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "delivery-notes:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Das PDF (`Content-Disposition: attachment`).",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/dunnings": {
      "get": {
        "tags": [
          "Mahnungen"
        ],
        "summary": "Mahnungen auflisten",
        "description": "Seitenweise. Mahnläufe und Versand bleiben in KostenKlar. Modul Mahnwesen, sonst 403 `FEATURE_DISABLED`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "dunnings:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "sent",
                "paid",
                "cancelled",
                "escalated"
              ]
            }
          },
          {
            "name": "level",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "first",
                "second",
                "final",
                "collections"
              ]
            }
          },
          {
            "name": "invoiceId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "customerId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Eine Seite mit Datensätzen.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Dunning"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Wert für den Parameter `cursor`, um die nächste Seite zu holen. `null` auf der letzten Seite."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/dunnings/{id}": {
      "get": {
        "tags": [
          "Mahnungen"
        ],
        "summary": "Eine Mahnung abrufen",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "dunnings:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Die Mahnung.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Dunning"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/dunnings/{id}/pdf": {
      "get": {
        "tags": [
          "Mahnungen"
        ],
        "summary": "Mahnung als PDF herunterladen",
        "description": "Dateiname `Mahnung_<Rechnungsnummer>_<Stufe>.pdf`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "dunnings:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Das PDF (`Content-Disposition: attachment`).",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/payments": {
      "get": {
        "tags": [
          "Zahlungen"
        ],
        "summary": "Zahlungen auflisten",
        "description": "Zahlungen der Einnahmen-Überschuss-Rechnung mit Zuordnung zu Rechnungen und Gutschriften, seitenweise. Modul EÜR, sonst 403 `FEATURE_DISABLED`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "payments:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "name": "direction",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "in",
                "out"
              ]
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Erster Tag (JJJJ-MM-TT, einschließlich).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Letzter Tag (JJJJ-MM-TT, einschließlich).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Eine Seite mit Datensätzen.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Payment"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Wert für den Parameter `cursor`, um die nächste Seite zu holen. `null` auf der letzten Seite."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/payments/{id}": {
      "get": {
        "tags": [
          "Zahlungen"
        ],
        "summary": "Eine Zahlung abrufen",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "payments:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Die Zahlung mit Zuordnungen.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payment"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/expenses": {
      "get": {
        "tags": [
          "Ausgaben"
        ],
        "summary": "Ausgaben auflisten",
        "description": "Ausgaben und Einnahmen ohne Rechnung, seitenweise, ohne Belegdatei. Modul Ausgaben, sonst 403 `FEATURE_DISABLED`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "expenses:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "expense",
                "income"
              ]
            }
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "office",
                "travel",
                "equipment",
                "software",
                "marketing",
                "training",
                "utilities",
                "rent",
                "insurance",
                "taxes",
                "salary",
                "meals",
                "entertainment",
                "other"
              ]
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Erster Tag (JJJJ-MM-TT, einschließlich).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Letzter Tag (JJJJ-MM-TT, einschließlich).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Eine Seite mit Datensätzen.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Expense"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Wert für den Parameter `cursor`, um die nächste Seite zu holen. `null` auf der letzten Seite."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Ausgaben"
        ],
        "summary": "Ausgabe anlegen",
        "description": "Mit denselben Prüfungen wie in KostenKlar (§ 13b nur mit Steuersatz, Privat ohne Steuer, festgeschriebene Monate, Kassenabschluss bei Barzahlung). Aufteilung auf mehrere Steuersätze und Anlagegüter bleiben in KostenKlar. Webhook `expense.created`.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "expenses:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExpenseInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Die angelegte Ausgabe.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Expense"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/expenses/{id}": {
      "get": {
        "tags": [
          "Ausgaben"
        ],
        "summary": "Eine Ausgabe abrufen",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "expenses:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Die Ausgabe.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Expense"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "tags": [
          "Ausgaben"
        ],
        "summary": "Ausgabe ändern",
        "description": "Ändert nur die gesendeten Felder, mit denselben Prüfungen wie beim Anlegen.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "expenses:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExpenseUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Die geänderte Ausgabe.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Expense"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnknownReference"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/expenses/{id}/receipt": {
      "post": {
        "tags": [
          "Ausgaben"
        ],
        "summary": "Beleg hochladen",
        "description": "Beleg als PDF, PNG, JPG oder E-Rechnung (XML) im Feld `file` (`multipart/form-data`), höchstens 10 MB. Ein vorhandener Beleg wird als Version archiviert (GoBD).",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "x-required-scope": "expenses:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Die Ausgabe mit Beleg.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Expense"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "webhooks": {
    "invoice.created": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Eine Rechnung wurde angelegt (auch als Entwurf).",
        "description": "Antworten Sie innerhalb von 5 Sekunden mit einem 2xx-Status. Jede andere Antwort gilt als Fehlschlag und wird erneut versucht.",
        "parameters": [
          {
            "name": "KostenKlar-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1790000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "KostenKlar-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "invoice.created"
            }
          },
          {
            "name": "KostenKlar-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Eindeutig je Zustellung. Bei Wiederholungen bleibt sie gleich, nutzen Sie sie zur Entdoppelung."
                  },
                  "type": {
                    "type": "string",
                    "const": "invoice.created"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "$ref": "#/components/schemas/Invoice"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Empfangen."
          }
        }
      }
    },
    "invoice.sent": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Eine Rechnung wurde versendet.",
        "description": "Antworten Sie innerhalb von 5 Sekunden mit einem 2xx-Status. Jede andere Antwort gilt als Fehlschlag und wird erneut versucht.",
        "parameters": [
          {
            "name": "KostenKlar-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1790000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "KostenKlar-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "invoice.sent"
            }
          },
          {
            "name": "KostenKlar-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Eindeutig je Zustellung. Bei Wiederholungen bleibt sie gleich, nutzen Sie sie zur Entdoppelung."
                  },
                  "type": {
                    "type": "string",
                    "const": "invoice.sent"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "$ref": "#/components/schemas/Invoice"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Empfangen."
          }
        }
      }
    },
    "invoice.paid": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Eine Rechnung wurde vollständig bezahlt.",
        "description": "Antworten Sie innerhalb von 5 Sekunden mit einem 2xx-Status. Jede andere Antwort gilt als Fehlschlag und wird erneut versucht.",
        "parameters": [
          {
            "name": "KostenKlar-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1790000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "KostenKlar-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "invoice.paid"
            }
          },
          {
            "name": "KostenKlar-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Eindeutig je Zustellung. Bei Wiederholungen bleibt sie gleich, nutzen Sie sie zur Entdoppelung."
                  },
                  "type": {
                    "type": "string",
                    "const": "invoice.paid"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "$ref": "#/components/schemas/Invoice"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Empfangen."
          }
        }
      }
    },
    "invoice.overdue": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Eine Rechnung ist überfällig geworden.",
        "description": "Antworten Sie innerhalb von 5 Sekunden mit einem 2xx-Status. Jede andere Antwort gilt als Fehlschlag und wird erneut versucht.",
        "parameters": [
          {
            "name": "KostenKlar-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1790000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "KostenKlar-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "invoice.overdue"
            }
          },
          {
            "name": "KostenKlar-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Eindeutig je Zustellung. Bei Wiederholungen bleibt sie gleich, nutzen Sie sie zur Entdoppelung."
                  },
                  "type": {
                    "type": "string",
                    "const": "invoice.overdue"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "$ref": "#/components/schemas/Invoice"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Empfangen."
          }
        }
      }
    },
    "quote.created": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Ein Angebot wurde angelegt (auch als Entwurf und als neue Fassung).",
        "description": "Antworten Sie innerhalb von 5 Sekunden mit einem 2xx-Status. Jede andere Antwort gilt als Fehlschlag und wird erneut versucht.",
        "parameters": [
          {
            "name": "KostenKlar-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1790000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "KostenKlar-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "quote.created"
            }
          },
          {
            "name": "KostenKlar-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Eindeutig je Zustellung. Bei Wiederholungen bleibt sie gleich, nutzen Sie sie zur Entdoppelung."
                  },
                  "type": {
                    "type": "string",
                    "const": "quote.created"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "$ref": "#/components/schemas/Quote"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Empfangen."
          }
        }
      }
    },
    "quote.accepted": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Ein Angebot wurde angenommen.",
        "description": "Antworten Sie innerhalb von 5 Sekunden mit einem 2xx-Status. Jede andere Antwort gilt als Fehlschlag und wird erneut versucht.",
        "parameters": [
          {
            "name": "KostenKlar-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1790000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "KostenKlar-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "quote.accepted"
            }
          },
          {
            "name": "KostenKlar-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Eindeutig je Zustellung. Bei Wiederholungen bleibt sie gleich, nutzen Sie sie zur Entdoppelung."
                  },
                  "type": {
                    "type": "string",
                    "const": "quote.accepted"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "$ref": "#/components/schemas/Quote"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Empfangen."
          }
        }
      }
    },
    "credit-note.created": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Eine Gutschrift wurde angelegt (auch als Entwurf).",
        "description": "Antworten Sie innerhalb von 5 Sekunden mit einem 2xx-Status. Jede andere Antwort gilt als Fehlschlag und wird erneut versucht.",
        "parameters": [
          {
            "name": "KostenKlar-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1790000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "KostenKlar-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "credit-note.created"
            }
          },
          {
            "name": "KostenKlar-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Eindeutig je Zustellung. Bei Wiederholungen bleibt sie gleich, nutzen Sie sie zur Entdoppelung."
                  },
                  "type": {
                    "type": "string",
                    "const": "credit-note.created"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "$ref": "#/components/schemas/CreditNote"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Empfangen."
          }
        }
      }
    },
    "payment.received": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Ein Zahlungseingang wurde erfasst: per Hand, aus dem Bankabgleich oder über die API.",
        "description": "Antworten Sie innerhalb von 5 Sekunden mit einem 2xx-Status. Jede andere Antwort gilt als Fehlschlag und wird erneut versucht.",
        "parameters": [
          {
            "name": "KostenKlar-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1790000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "KostenKlar-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "payment.received"
            }
          },
          {
            "name": "KostenKlar-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Eindeutig je Zustellung. Bei Wiederholungen bleibt sie gleich, nutzen Sie sie zur Entdoppelung."
                  },
                  "type": {
                    "type": "string",
                    "const": "payment.received"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "$ref": "#/components/schemas/Payment"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Empfangen."
          }
        }
      }
    },
    "expense.created": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Eine Ausgabe oder Einnahme ohne Rechnung wurde angelegt.",
        "description": "Antworten Sie innerhalb von 5 Sekunden mit einem 2xx-Status. Jede andere Antwort gilt als Fehlschlag und wird erneut versucht.",
        "parameters": [
          {
            "name": "KostenKlar-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1790000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "KostenKlar-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "expense.created"
            }
          },
          {
            "name": "KostenKlar-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Eindeutig je Zustellung. Bei Wiederholungen bleibt sie gleich, nutzen Sie sie zur Entdoppelung."
                  },
                  "type": {
                    "type": "string",
                    "const": "expense.created"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "$ref": "#/components/schemas/Expense"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Empfangen."
          }
        }
      }
    },
    "time-entry.created": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Eine Zeitbuchung wurde angelegt (auch per Stoppuhr).",
        "description": "Antworten Sie innerhalb von 5 Sekunden mit einem 2xx-Status. Jede andere Antwort gilt als Fehlschlag und wird erneut versucht.",
        "parameters": [
          {
            "name": "KostenKlar-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1790000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "KostenKlar-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "time-entry.created"
            }
          },
          {
            "name": "KostenKlar-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Eindeutig je Zustellung. Bei Wiederholungen bleibt sie gleich, nutzen Sie sie zur Entdoppelung."
                  },
                  "type": {
                    "type": "string",
                    "const": "time-entry.created"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "$ref": "#/components/schemas/TimeEntry"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Empfangen."
          }
        }
      }
    },
    "customer.created": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Ein Kunde wurde angelegt.",
        "description": "Antworten Sie innerhalb von 5 Sekunden mit einem 2xx-Status. Jede andere Antwort gilt als Fehlschlag und wird erneut versucht.",
        "parameters": [
          {
            "name": "KostenKlar-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1790000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "KostenKlar-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "customer.created"
            }
          },
          {
            "name": "KostenKlar-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Eindeutig je Zustellung. Bei Wiederholungen bleibt sie gleich, nutzen Sie sie zur Entdoppelung."
                  },
                  "type": {
                    "type": "string",
                    "const": "customer.created"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "$ref": "#/components/schemas/Customer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Empfangen."
          }
        }
      }
    },
    "customer.updated": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Ein Kunde wurde geändert.",
        "description": "Antworten Sie innerhalb von 5 Sekunden mit einem 2xx-Status. Jede andere Antwort gilt als Fehlschlag und wird erneut versucht.",
        "parameters": [
          {
            "name": "KostenKlar-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1790000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "KostenKlar-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "customer.updated"
            }
          },
          {
            "name": "KostenKlar-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Eindeutig je Zustellung. Bei Wiederholungen bleibt sie gleich, nutzen Sie sie zur Entdoppelung."
                  },
                  "type": {
                    "type": "string",
                    "const": "customer.updated"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "$ref": "#/components/schemas/Customer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Empfangen."
          }
        }
      }
    },
    "project.created": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Ein Projekt wurde angelegt.",
        "description": "Antworten Sie innerhalb von 5 Sekunden mit einem 2xx-Status. Jede andere Antwort gilt als Fehlschlag und wird erneut versucht.",
        "parameters": [
          {
            "name": "KostenKlar-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1790000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "KostenKlar-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "project.created"
            }
          },
          {
            "name": "KostenKlar-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Eindeutig je Zustellung. Bei Wiederholungen bleibt sie gleich, nutzen Sie sie zur Entdoppelung."
                  },
                  "type": {
                    "type": "string",
                    "const": "project.created"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "$ref": "#/components/schemas/Project"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Empfangen."
          }
        }
      }
    },
    "project.updated": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Ein Projekt wurde geändert, auch beim Statuswechsel und wenn sich seine Meilensteine ändern (anlegen, ändern, erledigen, abrechnen, löschen).",
        "description": "Antworten Sie innerhalb von 5 Sekunden mit einem 2xx-Status. Jede andere Antwort gilt als Fehlschlag und wird erneut versucht.",
        "parameters": [
          {
            "name": "KostenKlar-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1790000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "KostenKlar-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "project.updated"
            }
          },
          {
            "name": "KostenKlar-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Eindeutig je Zustellung. Bei Wiederholungen bleibt sie gleich, nutzen Sie sie zur Entdoppelung."
                  },
                  "type": {
                    "type": "string",
                    "const": "project.updated"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "$ref": "#/components/schemas/Project"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Empfangen."
          }
        }
      }
    },
    "project.deleted": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Ein Projekt wurde gelöscht. `data` enthält nur die Eckdaten vor dem Löschen.",
        "description": "Antworten Sie innerhalb von 5 Sekunden mit einem 2xx-Status. Jede andere Antwort gilt als Fehlschlag und wird erneut versucht.",
        "parameters": [
          {
            "name": "KostenKlar-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1790000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "KostenKlar-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "project.deleted"
            }
          },
          {
            "name": "KostenKlar-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Eindeutig je Zustellung. Bei Wiederholungen bleibt sie gleich, nutzen Sie sie zur Entdoppelung."
                  },
                  "type": {
                    "type": "string",
                    "const": "project.deleted"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "$ref": "#/components/schemas/DeletedProject"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Empfangen."
          }
        }
      }
    },
    "task.created": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Eine Aufgabe wurde angelegt.",
        "description": "Antworten Sie innerhalb von 5 Sekunden mit einem 2xx-Status. Jede andere Antwort gilt als Fehlschlag und wird erneut versucht.",
        "parameters": [
          {
            "name": "KostenKlar-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1790000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "KostenKlar-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "task.created"
            }
          },
          {
            "name": "KostenKlar-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Eindeutig je Zustellung. Bei Wiederholungen bleibt sie gleich, nutzen Sie sie zur Entdoppelung."
                  },
                  "type": {
                    "type": "string",
                    "const": "task.created"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "$ref": "#/components/schemas/Task"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Empfangen."
          }
        }
      }
    },
    "task.completed": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Eine Aufgabe wurde erledigt.",
        "description": "Antworten Sie innerhalb von 5 Sekunden mit einem 2xx-Status. Jede andere Antwort gilt als Fehlschlag und wird erneut versucht.",
        "parameters": [
          {
            "name": "KostenKlar-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1790000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "KostenKlar-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "task.completed"
            }
          },
          {
            "name": "KostenKlar-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Eindeutig je Zustellung. Bei Wiederholungen bleibt sie gleich, nutzen Sie sie zur Entdoppelung."
                  },
                  "type": {
                    "type": "string",
                    "const": "task.completed"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "$ref": "#/components/schemas/Task"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Empfangen."
          }
        }
      }
    },
    "workflow.triggered": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Eine eigene Workflow-Regel mit der Aktion \"Webhook auslösen\" hat gegriffen.",
        "description": "Antworten Sie innerhalb von 5 Sekunden mit einem 2xx-Status. Jede andere Antwort gilt als Fehlschlag und wird erneut versucht.",
        "parameters": [
          {
            "name": "KostenKlar-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1790000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          },
          {
            "name": "KostenKlar-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "workflow.triggered"
            }
          },
          {
            "name": "KostenKlar-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Eindeutig je Zustellung. Bei Wiederholungen bleibt sie gleich, nutzen Sie sie zur Entdoppelung."
                  },
                  "type": {
                    "type": "string",
                    "const": "workflow.triggered"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "$ref": "#/components/schemas/WorkflowTrigger"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Empfangen."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "API-Schlüssel im Format `kk_live_…`. Anlegen unter Einstellungen, Entwickler & API."
      }
    },
    "parameters": {
      "Id": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "ItemId": {
        "name": "itemId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "AcceptanceId": {
        "name": "acceptanceId",
        "in": "path",
        "required": true,
        "description": "ID des Abnahmeprotokolls aus `GET /v1/projects/{id}/acceptances`.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Datensätze pro Seite (1 bis 100, Standard 50).",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 50
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "description": "Der Wert `nextCursor` der vorigen Antwort.",
        "schema": {
          "type": "string",
          "maxLength": 100
        }
      },
      "UpdatedSince": {
        "name": "updatedSince",
        "in": "query",
        "required": false,
        "description": "Nur Datensätze, die seit diesem Zeitpunkt geändert wurden (ISO 8601).",
        "schema": {
          "type": "string",
          "format": "date-time"
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "Eindeutiger Wert pro Anfrage, 8 bis 100 Zeichen (Buchstaben, Ziffern, `.`, `:`, `_`, `-`). Eine Wiederholung mit demselben Key und Inhalt liefert die gespeicherte Antwort statt einen zweiten Datensatz zu erzeugen.",
        "schema": {
          "type": "string",
          "minLength": 8,
          "maxLength": 100,
          "pattern": "^[A-Za-z0-9_.:-]{8,100}$"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Die Anfrage ist ungültig (Format, Pflichtfeld, unbekanntes Feld, ungültiger Cursor oder fehlender Idempotency-Key).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Schlüssel fehlt, ist ungültig, widerrufen oder abgelaufen (`API_KEY_INVALID`, `API_KEY_EXPIRED`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Dem Schlüssel fehlt die Berechtigung (`API_SCOPE_MISSING`) oder die API ist für das Konto nicht freigeschaltet (`API_NOT_AVAILABLE`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Den Datensatz gibt es nicht. Fremde Datensätze liefern ebenfalls 404, nie 403.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Zu viele Anfragen (`API_RATE_LIMITED`). Der Header `Retry-After` nennt die Wartezeit in Sekunden.",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "IdempotencyConflict": {
        "description": "Der `Idempotency-Key` wurde bereits mit anderem Inhalt verwendet (`API_IDEMPOTENCY_MISMATCH`), oder der Kunde beziehungsweise ein Produkt der Rechnung existiert nicht (`NOT_FOUND`, `details.field` nennt das Feld).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "UnknownReference": {
        "description": "Ein Verweis im Body existiert nicht (`NOT_FOUND`, `details.field` nennt das Feld, z. B. `customerId`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "StatusConflict": {
        "description": "Der Status erlaubt die Aktion nicht (`QUOTE_INVALID_STATUS_TRANSITION`, `details.status` nennt den aktuellen), oder die erste Anfrage mit diesem `Idempotency-Key` läuft noch (`API_IDEMPOTENCY_IN_PROGRESS`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "IdempotencyInProgress": {
        "description": "Die erste Anfrage mit diesem `Idempotency-Key` läuft noch (`API_IDEMPOTENCY_IN_PROGRESS`). Kurz warten und mit demselben Key wiederholen.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "statusCode",
          "code",
          "message"
        ],
        "properties": {
          "statusCode": {
            "type": "integer",
            "example": 403
          },
          "code": {
            "type": "string",
            "example": "API_SCOPE_MISSING",
            "description": "Stabiler, maschinenlesbarer Code. Darauf programmieren, nicht auf `message`."
          },
          "message": {
            "type": "string",
            "description": "Lesbare Meldung auf Deutsch. Kann sich ändern."
          },
          "hint": {
            "type": [
              "string",
              "null"
            ]
          },
          "details": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "path": {
            "type": "string"
          }
        }
      },
      "Me": {
        "type": "object",
        "properties": {
          "keyId": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string",
            "example": "kk_live_AbCd1234"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "customers:read",
                "customers:write",
                "invoices:read",
                "invoices:write",
                "quotes:read",
                "quotes:write",
                "products:read",
                "products:write",
                "projects:read",
                "projects:write",
                "tasks:read",
                "tasks:write",
                "credit-notes:read",
                "credit-notes:write",
                "contracts:read",
                "delivery-notes:read",
                "dunnings:read",
                "time-entries:read",
                "time-entries:write",
                "calendar:read",
                "calendar:write",
                "payments:read",
                "payments:write",
                "expenses:read",
                "expenses:write"
              ]
            }
          },
          "scopeLabels": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "expiresAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "Address": {
        "type": "object",
        "properties": {
          "street": {
            "type": [
              "string",
              "null"
            ]
          },
          "postalCode": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "country": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Customer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "customerNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "private",
              "business",
              null
            ]
          },
          "partyType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "customer",
              "prospect",
              "supplier",
              "partner",
              "other",
              null
            ]
          },
          "company": {
            "type": [
              "string",
              "null"
            ]
          },
          "salutation": {
            "type": [
              "string",
              "null"
            ]
          },
          "firstName": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastName": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "mobilePhone": {
            "type": [
              "string",
              "null"
            ]
          },
          "website": {
            "type": [
              "string",
              "null"
            ]
          },
          "contactPerson": {
            "type": [
              "string",
              "null"
            ]
          },
          "address": {
            "$ref": "#/components/schemas/Address"
          },
          "shippingAddress": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "type": "object",
                "properties": {
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "street": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "postalCode": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "city": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "country": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              }
            ],
            "description": "Abweichende Lieferadresse, sonst `null`."
          },
          "contacts": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "role": {
                  "type": "string"
                },
                "email": {
                  "type": "string"
                },
                "phone": {
                  "type": "string"
                }
              }
            }
          },
          "taxNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "vatId": {
            "type": [
              "string",
              "null"
            ]
          },
          "leitwegId": {
            "type": [
              "string",
              "null"
            ]
          },
          "taxMode": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "standard",
              "reverse_charge",
              "tax_exempt",
              null
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "defaultPaymentDays": {
            "type": [
              "number",
              "null"
            ]
          },
          "isActive": {
            "type": "boolean"
          },
          "isBlocked": {
            "type": "boolean"
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "CustomerInput": {
        "type": "object",
        "properties": {
          "salutation": {
            "type": "string",
            "example": "Frau"
          },
          "customerType": {
            "type": "string",
            "enum": [
              "private",
              "business"
            ]
          },
          "partyType": {
            "type": "string",
            "enum": [
              "customer",
              "prospect",
              "supplier",
              "partner",
              "other"
            ]
          },
          "customerNumber": {
            "type": "string",
            "description": "Optional. Ohne Angabe vergibt KostenKlar die nächste Nummer."
          },
          "firstName": {
            "type": "string",
            "minLength": 2
          },
          "lastName": {
            "type": "string",
            "minLength": 2
          },
          "company": {
            "type": "string"
          },
          "contactPerson": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "phone": {
            "type": "string"
          },
          "mobilePhone": {
            "type": "string"
          },
          "website": {
            "type": "string"
          },
          "street": {
            "type": "string"
          },
          "postalCode": {
            "type": "string",
            "pattern": "^\\d{5}$",
            "description": "Genau 5 Ziffern."
          },
          "city": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "taxNumber": {
            "type": "string"
          },
          "vatId": {
            "type": "string"
          },
          "leitwegId": {
            "type": "string",
            "maxLength": 46,
            "description": "Leitweg-ID für Rechnungen an öffentliche Auftraggeber (XRechnung)."
          },
          "taxMode": {
            "type": "string",
            "enum": [
              "standard",
              "reverse_charge",
              "tax_exempt"
            ]
          },
          "defaultPaymentDays": {
            "type": "number",
            "minimum": 0,
            "description": "Zahlungsziel in Tagen."
          },
          "tags": {
            "type": "array",
            "maxItems": 25,
            "items": {
              "type": "string"
            }
          }
        },
        "additionalProperties": false
      },
      "InvoiceItem": {
        "type": "object",
        "properties": {
          "position": {
            "type": [
              "integer",
              "null"
            ]
          },
          "description": {
            "type": "string"
          },
          "quantity": {
            "type": [
              "number",
              "null"
            ]
          },
          "unit": {
            "type": [
              "string",
              "null"
            ]
          },
          "unitPrice": {
            "type": [
              "number",
              "null"
            ],
            "description": "Einzelpreis in Euro, netto oder brutto je nach `priceMode`."
          },
          "discountPercent": {
            "type": [
              "number",
              "null"
            ]
          },
          "taxRate": {
            "type": [
              "number",
              "null"
            ]
          },
          "total": {
            "type": [
              "number",
              "null"
            ]
          },
          "productId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "costType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "labor",
              "material",
              null
            ],
            "description": "Kostenart nach § 35a EStG: `labor` = Arbeits-, Maschinen- und Fahrtkosten."
          }
        }
      },
      "Invoice": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "number": {
            "type": [
              "string",
              "null"
            ],
            "description": "`null` bei Entwürfen. Die Nummer vergibt KostenKlar beim Festschreiben."
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "sent",
              "paid",
              "overdue",
              "cancelled",
              "archived",
              "cancellation",
              "payment_plan",
              "partially_paid",
              "dunning_level_1",
              "dunning_level_2",
              "dunning_level_3"
            ]
          },
          "customerId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "customerName": {
            "type": [
              "string",
              "null"
            ]
          },
          "issueDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "dueDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "serviceDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "paidDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "currency": {
            "type": "string",
            "example": "EUR"
          },
          "priceMode": {
            "type": "string",
            "enum": [
              "net",
              "gross"
            ]
          },
          "subtotal": {
            "type": [
              "number",
              "null"
            ]
          },
          "taxRate": {
            "type": [
              "number",
              "null"
            ]
          },
          "taxAmount": {
            "type": [
              "number",
              "null"
            ]
          },
          "total": {
            "type": [
              "number",
              "null"
            ]
          },
          "paidAmount": {
            "type": [
              "number",
              "null"
            ]
          },
          "reverseCharge": {
            "type": "boolean"
          },
          "isCancellation": {
            "type": "boolean"
          },
          "cancelledInvoiceId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "poNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "projectReference": {
            "type": [
              "string",
              "null"
            ]
          },
          "projectId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Zugeordnetes Projekt in KostenKlar."
          },
          "paymentReference": {
            "type": [
              "string",
              "null"
            ]
          },
          "propertyService": {
            "type": "boolean",
            "description": "Leistung am Grundstück: Bei Privatkunden steht der Aufbewahrungshinweis (§ 14b Abs. 1 Satz 5 UStG) auf der Rechnung."
          },
          "ossCountry": {
            "type": [
              "string",
              "null"
            ],
            "description": "OSS-Bestimmungsland (ISO 3166-1 alpha-2). `null` = Umsatz im Inland."
          },
          "ossSupplyType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "goods",
              "digital",
              null
            ]
          },
          "laborCost": {
            "type": [
              "object",
              "null"
            ],
            "description": "Arbeits-, Maschinen- und Fahrtkosten (§ 35a EStG) der `labor`-Positionen nach Rabatt, inklusive Umsatzsteuer. `null` ohne solche Positionen. Auf dem PDF steht der Betrag nur bei Privatkunden.",
            "properties": {
              "net": {
                "type": "number"
              },
              "tax": {
                "type": "number"
              },
              "gross": {
                "type": "number"
              }
            }
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InvoiceItem"
            }
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "InvoiceItemInput": {
        "type": "object",
        "required": [
          "description",
          "quantity",
          "unitPrice"
        ],
        "additionalProperties": false,
        "properties": {
          "description": {
            "type": "string",
            "maxLength": 2000
          },
          "quantity": {
            "type": "number",
            "minimum": 0,
            "description": "Bis 3 Nachkommastellen."
          },
          "unitPrice": {
            "type": "number",
            "minimum": 0,
            "description": "Einzelpreis in Euro, netto oder brutto je nach `priceMode` der Rechnung. Bis 4 Nachkommastellen."
          },
          "unit": {
            "type": "string",
            "maxLength": 20,
            "example": "Std."
          },
          "taxRate": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "Steuersatz der Position in Prozent. Fehlt er, gilt der Satz der Rechnung."
          },
          "discountPercent": {
            "type": "number",
            "minimum": 0,
            "maximum": 100
          },
          "productId": {
            "type": "string",
            "format": "uuid",
            "description": "Verknüpfung mit einem Produkt aus `/v1/products`."
          },
          "costType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "labor",
              "material",
              null
            ],
            "description": "Kostenart nach § 35a EStG: `labor` (Arbeits-, Maschinen- und Fahrtkosten) oder `material`. Fehlt sie, gilt die Kostenart des Produkts."
          },
          "marginScheme": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "used_goods",
              "art",
              "collectibles",
              null
            ],
            "description": "Differenzbesteuerung (§ 25a UStG): Art des Gegenstands. Das Modul muss im Profil eingeschaltet sein."
          },
          "marginPurchasePrice": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "Einkaufspreis je Einheit für die Differenzbesteuerung."
          },
          "marginMethod": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "individual",
              "total",
              "flat",
              null
            ],
            "description": "`flat`: Pauschalmarge 30 % für Kunstgegenstände; sonst gilt das Profil."
          }
        }
      },
      "InvoiceInput": {
        "type": "object",
        "required": [
          "customerId",
          "issueDate",
          "dueDate",
          "items"
        ],
        "additionalProperties": false,
        "properties": {
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "Der Kunde muss bereits existieren (siehe `POST /v1/customers`)."
          },
          "issueDate": {
            "type": "string",
            "format": "date",
            "example": "2026-10-06"
          },
          "dueDate": {
            "type": "string",
            "format": "date",
            "example": "2026-10-20"
          },
          "serviceDate": {
            "type": "string",
            "format": "date"
          },
          "servicePeriodStart": {
            "type": "string",
            "format": "date"
          },
          "servicePeriodEnd": {
            "type": "string",
            "format": "date"
          },
          "taxRate": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "example": 19
          },
          "priceMode": {
            "type": "string",
            "enum": [
              "net",
              "gross"
            ],
            "default": "net"
          },
          "introText": {
            "type": "string",
            "maxLength": 5000
          },
          "customerNotes": {
            "type": "string",
            "maxLength": 5000,
            "description": "Hinweis an den Kunden, wird auf dem PDF gedruckt."
          },
          "poNumber": {
            "type": "string",
            "maxLength": 100
          },
          "projectReference": {
            "type": "string",
            "maxLength": 100
          },
          "projectId": {
            "type": "string",
            "format": "uuid",
            "description": "Optional: Projekt des Kunden, dem die Rechnung zugeordnet wird."
          },
          "paymentReference": {
            "type": "string",
            "maxLength": 140
          },
          "propertyService": {
            "type": "boolean",
            "description": "Leistung am Grundstück. Fehlt der Wert, gilt der Standard aus dem Profil; bei OSS-Rechnungen immer `false`."
          },
          "ossCountry": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "AT",
              "BE",
              "BG",
              "CY",
              "CZ",
              "DK",
              "EE",
              "GR",
              "ES",
              "FI",
              "FR",
              "HR",
              "HU",
              "IE",
              "IT",
              "LT",
              "LU",
              "LV",
              "MT",
              "NL",
              "PL",
              "PT",
              "RO",
              "SE",
              "SI",
              "SK",
              null
            ],
            "description": "OSS: Bestimmungsland bei Verkauf an EU-Privatkunden. Voraussetzungen: OSS im Profil eingeschaltet, kein Kleinunternehmer, Buchungsautomatik aus, Privatkunde. Ohne `taxRate` gilt der Normalsatz des Landes; jeder Steuersatz muss im Land gelten."
          },
          "ossSupplyType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "goods",
              "digital",
              null
            ],
            "description": "Pflicht bei `ossCountry`: `goods` (Fernverkauf, § 3c UStG) oder `digital` (§ 3a Abs. 5 UStG)."
          },
          "discountValue": {
            "type": "number",
            "minimum": 0,
            "description": "Rabatt auf den ganzen Beleg (Prozent oder Betrag je nach `discountType`)."
          },
          "discountType": {
            "type": "string",
            "enum": [
              "percent",
              "absolute"
            ]
          },
          "taxExemptionReason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "none",
              "ust_4_nr_21",
              "ust_4_nr_22",
              "ust_4_nr_14",
              "ust_4_nr_16",
              "ust_4_nr_18",
              "ust_4_nr_20",
              "ust_4_nr_23",
              "ust_4_nr_26",
              "ust_4_nr_12",
              "ust_4_nr_11",
              "ust_4_nr_8",
              "ust_4_nr_1a",
              "ust_4_nr_1b",
              "ust_3a_abs_2",
              "ust_3a_abs_2_eu",
              "custom",
              null
            ],
            "description": "Grund der Umsatzsteuerbefreiung. `none` schaltet den Standard aus dem Profil für diese Rechnung ab."
          },
          "taxExemptionText": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500,
            "description": "Eigener Hinweistext; leer = Vorschlagstext des Grundes."
          },
          "skontoPercent": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 100
          },
          "skontoDays": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 365
          },
          "items": {
            "type": "array",
            "minItems": 1,
            "maxItems": 200,
            "items": {
              "$ref": "#/components/schemas/InvoiceItemInput"
            }
          }
        }
      },
      "QuoteItem": {
        "allOf": [
          {
            "$ref": "#/components/schemas/InvoiceItem"
          },
          {
            "type": "object",
            "properties": {
              "isOptional": {
                "type": "boolean"
              },
              "optionGroup": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "optionAccepted": {
                "type": "boolean"
              }
            }
          }
        ]
      },
      "Quote": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "number": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "sent",
              "accepted",
              "rejected",
              "archived"
            ]
          },
          "customerId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "customerName": {
            "type": [
              "string",
              "null"
            ]
          },
          "issueDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "validUntil": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "currency": {
            "type": "string",
            "example": "EUR"
          },
          "subtotal": {
            "type": [
              "number",
              "null"
            ]
          },
          "taxRate": {
            "type": [
              "number",
              "null"
            ]
          },
          "taxAmount": {
            "type": [
              "number",
              "null"
            ]
          },
          "total": {
            "type": [
              "number",
              "null"
            ]
          },
          "convertedToInvoiceId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "revision": {
            "type": [
              "integer",
              "null"
            ]
          },
          "signedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "poNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "projectReference": {
            "type": [
              "string",
              "null"
            ]
          },
          "laborCost": {
            "type": [
              "object",
              "null"
            ],
            "description": "Arbeits-, Maschinen- und Fahrtkosten (§ 35a EStG) der `labor`-Positionen nach Rabatt, inklusive Umsatzsteuer. `null` ohne solche Positionen. Auf dem PDF steht der Betrag nur bei Privatkunden.",
            "properties": {
              "net": {
                "type": "number"
              },
              "tax": {
                "type": "number"
              },
              "gross": {
                "type": "number"
              }
            }
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QuoteItem"
            }
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "QuoteItemInput": {
        "type": "object",
        "required": [
          "description",
          "quantity",
          "unitPrice"
        ],
        "additionalProperties": false,
        "properties": {
          "description": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "quantity": {
            "type": "number",
            "minimum": 0,
            "description": "Bis 3 Nachkommastellen."
          },
          "unitPrice": {
            "type": "number",
            "minimum": 0,
            "description": "Einzelpreis in Euro, netto oder brutto je nach `priceMode`. Bis 4 Nachkommastellen."
          },
          "unit": {
            "type": "string",
            "maxLength": 20,
            "example": "Std."
          },
          "costType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "labor",
              "material",
              null
            ],
            "description": "Kostenart nach § 35a EStG: `labor` (Arbeits-, Maschinen- und Fahrtkosten) oder `material`."
          }
        }
      },
      "QuoteInput": {
        "type": "object",
        "required": [
          "customerId",
          "items"
        ],
        "additionalProperties": false,
        "properties": {
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "Der Kunde muss bereits existieren (siehe `POST /v1/customers`)."
          },
          "issueDate": {
            "type": "string",
            "format": "date",
            "description": "Standard: heute (Europe/Berlin)."
          },
          "validUntil": {
            "type": "string",
            "format": "date",
            "description": "Standard: `issueDate` plus 30 Tage. Darf nicht vor `issueDate` liegen."
          },
          "taxRate": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "default": 19,
            "description": "Bei Kleinunternehmern (§ 19 UStG) immer 0."
          },
          "priceMode": {
            "type": "string",
            "enum": [
              "net",
              "gross"
            ],
            "default": "net",
            "description": "`gross`: Einzelpreise sind Bruttopreise und werden in Nettopreise umgerechnet."
          },
          "projectReference": {
            "type": "string",
            "maxLength": 100
          },
          "introText": {
            "type": "string",
            "maxLength": 5000
          },
          "notes": {
            "type": "string",
            "maxLength": 5000,
            "description": "Hinweis an den Kunden, wird auf dem PDF gedruckt."
          },
          "items": {
            "type": "array",
            "minItems": 1,
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/QuoteItemInput"
            }
          },
          "discountValue": {
            "type": "number",
            "minimum": 0,
            "description": "Rabatt auf das ganze Angebot (Prozent oder Nettobetrag je nach `discountType`)."
          },
          "discountType": {
            "type": "string",
            "enum": [
              "percent",
              "absolute"
            ]
          },
          "markSent": {
            "type": "boolean",
            "default": false,
            "description": "`true`: sofort als versendet markieren, ohne E-Mail."
          }
        }
      },
      "TimeEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Tag des Beginns in Europe/Berlin."
          },
          "startTime": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "durationMinutes": {
            "type": "integer",
            "description": "Netto-Dauer ohne Pause."
          },
          "breakMinutes": {
            "type": "integer"
          },
          "billable": {
            "type": "boolean",
            "description": "Als abrechenbar markiert."
          },
          "projectId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "customerId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "taskId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "invoiced": {
            "type": "boolean",
            "description": "Bereits abgerechnet (einer Rechnung zugeordnet)."
          },
          "editable": {
            "type": "boolean",
            "description": "Offen: lässt sich über die API ändern und löschen (nicht freigegeben, abgerechnet oder gesperrt)."
          }
        }
      },
      "BusySlot": {
        "type": "object",
        "properties": {
          "start": {
            "type": "string",
            "format": "date-time"
          },
          "end": {
            "type": "string",
            "format": "date-time",
            "description": "Ausschließlich."
          },
          "allDay": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "enum": [
              "confirmed",
              "tentative"
            ]
          }
        }
      },
      "CalendarEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "location": {
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "appointment",
              "visit",
              "call",
              "deadline",
              "other"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "confirmed",
              "tentative",
              "cancelled"
            ]
          },
          "start": {
            "type": "string",
            "format": "date-time"
          },
          "end": {
            "type": "string",
            "format": "date-time",
            "description": "Ausschließlich. Bei ganztägigen Terminen 00:00 Uhr nach dem letzten Tag (Europe/Berlin)."
          },
          "allDay": {
            "type": "boolean",
            "description": "Über die API angelegte Termine sind nie ganztägig."
          },
          "linkType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "customer",
              "project",
              "quote",
              "invoice",
              "task",
              null
            ]
          },
          "linkId": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "CalendarEventInput": {
        "type": "object",
        "required": [
          "title",
          "start",
          "end"
        ],
        "additionalProperties": false,
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "description": {
            "type": "string",
            "maxLength": 4000
          },
          "location": {
            "type": "string",
            "maxLength": 300
          },
          "type": {
            "type": "string",
            "enum": [
              "appointment",
              "call",
              "visit",
              "other"
            ],
            "default": "appointment"
          },
          "status": {
            "type": "string",
            "enum": [
              "confirmed",
              "tentative"
            ],
            "default": "tentative"
          },
          "start": {
            "type": "string",
            "format": "date-time",
            "example": "2026-10-20T09:00:00+02:00"
          },
          "end": {
            "type": "string",
            "format": "date-time",
            "description": "Nach `start`, höchstens 12 Stunden später."
          },
          "linkType": {
            "type": "string",
            "enum": [
              "customer",
              "project"
            ]
          },
          "linkId": {
            "type": "string",
            "format": "uuid",
            "description": "Nur zusammen mit `linkType`."
          }
        }
      },
      "CalendarEventUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 4000
          },
          "location": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 300
          },
          "status": {
            "type": "string",
            "enum": [
              "confirmed",
              "tentative",
              "cancelled"
            ]
          },
          "start": {
            "type": "string",
            "format": "date-time"
          },
          "end": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Product": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "code": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "category": {
            "type": [
              "string",
              "null"
            ]
          },
          "unit": {
            "type": [
              "string",
              "null"
            ]
          },
          "sellingPrice": {
            "type": [
              "number",
              "null"
            ]
          },
          "taxRate": {
            "type": [
              "number",
              "null"
            ]
          },
          "ean": {
            "type": [
              "string",
              "null"
            ]
          },
          "trackStock": {
            "type": "boolean"
          },
          "stockQuantity": {
            "type": [
              "number",
              "null"
            ],
            "description": "Nur bei Produkten mit Lagerführung, sonst `null`."
          },
          "costType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "labor",
              "material",
              null
            ],
            "description": "Kostenart nach § 35a EStG; Positionen ohne eigene Kostenart übernehmen sie."
          },
          "isActive": {
            "type": "boolean"
          }
        }
      },
      "Project": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "customerId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "completed",
              "archived"
            ]
          },
          "startDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "endDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "budgetAmount": {
            "type": [
              "number",
              "null"
            ],
            "description": "Budget bzw. Auftragswert, netto in Euro."
          },
          "budgetHours": {
            "type": [
              "number",
              "null"
            ],
            "description": "Stundenkontingent."
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "WorkflowTrigger": {
        "type": "object",
        "description": "Eine eigene Workflow-Regel mit der Aktion \"Webhook auslösen\" hat gegriffen.",
        "required": [
          "entityId",
          "rule",
          "event",
          "number",
          "customerId",
          "amount",
          "message"
        ],
        "properties": {
          "entityId": {
            "type": "string",
            "description": "Kennung des auslösenden Belegs, z. B. Rechnung, Angebot oder Projekt. Bei Mängeln Protokoll und Mangel, durch Doppelpunkt getrennt."
          },
          "rule": {
            "type": "string",
            "description": "Name der Regel."
          },
          "event": {
            "type": "string",
            "description": "Ereignis der Regel.",
            "enum": [
              "quote_sent",
              "quote_accepted",
              "quote_expiring",
              "invoice_sent",
              "invoice_overdue",
              "invoice_paid",
              "expense_created",
              "receipt_received",
              "project_budget_reached",
              "contract_ending",
              "delivery_note_issued",
              "defect_overdue"
            ]
          },
          "number": {
            "type": "string",
            "description": "Nummer oder Bezeichnung des Belegs."
          },
          "customerId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "amount": {
            "type": [
              "number",
              "null"
            ],
            "description": "Betrag des Belegs (brutto), falls vorhanden."
          },
          "message": {
            "type": "string",
            "description": "Text aus der Regel, Platzhalter ersetzt."
          }
        }
      },
      "DeletedProject": {
        "type": "object",
        "description": "Eckdaten eines gelöschten Projekts (Stand vor dem Löschen). Das Projekt selbst lässt sich danach nicht mehr abrufen.",
        "required": [
          "id",
          "customerId",
          "name",
          "status"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "customerId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "completed",
              "archived"
            ]
          }
        }
      },
      "ProjectInput": {
        "type": "object",
        "required": [
          "customerId",
          "name"
        ],
        "additionalProperties": false,
        "properties": {
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "Der Kunde muss bereits existieren (siehe `POST /v1/customers`)."
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 150
          },
          "startDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "endDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Darf nicht vor `startDate` liegen."
          },
          "budgetAmount": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "Netto in Euro, bis 2 Nachkommastellen."
          },
          "budgetHours": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "Stundenkontingent, bis 2 Nachkommastellen."
          }
        }
      },
      "ProjectUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 150
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "completed",
              "archived"
            ]
          },
          "startDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "endDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Darf nicht vor `startDate` liegen."
          },
          "budgetAmount": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "Netto in Euro, bis 2 Nachkommastellen."
          },
          "budgetHours": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "Stundenkontingent, bis 2 Nachkommastellen."
          }
        }
      },
      "Milestone": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "projectId": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "position": {
            "type": [
              "integer",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "done",
              "invoiced"
            ]
          },
          "dueDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "amountNet": {
            "type": [
              "number",
              "null"
            ],
            "description": "Betrag netto in Euro."
          },
          "completedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "invoiceId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Rechnung, mit der der Meilenstein abgerechnet wurde."
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ChecklistItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "done": {
            "type": "boolean"
          },
          "position": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "ChecklistItemInput": {
        "type": "object",
        "required": [
          "title"
        ],
        "additionalProperties": false,
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          }
        }
      },
      "ChecklistItemUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "done": {
            "type": "boolean"
          }
        }
      },
      "Task": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "in_progress",
              "waiting",
              "done"
            ],
            "description": "`done` genau dann, wenn `completedAt` gesetzt ist."
          },
          "priority": {
            "type": "string",
            "enum": [
              "low",
              "normal",
              "high",
              "urgent"
            ]
          },
          "customerId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "projectId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "assigneeId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Zuständige Person (Kontoinhaber oder Teammitglied)."
          },
          "startDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "dueDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "estimatedMinutes": {
            "type": [
              "number",
              "null"
            ]
          },
          "recurrence": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "daily",
              "weekly",
              "monthly",
              "yearly",
              null
            ]
          },
          "completedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "checklist": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChecklistItem"
            }
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "CreditNote": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "number": {
            "type": [
              "string",
              "null"
            ],
            "description": "Gutschriftsnummer, z. B. `GS-2026-0001`."
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "issued",
              "applied",
              "refunded",
              "cancelled"
            ]
          },
          "type": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "customer_correction",
              "settlement_reverse_charge",
              null
            ],
            "description": "`customer_correction` (Rechnungskorrektur) oder `settlement_reverse_charge` (Gutschrift im Gutschriftverfahren)."
          },
          "customerId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "customerName": {
            "type": [
              "string",
              "null"
            ]
          },
          "referenceInvoiceId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Korrigierte Rechnung, falls verknüpft."
          },
          "referenceInvoiceNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "issueDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "currency": {
            "type": "string",
            "example": "EUR"
          },
          "subtotal": {
            "type": [
              "number",
              "null"
            ]
          },
          "taxRate": {
            "type": [
              "number",
              "null"
            ]
          },
          "taxAmount": {
            "type": [
              "number",
              "null"
            ]
          },
          "total": {
            "type": [
              "number",
              "null"
            ]
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Grund der Gutschrift, steht auch auf dem PDF."
          },
          "isCancellation": {
            "type": "boolean",
            "description": "`true`: Storno zu einer Gutschrift (eigener Nummernkreis, z. B. `SG-2026-0001`, Beträge negativ)."
          },
          "cancelledCreditNoteId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Beim Storno-Beleg: die stornierte Gutschrift."
          },
          "cancelledByCreditNoteId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Bei einer stornierten Gutschrift: ihr Storno-Beleg."
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InvoiceItem"
            },
            "description": "Gleiche Form wie Rechnungspositionen; `taxRate`, `productId` und `costType` sind bei Gutschriften immer `null`."
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ContractItem": {
        "type": "object",
        "properties": {
          "position": {
            "type": [
              "integer",
              "null"
            ]
          },
          "description": {
            "type": "string"
          },
          "quantity": {
            "type": [
              "number",
              "null"
            ]
          },
          "unit": {
            "type": [
              "string",
              "null"
            ]
          },
          "unitPrice": {
            "type": [
              "number",
              "null"
            ],
            "description": "Vereinbarter Einzelpreis in Euro, **brutto**."
          },
          "taxRate": {
            "type": [
              "number",
              "null"
            ],
            "description": "`null` = Standardsteuersatz des Kontos."
          },
          "total": {
            "type": [
              "number",
              "null"
            ],
            "description": "Menge mal Einzelpreis, brutto."
          },
          "isOneTime": {
            "type": "boolean",
            "description": "Einmalige Position (z. B. Einrichtung), zählt nicht zum wiederkehrenden Betrag."
          }
        }
      },
      "Contract": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "number": {
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "subject": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "sent",
              "signed",
              "active",
              "expired",
              "cancelled"
            ],
            "description": "Alle Status werden geliefert, auch `draft`."
          },
          "customerId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "customerName": {
            "type": [
              "string",
              "null"
            ]
          },
          "amount": {
            "type": [
              "number",
              "null"
            ],
            "description": "Vergütung pro Abrechnungsintervall in Euro, **brutto** (Summe der wiederkehrenden Positionen)."
          },
          "billingInterval": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "monthly",
              "quarterly",
              "yearly",
              "one_time",
              null
            ]
          },
          "startDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "endDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "`null` bei unbefristeten Verträgen."
          },
          "nextInvoiceDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "noticePeriodDays": {
            "type": [
              "number",
              "null"
            ],
            "description": "Kündigungsfrist in Tagen."
          },
          "autoRenew": {
            "type": "boolean"
          },
          "renewalPeriodMonths": {
            "type": [
              "number",
              "null"
            ]
          },
          "signedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Zeitpunkt der Unterschrift des Kunden, sonst `null`."
          },
          "plannedMinutesPerPeriod": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Inklusivzeit pro Abrechnungszeitraum in Minuten. `null`, wenn der Vertrag nicht zeitbasiert ist."
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ContractItem"
            }
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ContractUsage": {
        "type": "object",
        "properties": {
          "contractId": {
            "type": "string",
            "format": "uuid"
          },
          "periodStart": {
            "type": "string",
            "format": "date",
            "description": "Beginn des aktuellen Abrechnungszeitraums."
          },
          "periodEnd": {
            "type": "string",
            "format": "date",
            "description": "Ende des Zeitraums (Datum der nächsten Rechnung bzw. Vertragsende)."
          },
          "plannedMinutes": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Inklusivzeit im Zeitraum in Minuten, `null` ohne hinterlegte Inklusivzeit."
          },
          "usedMinutes": {
            "type": "integer",
            "description": "Im Zeitraum für den Kunden gebuchte Minuten."
          },
          "billableMinutes": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Derzeit immer `null`: Vertragszeiten werden nicht nach abrechenbar/nicht abrechenbar unterschieden. Reserviert für später."
          }
        }
      },
      "Acceptance": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "projectId": {
            "type": "string",
            "format": "uuid"
          },
          "milestoneId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Abgenommener Meilenstein, falls verknüpft."
          },
          "title": {
            "type": "string"
          },
          "protocolDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "place": {
            "type": [
              "string",
              "null"
            ]
          },
          "result": {
            "type": "string",
            "enum": [
              "accepted",
              "accepted_with_defects",
              "rejected"
            ]
          },
          "defects": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "description": {
                  "type": "string"
                },
                "deadline": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date",
                  "description": "Frist zur Beseitigung."
                },
                "fixed": {
                  "type": "boolean"
                }
              }
            }
          },
          "clientSignerName": {
            "type": [
              "string",
              "null"
            ]
          },
          "clientSignedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "contractorSignerName": {
            "type": [
              "string",
              "null"
            ]
          },
          "contractorSignedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ProjectSummary": {
        "type": "object",
        "properties": {
          "projectId": {
            "type": "string",
            "format": "uuid"
          },
          "budgetAmount": {
            "type": [
              "number",
              "null"
            ],
            "description": "Budget bzw. Auftragswert, netto in Euro."
          },
          "budgetHours": {
            "type": [
              "number",
              "null"
            ],
            "description": "Stundenkontingent."
          },
          "trackedMinutes": {
            "type": "integer",
            "description": "Alle erfassten Minuten des Projekts."
          },
          "billableMinutes": {
            "type": "integer",
            "description": "Davon als abrechenbar markiert."
          },
          "invoicedNet": {
            "type": [
              "number",
              "null"
            ],
            "description": "Netto-Summe der ausgestellten Rechnungen zum Projekt (ohne Entwürfe)."
          },
          "milestones": {
            "type": "object",
            "properties": {
              "count": {
                "type": "integer"
              },
              "planned": {
                "type": "number",
                "description": "Summe aller Meilensteine, netto."
              },
              "invoiced": {
                "type": "number",
                "description": "Davon abgerechnet."
              },
              "open": {
                "type": "number",
                "description": "Noch nicht abgerechnet."
              }
            }
          },
          "tasks": {
            "type": "object",
            "properties": {
              "open": {
                "type": "integer"
              },
              "done": {
                "type": "integer"
              },
              "overdue": {
                "type": "integer",
                "description": "Offen und Fälligkeit überschritten."
              }
            }
          }
        }
      },
      "DeliveryNote": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "number": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "issued",
              "cancelled"
            ],
            "description": "`issued` = ausgeliefert: Bestand gebucht, Inhalt gesperrt."
          },
          "customerId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "customerName": {
            "type": [
              "string",
              "null"
            ]
          },
          "deliveryDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "poNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "shippingCarrier": {
            "type": [
              "string",
              "null"
            ]
          },
          "trackingNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "invoiceId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "invoiceNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "position": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "description": {
                  "type": "string"
                },
                "quantity": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "unit": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "productId": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "uuid"
                },
                "articleNumber": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "Dunning": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "invoiceId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "invoiceNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "customerId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "level": {
            "type": "string",
            "enum": [
              "first",
              "second",
              "final",
              "collections"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "sent",
              "paid",
              "cancelled",
              "escalated"
            ]
          },
          "originalAmount": {
            "type": [
              "number",
              "null"
            ],
            "description": "Offener Rechnungsbetrag bei Erstellung der Mahnung."
          },
          "feeAmount": {
            "type": [
              "number",
              "null"
            ],
            "description": "Mahngebühr."
          },
          "flatFee": {
            "type": [
              "number",
              "null"
            ],
            "description": "Verzugspauschale nach § 288 Abs. 5 BGB (nur Unternehmer)."
          },
          "lateFees": {
            "type": [
              "number",
              "null"
            ],
            "description": "Verzugszinsen."
          },
          "totalDue": {
            "type": [
              "number",
              "null"
            ]
          },
          "dueDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "sentAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "Payment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "direction": {
            "type": "string",
            "enum": [
              "in",
              "out"
            ]
          },
          "paymentDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "amount": {
            "type": [
              "number",
              "null"
            ]
          },
          "counterparty": {
            "type": [
              "string",
              "null"
            ]
          },
          "reference": {
            "type": [
              "string",
              "null"
            ]
          },
          "note": {
            "type": [
              "string",
              "null"
            ]
          },
          "allocations": {
            "type": "array",
            "description": "Aufteilung auf Rechnungen bzw. Gutschriften und Steuersätze.",
            "items": {
              "type": "object",
              "properties": {
                "invoiceId": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "uuid"
                },
                "invoiceNumber": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "creditNoteId": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "uuid"
                },
                "creditNoteNumber": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "grossAmount": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "netAmount": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "taxAmount": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "taxRate": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "vatCategory": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Steuerliche Einordnung, z. B. `standard_19`, `reduced_7`, `reverse_charge_13b`, `oss_eu`."
                }
              }
            }
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "Expense": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "enum": [
              "expense",
              "income"
            ]
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "amount": {
            "type": [
              "number",
              "null"
            ],
            "description": "Bruttobetrag in Euro."
          },
          "taxRate": {
            "type": [
              "number",
              "null"
            ]
          },
          "date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Zahldatum, maßgeblich für die EÜR."
          },
          "invoiceDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "category": {
            "type": "string",
            "enum": [
              "office",
              "travel",
              "equipment",
              "software",
              "marketing",
              "training",
              "utilities",
              "rent",
              "insurance",
              "taxes",
              "salary",
              "meals",
              "entertainment",
              "other"
            ]
          },
          "paymentMethod": {
            "type": "string",
            "enum": [
              "cash",
              "bank_transfer",
              "credit_card",
              "debit_card",
              "paypal",
              "other"
            ]
          },
          "vendor": {
            "type": [
              "string",
              "null"
            ]
          },
          "receiptNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "taxDeductible": {
            "type": "boolean"
          },
          "reverseCharge13b": {
            "type": "boolean"
          },
          "reverseCharge13bType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "foreign",
              "construction",
              "domestic",
              null
            ]
          },
          "kskRelevant": {
            "type": "boolean",
            "description": "Honorar an Künstler oder Publizisten (Künstlersozialabgabe)."
          },
          "isPrivate": {
            "type": "boolean"
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "receipt": {
            "type": [
              "object",
              "null"
            ],
            "description": "Hochgeladener Beleg (ohne Inhalt).",
            "properties": {
              "fileName": {
                "type": "string"
              },
              "mimeType": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "CreditNoteItemInput": {
        "type": "object",
        "required": [
          "description",
          "quantity",
          "unitPrice"
        ],
        "additionalProperties": false,
        "properties": {
          "description": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "quantity": {
            "type": "number",
            "minimum": 0.001
          },
          "unitPrice": {
            "type": "number",
            "minimum": 0,
            "description": "Einzelpreis netto in Euro, positiv."
          },
          "unit": {
            "type": "string",
            "maxLength": 20
          },
          "discountPercent": {
            "type": "number",
            "minimum": 0,
            "maximum": 100
          }
        }
      },
      "CreditNoteInput": {
        "type": "object",
        "required": [
          "referenceInvoiceId",
          "reason",
          "items"
        ],
        "additionalProperties": false,
        "properties": {
          "referenceInvoiceId": {
            "type": "string",
            "format": "uuid",
            "description": "Die korrigierte Rechnung (ausgestellt, nicht storniert)."
          },
          "issueDate": {
            "type": "string",
            "format": "date"
          },
          "reason": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000,
            "description": "Grund der Gutschrift, steht auf dem PDF."
          },
          "introText": {
            "type": "string",
            "maxLength": 5000
          },
          "taxRate": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "default": 19,
            "description": "Bei Kleinunternehmern immer 0."
          },
          "items": {
            "type": "array",
            "minItems": 1,
            "maxItems": 200,
            "items": {
              "$ref": "#/components/schemas/CreditNoteItemInput"
            }
          }
        }
      },
      "ProductInput": {
        "type": "object",
        "required": [
          "code",
          "name",
          "sellingPrice"
        ],
        "additionalProperties": false,
        "properties": {
          "code": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "description": "Je Konto eindeutig."
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "type": {
            "type": "string",
            "enum": [
              "service",
              "material",
              "flat"
            ],
            "default": "service"
          },
          "category": {
            "type": "string",
            "maxLength": 100,
            "default": "Allgemein"
          },
          "description": {
            "type": "string",
            "maxLength": 5000
          },
          "unit": {
            "type": "string",
            "maxLength": 20,
            "description": "Standard: `Std.` (Leistung), `Stk.` (Material), `pauschal`."
          },
          "sellingPrice": {
            "type": "number",
            "minimum": 0,
            "description": "Verkaufspreis netto in Euro."
          },
          "purchasePrice": {
            "type": "number",
            "minimum": 0,
            "description": "Einkaufspreis netto (Material, Standard 0)."
          },
          "taxRate": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "default": 19
          },
          "ean": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 32
          },
          "costType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "labor",
              "material",
              null
            ],
            "description": "Kostenart nach § 35a EStG: `labor` (Arbeits-, Maschinen- und Fahrtkosten) oder `material`. Fehlt sie, gilt die Kostenart des Produkts."
          },
          "plannedMinutesPerUnit": {
            "type": "integer",
            "minimum": 1,
            "description": "Leistungen: geplante Minuten je Einheit, Standard 60."
          },
          "trackStock": {
            "type": "boolean",
            "description": "Bestand führen."
          },
          "stockQuantity": {
            "type": "number",
            "minimum": 0,
            "description": "Anfangsbestand (nur beim Anlegen mit `trackStock`)."
          },
          "minStock": {
            "type": "number",
            "minimum": 0,
            "description": "Meldebestand."
          }
        }
      },
      "StockMovementInput": {
        "type": "object",
        "required": [
          "type",
          "quantity"
        ],
        "additionalProperties": false,
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "in",
              "out",
              "correction"
            ]
          },
          "quantity": {
            "type": "number",
            "minimum": 0,
            "description": "Menge; bei `correction` der gezählte Bestand."
          },
          "date": {
            "type": "string",
            "format": "date",
            "description": "Standard: heute."
          },
          "note": {
            "type": "string",
            "maxLength": 255
          }
        }
      },
      "TimeEntryInput": {
        "type": "object",
        "required": [
          "title",
          "startTime",
          "durationMinutes"
        ],
        "additionalProperties": false,
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "description": {
            "type": "string",
            "maxLength": 5000
          },
          "startTime": {
            "type": "string",
            "format": "date-time",
            "description": "Beginn mit Uhrzeit, nicht in der Zukunft."
          },
          "durationMinutes": {
            "type": "integer",
            "minimum": 1,
            "maximum": 960,
            "description": "Netto-Dauer ohne Pause."
          },
          "breakMinutes": {
            "type": "integer",
            "minimum": 0,
            "maximum": 720
          },
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "Ohne Angabe gilt der Kunde des Projekts."
          },
          "projectId": {
            "type": "string",
            "format": "uuid"
          },
          "taskId": {
            "type": "string",
            "format": "uuid"
          },
          "productId": {
            "type": "string",
            "format": "uuid",
            "description": "Leistung aus `/v1/products`."
          },
          "billable": {
            "type": "boolean",
            "default": true
          }
        }
      },
      "PaymentInput": {
        "type": "object",
        "required": [
          "amount"
        ],
        "additionalProperties": false,
        "properties": {
          "amount": {
            "type": "number",
            "minimum": 0.01,
            "description": "Betrag in Euro, höchstens der offene Betrag."
          },
          "date": {
            "type": "string",
            "format": "date",
            "description": "Zahldatum, Standard heute."
          },
          "method": {
            "type": "string",
            "enum": [
              "bank_transfer",
              "cash",
              "card",
              "paypal",
              "other"
            ]
          },
          "note": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "ExpenseInput": {
        "type": "object",
        "required": [
          "title",
          "amount",
          "date",
          "category",
          "paymentMethod"
        ],
        "additionalProperties": false,
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "expense",
              "income"
            ],
            "default": "expense"
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "description": {
            "type": "string",
            "maxLength": 5000
          },
          "amount": {
            "type": "number",
            "minimum": 0.01,
            "description": "Bruttobetrag in Euro."
          },
          "taxRate": {
            "type": "number",
            "minimum": 0,
            "maximum": 100
          },
          "date": {
            "type": "string",
            "format": "date",
            "description": "Zahldatum."
          },
          "invoiceDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "category": {
            "type": "string",
            "enum": [
              "office",
              "travel",
              "equipment",
              "software",
              "marketing",
              "training",
              "utilities",
              "rent",
              "insurance",
              "taxes",
              "salary",
              "meals",
              "entertainment",
              "other"
            ]
          },
          "paymentMethod": {
            "type": "string",
            "enum": [
              "cash",
              "bank_transfer",
              "credit_card",
              "debit_card",
              "paypal",
              "other"
            ]
          },
          "vendor": {
            "type": "string",
            "maxLength": 255
          },
          "receiptNumber": {
            "type": "string",
            "maxLength": 100
          },
          "taxDeductible": {
            "type": "boolean"
          },
          "reverseCharge13b": {
            "type": "boolean",
            "description": "Steuerschuldnerschaft nach § 13b UStG (nur mit Steuersatz)."
          },
          "reverseCharge13bType": {
            "type": "string",
            "enum": [
              "foreign",
              "construction",
              "domestic"
            ]
          },
          "kskRelevant": {
            "type": "boolean"
          },
          "isPrivate": {
            "type": "boolean",
            "description": "Privat: ohne Steuer, nicht abziehbar."
          },
          "notes": {
            "type": "string",
            "maxLength": 5000
          }
        }
      },
      "TaskInput": {
        "type": "object",
        "required": [
          "title"
        ],
        "additionalProperties": false,
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "notes": {
            "type": "string",
            "maxLength": 5000
          },
          "customerId": {
            "type": "string",
            "format": "uuid"
          },
          "projectId": {
            "type": "string",
            "format": "uuid"
          },
          "assigneeId": {
            "type": "string",
            "format": "uuid",
            "description": "Kontoinhaber oder aktives Teammitglied."
          },
          "priority": {
            "type": "string",
            "enum": [
              "low",
              "normal",
              "high",
              "urgent"
            ],
            "default": "normal"
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "in_progress",
              "waiting"
            ],
            "default": "open"
          },
          "startDate": {
            "type": "string",
            "format": "date"
          },
          "dueDate": {
            "type": "string",
            "format": "date"
          },
          "estimatedMinutes": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100000
          },
          "recurrence": {
            "type": "string",
            "enum": [
              "daily",
              "weekly",
              "monthly",
              "yearly"
            ],
            "description": "Beim Erledigen entsteht die nächste Aufgabe der Serie."
          }
        }
      },
      "TaskUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "notes": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 5000
          },
          "customerId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "projectId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "assigneeId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "priority": {
            "type": "string",
            "enum": [
              "low",
              "normal",
              "high",
              "urgent"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "in_progress",
              "waiting",
              "done"
            ]
          },
          "startDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "dueDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "estimatedMinutes": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 100000
          },
          "recurrence": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "daily",
              "weekly",
              "monthly",
              "yearly",
              null
            ]
          }
        }
      },
      "InvoiceUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "Der Kunde muss bereits existieren (siehe `POST /v1/customers`)."
          },
          "issueDate": {
            "type": "string",
            "format": "date",
            "example": "2026-10-06"
          },
          "dueDate": {
            "type": "string",
            "format": "date",
            "example": "2026-10-20"
          },
          "serviceDate": {
            "type": "string",
            "format": "date"
          },
          "servicePeriodStart": {
            "type": "string",
            "format": "date"
          },
          "servicePeriodEnd": {
            "type": "string",
            "format": "date"
          },
          "taxRate": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "example": 19
          },
          "priceMode": {
            "type": "string",
            "enum": [
              "net",
              "gross"
            ],
            "default": "net"
          },
          "introText": {
            "type": "string",
            "maxLength": 5000
          },
          "customerNotes": {
            "type": "string",
            "maxLength": 5000,
            "description": "Hinweis an den Kunden, wird auf dem PDF gedruckt."
          },
          "poNumber": {
            "type": "string",
            "maxLength": 100
          },
          "projectReference": {
            "type": "string",
            "maxLength": 100
          },
          "projectId": {
            "type": "string",
            "format": "uuid",
            "description": "Optional: Projekt des Kunden, dem die Rechnung zugeordnet wird."
          },
          "paymentReference": {
            "type": "string",
            "maxLength": 140
          },
          "propertyService": {
            "type": "boolean",
            "description": "Leistung am Grundstück. Fehlt der Wert, gilt der Standard aus dem Profil; bei OSS-Rechnungen immer `false`."
          },
          "ossCountry": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "AT",
              "BE",
              "BG",
              "CY",
              "CZ",
              "DK",
              "EE",
              "GR",
              "ES",
              "FI",
              "FR",
              "HR",
              "HU",
              "IE",
              "IT",
              "LT",
              "LU",
              "LV",
              "MT",
              "NL",
              "PL",
              "PT",
              "RO",
              "SE",
              "SI",
              "SK",
              null
            ],
            "description": "OSS: Bestimmungsland bei Verkauf an EU-Privatkunden. Voraussetzungen: OSS im Profil eingeschaltet, kein Kleinunternehmer, Buchungsautomatik aus, Privatkunde. Ohne `taxRate` gilt der Normalsatz des Landes; jeder Steuersatz muss im Land gelten."
          },
          "ossSupplyType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "goods",
              "digital",
              null
            ],
            "description": "Pflicht bei `ossCountry`: `goods` (Fernverkauf, § 3c UStG) oder `digital` (§ 3a Abs. 5 UStG)."
          },
          "discountValue": {
            "type": "number",
            "minimum": 0,
            "description": "Rabatt auf den ganzen Beleg (Prozent oder Betrag je nach `discountType`)."
          },
          "discountType": {
            "type": "string",
            "enum": [
              "percent",
              "absolute"
            ]
          },
          "taxExemptionReason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "none",
              "ust_4_nr_21",
              "ust_4_nr_22",
              "ust_4_nr_14",
              "ust_4_nr_16",
              "ust_4_nr_18",
              "ust_4_nr_20",
              "ust_4_nr_23",
              "ust_4_nr_26",
              "ust_4_nr_12",
              "ust_4_nr_11",
              "ust_4_nr_8",
              "ust_4_nr_1a",
              "ust_4_nr_1b",
              "ust_3a_abs_2",
              "ust_3a_abs_2_eu",
              "custom",
              null
            ],
            "description": "Grund der Umsatzsteuerbefreiung. `none` schaltet den Standard aus dem Profil für diese Rechnung ab."
          },
          "taxExemptionText": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500,
            "description": "Eigener Hinweistext; leer = Vorschlagstext des Grundes."
          },
          "skontoPercent": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 100
          },
          "skontoDays": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 365
          },
          "items": {
            "type": "array",
            "minItems": 1,
            "maxItems": 200,
            "items": {
              "$ref": "#/components/schemas/InvoiceItemInput"
            }
          }
        }
      },
      "QuoteUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "Der Kunde muss bereits existieren (siehe `POST /v1/customers`)."
          },
          "issueDate": {
            "type": "string",
            "format": "date",
            "description": "Standard: heute (Europe/Berlin)."
          },
          "validUntil": {
            "type": "string",
            "format": "date",
            "description": "Standard: `issueDate` plus 30 Tage. Darf nicht vor `issueDate` liegen."
          },
          "taxRate": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "default": 19,
            "description": "Bei Kleinunternehmern (§ 19 UStG) immer 0."
          },
          "priceMode": {
            "type": "string",
            "enum": [
              "net",
              "gross"
            ],
            "default": "net",
            "description": "`gross`: Einzelpreise sind Bruttopreise und werden in Nettopreise umgerechnet."
          },
          "projectReference": {
            "type": "string",
            "maxLength": 100
          },
          "introText": {
            "type": "string",
            "maxLength": 5000
          },
          "notes": {
            "type": "string",
            "maxLength": 5000,
            "description": "Hinweis an den Kunden, wird auf dem PDF gedruckt."
          },
          "items": {
            "type": "array",
            "minItems": 1,
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/QuoteItemInput"
            }
          },
          "discountValue": {
            "type": "number",
            "minimum": 0,
            "description": "Rabatt auf das ganze Angebot (Prozent oder Nettobetrag je nach `discountType`)."
          },
          "discountType": {
            "type": "string",
            "enum": [
              "percent",
              "absolute"
            ]
          }
        }
      },
      "ProductUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "code": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "description": "Je Konto eindeutig."
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "type": {
            "type": "string",
            "enum": [
              "service",
              "material",
              "flat"
            ],
            "default": "service"
          },
          "category": {
            "type": "string",
            "maxLength": 100,
            "default": "Allgemein"
          },
          "description": {
            "type": "string",
            "maxLength": 5000
          },
          "unit": {
            "type": "string",
            "maxLength": 20,
            "description": "Standard: `Std.` (Leistung), `Stk.` (Material), `pauschal`."
          },
          "sellingPrice": {
            "type": "number",
            "minimum": 0,
            "description": "Verkaufspreis netto in Euro."
          },
          "purchasePrice": {
            "type": "number",
            "minimum": 0,
            "description": "Einkaufspreis netto (Material, Standard 0)."
          },
          "taxRate": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "default": 19
          },
          "ean": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 32
          },
          "costType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "labor",
              "material",
              null
            ],
            "description": "Kostenart nach § 35a EStG: `labor` (Arbeits-, Maschinen- und Fahrtkosten) oder `material`. Fehlt sie, gilt die Kostenart des Produkts."
          },
          "plannedMinutesPerUnit": {
            "type": "integer",
            "minimum": 1,
            "description": "Leistungen: geplante Minuten je Einheit, Standard 60."
          },
          "trackStock": {
            "type": "boolean",
            "description": "Bestand führen."
          },
          "minStock": {
            "type": "number",
            "minimum": 0,
            "description": "Meldebestand."
          },
          "isActive": {
            "type": "boolean"
          }
        }
      },
      "TimeEntryUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "description": {
            "type": "string",
            "maxLength": 5000
          },
          "startTime": {
            "type": "string",
            "format": "date-time",
            "description": "Beginn mit Uhrzeit, nicht in der Zukunft."
          },
          "durationMinutes": {
            "type": "integer",
            "minimum": 1,
            "maximum": 960,
            "description": "Netto-Dauer ohne Pause."
          },
          "breakMinutes": {
            "type": "integer",
            "minimum": 0,
            "maximum": 720
          },
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "Ohne Angabe gilt der Kunde des Projekts."
          },
          "projectId": {
            "type": "string",
            "format": "uuid"
          },
          "taskId": {
            "type": "string",
            "format": "uuid"
          },
          "productId": {
            "type": "string",
            "format": "uuid",
            "description": "Leistung aus `/v1/products`."
          },
          "billable": {
            "type": "boolean",
            "default": true
          }
        }
      },
      "ExpenseUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "expense",
              "income"
            ],
            "default": "expense"
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "description": {
            "type": "string",
            "maxLength": 5000
          },
          "amount": {
            "type": "number",
            "minimum": 0.01,
            "description": "Bruttobetrag in Euro."
          },
          "taxRate": {
            "type": "number",
            "minimum": 0,
            "maximum": 100
          },
          "date": {
            "type": "string",
            "format": "date",
            "description": "Zahldatum."
          },
          "invoiceDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "category": {
            "type": "string",
            "enum": [
              "office",
              "travel",
              "equipment",
              "software",
              "marketing",
              "training",
              "utilities",
              "rent",
              "insurance",
              "taxes",
              "salary",
              "meals",
              "entertainment",
              "other"
            ]
          },
          "paymentMethod": {
            "type": "string",
            "enum": [
              "cash",
              "bank_transfer",
              "credit_card",
              "debit_card",
              "paypal",
              "other"
            ]
          },
          "vendor": {
            "type": "string",
            "maxLength": 255
          },
          "receiptNumber": {
            "type": "string",
            "maxLength": 100
          },
          "taxDeductible": {
            "type": "boolean"
          },
          "reverseCharge13b": {
            "type": "boolean",
            "description": "Steuerschuldnerschaft nach § 13b UStG (nur mit Steuersatz)."
          },
          "reverseCharge13bType": {
            "type": "string",
            "enum": [
              "foreign",
              "construction",
              "domestic"
            ]
          },
          "kskRelevant": {
            "type": "boolean"
          },
          "isPrivate": {
            "type": "boolean",
            "description": "Privat: ohne Steuer, nicht abziehbar."
          },
          "notes": {
            "type": "string",
            "maxLength": 5000
          }
        }
      }
    }
  }
}
