{
  "openapi": "3.0.3",
  "info": {
    "title": "datenmaske.de API",
    "version": "1.0.0",
    "description": "REST API zur programmatischen PDF-Schwärzung. KI-gestützte Erkennung personenbezogener Daten (PII) mit Human-in-the-Loop-Review: Dokumente hochladen, PII-Vorschläge prüfen, Schwärzung anwenden und exportieren.\n\n## Authentifizierung\n\nAlle Endpunkte (ausgenommen `GET /v1`) erfordern einen API-Schlüssel im `Authorization`-Header als Bearer-Token:\n\n```\nAuthorization: Bearer dm_live_<schluessel>\n```\n\nAPI-Schlüssel haben das Präfix `dm_live_` (Live) bzw. `dm_test_` (Test) und können in den Kontoeinstellungen unter `/settings/api` erstellt werden. Maximal 5 aktive Schlüssel pro Konto. API-Zugang ab dem Solo-Tarif (29€/Monat).\n\n## Rate-Limiting & Kontingente\n\nAntworten enthalten `X-RateLimit-Limit`, `X-RateLimit-Remaining` und `X-RateLimit-Reset` (Unix-Sekunden) Header. Bei Überschreitung des Minuten-Limits wird HTTP 429 mit `Retry-After`-Header zurückgegeben. Überschreitung des Monatskontingents liefert HTTP 403.\n\n|x-rate-limit|Anfragen/Min|Dokumente/Monat|Max. Seiten|\n|---|---|---|---|\n|Starter|Kein API-Zugang|—|—|\n|Solo|15|50|Unbegrenzt|\n|Professional|30|150|Unbegrenzt|\n|Business|120|500 inklusive|Unbegrenzt|",
    "contact": {
      "name": "Datenmaske",
      "url": "https://datenmaske.de"
    }
  },
  "servers": [
    {
      "url": "https://datenmaske.de/api/v1",
      "description": "Produktion"
    }
  ],
  "tags": [
    { "name": "Allgemein", "description": "API-Status und Metadaten" },
    { "name": "Dokumente", "description": "PDF-Upload, -Auflistung, -Abruf und -Löschung" },
    { "name": "Vorschläge", "description": "PII-Schwärzungsvorschläge erkennen und reviewen" },
    { "name": "Schwärzung", "description": "Schwärzung anwenden und PDF exportieren" },
    { "name": "Bericht", "description": "JSON-Schwärzungsbericht" },
    { "name": "Konto", "description": "Kontoinformationen und Nutzung" },
    { "name": "Audit", "description": "Audit-Protokolle (nur Business-Tarif)" }
  ],
  "security": [
    { "bearerAuth": [] }
  ],
  "paths": {
    "/": {
      "get": {
        "tags": ["Allgemein"],
        "summary": "API-Status",
        "description": "API-Status und verfügbare Endpunkte abrufen.",
        "security": [],
        "responses": {
          "200": {
            "description": "API-Status",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ApiStatus" }
              }
            }
          }
        }
      }
    },
    "/documents": {
      "post": {
        "tags": ["Dokumente"],
        "summary": "PDF hochladen und verarbeiten",
        "description": "PDF hochladen und verarbeiten. Das Dokument wird analysiert, PII erkannt und Schwärzungsvorschläge erstellt. Zählt gegen das monatliche Dokumentkontingent.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": ["file"],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "PDF-Datei (max. 50 MB)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Dokument erfolgreich verarbeitet",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/X-RateLimit-Limit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/X-RateLimit-Remaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/X-RateLimit-Reset" }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/DocumentUploadResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      },
      "get": {
        "tags": ["Dokumente"],
        "summary": "Dokumente auflisten",
        "description": "Alle Dokumente des Benutzers auflisten.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "minimum": 1, "default": 1 }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 }
          }
        ],
        "responses": {
          "200": {
            "description": "Dokumentenliste",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/X-RateLimit-Limit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/X-RateLimit-Remaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/X-RateLimit-Reset" }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/DocumentListResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/documents/{id}": {
      "parameters": [
        { "$ref": "#/components/parameters/DocumentId" }
      ],
      "get": {
        "tags": ["Dokumente"],
        "summary": "Dokumentendetails abrufen",
        "description": "Status und Zusammenfassung eines Dokuments abrufen, inklusive Anzahl der Schwärzungsvorschläge nach Status.",
        "responses": {
          "200": {
            "description": "Dokumentendetails",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/X-RateLimit-Limit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/X-RateLimit-Remaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/X-RateLimit-Reset" }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/DocumentDetail" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "delete": {
        "tags": ["Dokumente"],
        "summary": "Dokument löschen",
        "description": "Dokument und alle zugehörigen Daten (Soft-Delete) löschen.",
        "responses": {
          "200": {
            "description": "Erfolgreich gelöscht",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/DeleteResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/documents/{id}/suggestions": {
      "parameters": [
        { "$ref": "#/components/parameters/DocumentId" }
      ],
      "get": {
        "tags": ["Vorschläge"],
        "summary": "Schwärzungsvorschläge auflisten",
        "description": "Alle erkannten PII-Vorschläge eines Dokuments abrufen (paginiert). Filter nach Status möglich.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": ["suggested", "accepted", "rejected", "manual"]
            },
            "description": "Filter nach Status"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "minimum": 1, "default": 1 }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "minimum": 1, "maximum": 200, "default": 50 }
          }
        ],
        "responses": {
          "200": {
            "description": "Vorschlagsliste",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/X-RateLimit-Limit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/X-RateLimit-Remaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/X-RateLimit-Reset" }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/SuggestionListResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "patch": {
        "tags": ["Vorschläge"],
        "summary": "Mehrere Vorschläge aktualisieren",
        "description": "Mehrere Schwärzungsvorschläge gleichzeitig akzeptieren oder ablehnen. Senden Sie ein JSON-Array mit Vorschlags-ID und gewünschtem Status.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/SuggestionBatchUpdateRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Anzahl aktualisierter Vorschläge",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/X-RateLimit-Limit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/X-RateLimit-Remaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/X-RateLimit-Reset" }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/SuggestionBatchUpdateResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/documents/{id}/suggestions/{suggestionId}": {
      "parameters": [
        { "$ref": "#/components/parameters/DocumentId" },
        { "$ref": "#/components/parameters/SuggestionId" }
      ],
      "patch": {
        "tags": ["Vorschläge"],
        "summary": "Einzelnen Vorschlag aktualisieren",
        "description": "Einen Schwärzungsvorschlag akzeptieren oder ablehnen. Status muss 'accepted' oder 'rejected' sein.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/SuggestionSingleUpdateRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Vorschlag aktualisiert",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/SuggestionSingleUpdateResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/documents/{id}/redact": {
      "parameters": [
        { "$ref": "#/components/parameters/DocumentId" }
      ],
      "post": {
        "tags": ["Schwärzung"],
        "summary": "Schwärzung anwenden und PDF exportieren",
        "description": "Schwärzung anwenden und das redaktierte PDF exportieren. Nur akzeptierte (und manuelle) Vorschläge werden geschwärzt. Mit `autoAcceptAll: true` werden alle offenen Vorschläge vor der Schwärzung automatisch akzeptiert. Die Antwort ist eine binäre PDF-Datei (nicht JSON). Wenn keine Schwärzungen vorhanden sind, wird 400 zurückgegeben; scheitert die Verifikation, 422.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/RedactRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Redaktiertes PDF (binär)",
            "headers": {
              "Content-Disposition": {
                "schema": { "type": "string" },
                "description": "Attachment-Header mit Dateiname `redacted_<originalname>`"
              },
              "X-Redaction-Count": {
                "schema": { "type": "integer" },
                "description": "Anzahl angewendeter Schwärzungen"
              },
              "X-Verification-Passed": {
                "schema": { "type": "boolean" },
                "description": "Verifizierungsstatus der Schwärzung"
              },
              "X-Verification-TextLayer-Only": {
                "schema": { "type": "boolean" },
                "description": "true, wenn das Dokument OCR-Seiten enthält (nur Textlayer verifizierbar)"
              },
              "X-Document-Id": { "schema": { "type": "string" } },
              "X-RateLimit-Limit": { "$ref": "#/components/headers/X-RateLimit-Limit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/X-RateLimit-Remaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/X-RateLimit-Reset" }
            },
            "content": {
              "application/pdf": {
                "schema": { "type": "string", "format": "binary" }
              }
            }
          },
          "400": {
            "description": "Keine anwendbaren Schwärzungen (z.B. keine akzeptierten Vorschläge und `autoAcceptAll` nicht gesetzt)",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "422": {
            "description": "Schwärzungsverifikation fehlgeschlagen — das Dokument wurde nicht exportiert",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/documents/{id}/report": {
      "parameters": [
        { "$ref": "#/components/parameters/DocumentId" }
      ],
      "post": {
        "tags": ["Bericht"],
        "summary": "JSON-Schwärzungsbericht abrufen",
        "description": "JSON-Schwärzungsbericht eines Dokuments mit Zusammenfassung und Typ-Aufschlüsselung. Enthält SHA-256-Prüfsumme des Originaldokuments und Verifizierungsstatus.",
        "responses": {
          "200": {
            "description": "Schwärzungsbericht",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/X-RateLimit-Limit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/X-RateLimit-Remaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/X-RateLimit-Reset" }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ReportResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/account": {
      "get": {
        "tags": ["Konto"],
        "summary": "Kontoinformationen und Nutzung",
        "description": "Kontoinformationen, aktuellen Tarif und monatliche Nutzung abrufen. `monthlyLimit` von -1 bedeutet unbegrenzt (Business-Tarif).",
        "responses": {
          "200": {
            "description": "Kontoinformationen",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/X-RateLimit-Limit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/X-RateLimit-Remaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/X-RateLimit-Reset" }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/AccountResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/audit": {
      "get": {
        "tags": ["Audit"],
        "summary": "Audit-Protokolle abrufen",
        "description": "Audit-Protokolle für alle API-Aktionen abrufen. Nur im Business-Tarif verfügbar; andere Tarife erhalten 403.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 }
          },
          {
            "name": "documentId",
            "in": "query",
            "required": false,
            "schema": { "type": "string" },
            "description": "Filter nach Dokument-ID"
          }
        ],
        "responses": {
          "200": {
            "description": "Audit-Logs",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/X-RateLimit-Limit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/X-RateLimit-Remaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/X-RateLimit-Reset" }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/AuditResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "API-Schlüssel als Bearer-Token. Token-Format: `dm_live_...` (oder `dm_test_...`). Header: `Authorization: Bearer dm_live_<schluessel>`."
      }
    },
    "parameters": {
      "DocumentId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": { "type": "string" },
        "description": "Dokument-ID"
      },
      "SuggestionId": {
        "name": "suggestionId",
        "in": "path",
        "required": true,
        "schema": { "type": "string" },
        "description": "Vorschlags-ID"
      }
    },
    "headers": {
      "X-RateLimit-Limit": {
        "schema": { "type": "integer" },
        "description": "Anfragen pro Minute laut Tarif"
      },
      "X-RateLimit-Remaining": {
        "schema": { "type": "integer" },
        "description": "Verbleibende Anfragen im aktuellen Minutenfenster"
      },
      "X-RateLimit-Reset": {
        "schema": { "type": "integer" },
        "description": "Unix-Zeitstempel (Sekunden), wann das Limit zurückgesetzt wird"
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Nicht autorisiert — API-Schlüssel fehlt oder ungültig",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      },
      "Forbidden": {
        "description": "Verboten — Tarif unterstützt diese Funktion nicht oder Monatskontingent erschöpft",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      },
      "NotFound": {
        "description": "Ressource nicht gefunden",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      },
      "ValidationError": {
        "description": "Validierungsfehler — ungültige Eingabe",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      },
      "RateLimited": {
        "description": "Rate-Limit überschritten",
        "headers": {
          "Retry-After": {
            "schema": { "type": "integer" },
            "description": "Sekunden bis zum nächsten Versuch"
          }
        },
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      },
      "ServerError": {
        "description": "Serverfehler",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "string",
            "description": "Kurzbeschreibung des Fehlers"
          },
          "details": {
            "type": "array",
            "items": { "type": "string" },
            "description": "Optionale Liste von Feld-/Validierungsfehlern (v.a. bei 400)"
          },
          "retryAfter": {
            "type": "integer",
            "description": "Sekunden bis zum Zurücksetzen des Rate-Limits (nur bei 429)"
          },
          "unverifiedPages": {
            "type": "array",
            "items": { "type": "integer" },
            "description": "Seiten mit nicht verifizierbaren Schwärzungen (nur bei 422)"
          }
        }
      },
      "DocumentUploadResponse": {
        "type": "object",
        "required": ["id", "filename", "pageCount", "suggestionsCount", "status"],
        "properties": {
          "id": { "type": "string", "description": "Dokument-ID" },
          "filename": { "type": "string", "description": "Dateiname des hochgeladenen PDFs" },
          "pageCount": { "type": "integer", "description": "Anzahl Seiten" },
          "suggestionsCount": { "type": "integer", "description": "Anzahl erkannter Schwärzungsvorschläge" },
          "status": { "type": "string", "example": "review" }
        }
      },
      "DocumentListItem": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "originalName": { "type": "string" },
          "pageCount": { "type": "integer" },
          "status": { "type": "string" },
          "createdAt": { "type": "string", "nullable": true, "description": "Convex-Zeitstempel" }
        }
      },
      "DocumentListResponse": {
        "type": "object",
        "required": ["documents", "total", "page", "limit"],
        "properties": {
          "documents": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/DocumentListItem" }
          },
          "total": { "type": "integer" },
          "page": { "type": "integer" },
          "limit": { "type": "integer" }
        }
      },
      "DocumentDetail": {
        "type": "object",
        "required": ["id", "originalName", "pageCount", "status", "createdAt", "suggestions"],
        "properties": {
          "id": { "type": "string" },
          "originalName": { "type": "string" },
          "pageCount": { "type": "integer" },
          "status": { "type": "string" },
          "createdAt": { "type": "string", "nullable": true },
          "suggestions": {
            "type": "object",
            "required": ["total", "accepted", "rejected", "pending"],
            "properties": {
              "total": { "type": "integer" },
              "accepted": { "type": "integer" },
              "rejected": { "type": "integer" },
              "pending": { "type": "integer" }
            }
          }
        }
      },
      "Suggestion": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "documentId": { "type": "string", "nullable": true },
          "sensitivityType": {
            "type": "string",
            "description": "PII-Typ, z.B. name, address, phone, email, birth_date, iban, customer_number, insurance_number, ssn, credit_card, other"
          },
          "detectedText": { "type": "string", "description": "Erkannter Text (kann leer sein)" },
          "confidence": { "type": "number", "description": "Konfidenzwert" },
          "pageNumber": { "type": "integer", "nullable": true },
          "bboxX": { "type": "number", "nullable": true },
          "bboxY": { "type": "number", "nullable": true },
          "bboxWidth": { "type": "number", "nullable": true },
          "bboxHeight": { "type": "number", "nullable": true },
          "status": {
            "type": "string",
            "enum": ["suggested", "accepted", "rejected", "manual"]
          },
          "source": {
            "type": "string",
            "description": "Erkennungsquelle, z.B. regex, ner, custom"
          }
        }
      },
      "SuggestionListResponse": {
        "type": "object",
        "required": ["suggestions", "total", "page", "limit"],
        "properties": {
          "suggestions": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Suggestion" }
          },
          "total": { "type": "integer" },
          "page": { "type": "integer" },
          "limit": { "type": "integer" }
        }
      },
      "SuggestionStatusUpdate": {
        "type": "object",
        "required": ["id", "status"],
        "properties": {
          "id": { "type": "string", "description": "Vorschlags-ID" },
          "status": { "type": "string", "enum": ["accepted", "rejected"] }
        }
      },
      "SuggestionBatchUpdateRequest": {
        "type": "array",
        "items": { "$ref": "#/components/schemas/SuggestionStatusUpdate" }
      },
      "SuggestionBatchUpdateResponse": {
        "type": "object",
        "required": ["updated"],
        "properties": {
          "updated": { "type": "integer", "description": "Anzahl aktualisierter Vorschläge" }
        }
      },
      "SuggestionSingleUpdateRequest": {
        "type": "object",
        "required": ["status"],
        "properties": {
          "status": { "type": "string", "enum": ["accepted", "rejected"] }
        }
      },
      "SuggestionSingleUpdateResponse": {
        "type": "object",
        "required": ["id", "status"],
        "properties": {
          "id": { "type": "string" },
          "status": { "type": "string", "enum": ["accepted", "rejected"] }
        }
      },
      "RedactRequest": {
        "type": "object",
        "properties": {
          "autoAcceptAll": {
            "type": "boolean",
            "default": false,
            "description": "Wenn true, werden alle offenen Vorschläge (Status suggested) automatisch akzeptiert, bevor geschwärzt wird."
          }
        }
      },
      "DeleteResponse": {
        "type": "object",
        "required": ["success"],
        "properties": {
          "success": { "type": "boolean", "example": true }
        }
      },
      "ReportDocument": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "originalName": { "type": "string", "nullable": true },
          "pageCount": { "type": "integer", "nullable": true },
          "status": { "type": "string", "nullable": true },
          "fileSize": { "type": "integer", "nullable": true },
          "checksumSha256": { "type": "string", "nullable": true },
          "createdAt": { "type": "string", "nullable": true }
        }
      },
      "RedactionSummary": {
        "type": "object",
        "required": ["total", "accepted", "rejected", "pending", "manual"],
        "properties": {
          "total": { "type": "integer" },
          "accepted": { "type": "integer" },
          "rejected": { "type": "integer" },
          "pending": { "type": "integer" },
          "manual": { "type": "integer" }
        }
      },
      "ReportResponse": {
        "type": "object",
        "required": ["document", "redactionSummary", "typeBreakdown", "verificationPassed"],
        "properties": {
          "document": { "$ref": "#/components/schemas/ReportDocument" },
          "redactionSummary": { "$ref": "#/components/schemas/RedactionSummary" },
          "typeBreakdown": {
            "type": "object",
            "additionalProperties": { "type": "integer" },
            "description": "Anzahl Vorschläge pro PII-Typ (Schlüssel sind sensitivityType-Werte)"
          },
          "verificationPassed": { "type": "boolean" }
        }
      },
      "AccountFeatures": {
        "type": "object",
        "properties": {
          "enableOcr": { "type": "boolean" },
          "enableNer": { "type": "boolean" },
          "enableCustomPatterns": { "type": "boolean" },
          "enableApiAccess": { "type": "boolean" },
          "apiRateLimit": { "type": "integer" },
          "docsPerMonth": {
            "description": "Monatskontingent; -1 oder 'unlimited' bedeutet unbegrenzt.",
            "oneOf": [
              { "type": "integer" },
              { "type": "string", "enum": ["unlimited"] }
            ]
          },
          "maxPages": {
            "description": "Seitenlimit; Wert kann 'unlimited' sein.",
            "oneOf": [
              { "type": "integer" },
              { "type": "string", "enum": ["unlimited"] }
            ]
          }
        }
      },
      "AccountResponse": {
        "type": "object",
        "required": ["plan", "usage"],
        "properties": {
          "plan": {
            "type": "object",
            "required": ["name", "key", "features"],
            "properties": {
              "name": { "type": "string" },
              "key": { "type": "string" },
              "features": { "$ref": "#/components/schemas/AccountFeatures" }
            }
          },
          "usage": {
            "type": "object",
            "required": ["monthlyUsed", "monthlyLimit", "periodStart"],
            "properties": {
              "monthlyUsed": { "type": "integer" },
              "monthlyLimit": {
                "type": "integer",
                "description": "-1 bedeutet unbegrenzt (Business-Tarif)"
              },
              "periodStart": { "type": "string", "description": "ISO 8601" },
              "periodEnd": { "type": "string", "nullable": true, "description": "ISO 8601 oder null" }
            }
          }
        }
      },
      "AuditLog": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "documentId": { "type": "string", "nullable": true },
          "action": { "type": "string", "description": "z.B. api.document.upload, api.suggestion.accepted" },
          "details": { "type": "object", "nullable": true },
          "ipAddress": { "type": "string", "nullable": true },
          "createdAt": { "type": "string", "nullable": true, "description": "ISO 8601" }
        }
      },
      "AuditResponse": {
        "type": "object",
        "required": ["logs"],
        "properties": {
          "logs": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/AuditLog" }
          }
        }
      },
      "ApiStatus": {
        "type": "object",
        "required": ["status", "service", "version", "endpoints"],
        "properties": {
          "status": { "type": "string", "example": "ok" },
          "service": { "type": "string", "example": "datenmaske-api" },
          "version": { "type": "string", "example": "v1" },
          "endpoints": {
            "type": "object",
            "additionalProperties": { "type": "string" },
            "description": "Map von Endpunkt-Pfaden auf Beschreibungen"
          }
        }
      }
    }
  }
}
