openapi: "3.1.0" info: title: go-documenter API description: | API-first Dokumentenmanagement-System. Dieses Dokument ist die Quelle der Wahrheit für den API-Vertrag; Endpunkte werden hier VOR der Implementierung spezifiziert. Architektur: doc/architecture.md, Anforderungen: doc/requirements/. version: "0.1.0" servers: - url: / description: Hinter Caddy-Reverse-Proxy security: - bearerAuth: [] paths: /healthz: get: summary: Liveness-Check mit Versionsinformation security: [] responses: "200": description: Dienst läuft content: application/json: schema: type: object properties: status: { type: string, example: ok } commit: { type: string } tag: { type: string } /v1/documents: post: summary: Dokument hochladen (PDF, JPG, PNG, HEIC) requestBody: required: true content: multipart/form-data: schema: type: object required: [file] properties: file: type: string format: binary responses: "202": description: Angenommen, Verarbeitung gestartet content: application/json: schema: type: object properties: id: { $ref: "#/components/schemas/UUID" } status: { type: string, example: received } "401": { $ref: "#/components/responses/Unauthorized" } "409": description: Byte-identisches Dokument existiert bereits content: application/json: schema: allOf: - $ref: "#/components/schemas/Error" - type: object properties: existing_document_id: { $ref: "#/components/schemas/UUID" } "413": { description: Upload überschreitet das konfigurierte Größenlimit } "422": { description: Nicht unterstützter Dateityp } get: summary: Dokumente auflisten (Projektion) parameters: - { name: status, in: query, schema: { $ref: "#/components/schemas/Status" } } - { name: tag, in: query, schema: { type: string } } - { name: limit, in: query, schema: { type: integer, default: 50, maximum: 200 } } - { name: offset, in: query, schema: { type: integer, default: 0 } } responses: "200": description: Liste content: application/json: schema: type: object properties: documents: type: array items: { $ref: "#/components/schemas/Document" } total: { type: integer } "401": { $ref: "#/components/responses/Unauthorized" } /v1/documents/{id}: get: summary: Einzelnes Dokument abrufen parameters: - { $ref: "#/components/parameters/DocumentID" } - name: include_text in: query description: Volltext im Response mitliefern schema: { type: boolean, default: false } responses: "200": description: Dokument content: application/json: schema: { $ref: "#/components/schemas/Document" } "401": { $ref: "#/components/responses/Unauthorized" } "404": { $ref: "#/components/responses/NotFound" } /v1/documents/{id}/file: get: summary: Originaldatei streamen (byte-identisch zum Import) parameters: - { $ref: "#/components/parameters/DocumentID" } responses: "200": description: Dateiinhalt content: application/pdf: { schema: { type: string, format: binary } } image/jpeg: { schema: { type: string, format: binary } } image/png: { schema: { type: string, format: binary } } image/heic: { schema: { type: string, format: binary } } "401": { $ref: "#/components/responses/Unauthorized" } "404": { $ref: "#/components/responses/NotFound" } /v1/documents/{id}/events: get: summary: Audit-Trail — alle Events des Dokument-Aggregats parameters: - { $ref: "#/components/parameters/DocumentID" } responses: "200": description: Eventfolge in seq-Reihenfolge content: application/json: schema: type: object properties: events: type: array items: { $ref: "#/components/schemas/Event" } "401": { $ref: "#/components/responses/Unauthorized" } "404": { $ref: "#/components/responses/NotFound" } /v1/documents/{id}/reprocess: post: summary: Verarbeitung wiederaufnehmen (nach Fehler oder für Nachanreicherung) parameters: - { $ref: "#/components/parameters/DocumentID" } requestBody: required: false content: application/json: schema: type: object properties: from_stage: type: string enum: [extract, ocr, enrich, index, file] responses: "202": { description: Wiederaufnahme eingeplant } "401": { $ref: "#/components/responses/Unauthorized" } "404": { $ref: "#/components/responses/NotFound" } "409": { description: Dokument ist aktuell in Verarbeitung (geclaimt) } /v1/search: get: summary: Volltextsuche (FTS5, BM25-Ranking) parameters: - { name: q, in: query, required: true, schema: { type: string } } - { name: limit, in: query, schema: { type: integer, default: 20, maximum: 100 } } - { name: offset, in: query, schema: { type: integer, default: 0 } } responses: "200": description: Treffer nach Relevanz sortiert content: application/json: schema: type: object properties: results: type: array items: type: object properties: id: { $ref: "#/components/schemas/UUID" } title: { type: string } original_name: { type: string } archive_path: { type: string } snippet: { type: string, description: Treffer-Ausschnitt mit Hervorhebung } rank: { type: number } total: { type: integer } "400": { description: Fehlender oder ungültiger Suchbegriff } "401": { $ref: "#/components/responses/Unauthorized" } /v1/admin/search/rebuild: post: summary: FTS-Index vollständig aus der documents-Projektion neu aufbauen responses: "200": description: Rebuild abgeschlossen content: application/json: schema: type: object properties: indexed_documents: { type: integer } duration_ms: { type: integer } "401": { $ref: "#/components/responses/Unauthorized" } components: securitySchemes: bearerAuth: type: http scheme: bearer description: Statischer API-Token (Env `API_TOKEN`) parameters: DocumentID: name: id in: path required: true schema: { $ref: "#/components/schemas/UUID" } responses: Unauthorized: description: Fehlender oder ungültiger Token content: application/json: schema: { $ref: "#/components/schemas/Error" } NotFound: description: Dokument nicht gefunden content: application/json: schema: { $ref: "#/components/schemas/Error" } schemas: UUID: type: string format: uuid example: a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d Status: type: string enum: [received, processing, ocr_done, enriched, indexed, filed, failed] Error: type: object properties: error: type: object properties: code: { type: string, example: duplicate_document } message: { type: string } Event: type: object properties: seq: { type: integer } event_type: { type: string, example: DocumentReceived } payload: { type: object, description: Event-spezifischer Payload (snake_case) } created_at: { type: string, format: date-time } Document: type: object properties: id: { $ref: "#/components/schemas/UUID" } status: { $ref: "#/components/schemas/Status" } original_name: { type: string } media_type: { type: string, enum: [pdf, jpg, png, heic] } origin: { type: string, enum: [api, import_folder] } content_hash: { type: string, description: SHA-256 (hex) } size_bytes: { type: integer } page_count: { type: integer } title: { type: string } summary: { type: string } tags: type: array items: { type: string } doc_date: { type: string, description: Vom LLM erkanntes Dokumentdatum (Metadatum) } language: { type: string } enriched: { type: boolean } archive_path: { type: string, description: Relativ zum Archiv-Wurzelverzeichnis } failed_stage: { type: string } last_error: { type: string } text: { type: string, description: Nur bei include_text=true } imported_at: { type: string, format: date-time } updated_at: { type: string, format: date-time }