{"openapi":"3.1.0","info":{"title":"drbruening API","description":"\n## drbruening RAG-KI API\n\nSchnittstelle für den Zugriff auf die KI-gestützte TIA-Analyse.\n\n### Authentifizierung\nAlle Endpunkte (außer `/health` und `/api/v1/auth/login`) erfordern einen Bearer-Token.\n\n1. Token holen: `POST /api/v1/auth/login`\n2. Token in Header senden: `Authorization: Bearer <token>`\n\n### Mandanten-Isolation\nJeder Benutzer gehört zu einem Mandanten (Tenant), der aus dem JWT abgeleitet wird\n(implizites Tenant-Scoping). Rückgaben sind immer auf den eigenen Mandanten begrenzt,\naußer bei `/admin/*`-Endpunkten (nur `system-admin`).\n\n### Rollen\n`system-admin` (nur Matthias, tenant-übergreifend) · `developer` (externer\nProgrammierer, eigener Sandbox-Tenant mit Test-Projektdaten) · `owner` /\n`admin` (Verwaltungsrechte im eigenen Tenant) · `member` (normaler Nutzer).\n\n### Projektdaten\nDer Client spricht für **alles** ausschließlich mit dieser API -- nie direkt\nmit Slokal, nie mit einem VPN/Tunnel-Endpunkt. Rohdaten (XML/SCL von\nTIA-Portal-Projekten sowie hochgeladene Dokumente) werden trotzdem **nie**\nim Scloud-Anwendungscode gespeichert oder verarbeitet: der Datei-Upload läuft\nüber einen eigenen Scloud-API-Endpunkt, den nginx per Streaming direkt an\nSlokal durchreicht (Details siehe `POST /projects/{project_id}/snapshots`).\nScloud verwaltet dazu nur Metadaten (`/projects/*/snapshots`,\n`/knowledge/documents`).\n","version":"1.0.0"},"paths":{"/api/v1/health/live":{"get":{"tags":["health"],"summary":"Liveness (nur Scloud + DB)","description":"Fuer den Docker-HEALTHCHECK des Containers selbst -- haengt bewusst NICHT von Slokal-Erreichbarkeit ab, sonst wuerde der Container als 'unhealthy' gelten, sobald nur Slokal down ist.","operationId":"health_live_api_v1_health_live_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/health":{"get":{"tags":["health"],"summary":"Gesundheitsstatus (Scloud, Slokal, Gesamtsystem)","description":"Ein konsolidierter Endpunkt statt vier einzelner -- liefert Scloud-, Scloud-DB-,\nSlokal- und Slokal-Software-Status in einem JSON. Wird von der Weboberfläche\n(Health-Check-Anzeige) verwendet.","operationId":"health_api_v1_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/auth/login":{"post":{"tags":["auth"],"summary":"Anmelden","description":"Gibt einen JWT-Bearer-Token zurück. Token-Laufzeit: 60 Minuten.","operationId":"login_api_v1_auth_login_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_login_api_v1_auth_login_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/register":{"post":{"tags":["auth"],"summary":"Benutzer registrieren","description":"Neuen Benutzer (Rolle `member`) für einen bestehenden Mandanten anlegen.\n\n**Jetzt:** nur `system-admin` darf diesen Endpunkt aufrufen -- es gibt noch\nkeine Selbstregistrierung und keinen Einladungs-Flow (kommt erst in der\nkommerziellen Version). Für Benutzer mit anderer Rolle `POST /users` nutzen.","operationId":"register_api_v1_auth_register_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/api/v1/auth/refresh":{"post":{"tags":["auth"],"summary":"Token erneuern","description":"Erfordert einen noch gültigen Bearer-Token und gibt einen neuen mit frischer Laufzeit zurück (Sliding Session).","operationId":"refresh_api_v1_auth_refresh_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/api/v1/auth/logout":{"post":{"tags":["auth"],"summary":"Abmelden","description":"Zustandslose JWTs (12-Faktor: kein Sitzungsspeicher im Prozess) -- es gibt\nkeine serverseitige Token-Blockliste. Der Client verwirft den Token; dieser\nEndpunkt bestätigt das nur und dient als klarer API-Vertragspunkt für Clients.","operationId":"logout_api_v1_auth_logout_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/api/v1/auth/me":{"get":{"tags":["auth"],"summary":"Eigene Benutzerdaten","description":"Gibt Daten des aktuell angemeldeten Benutzers zurück.","operationId":"me_api_v1_auth_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/api/v1/tenants/{tenant_id}":{"get":{"tags":["tenants"],"summary":"Mandant abrufen","description":"Nur der eigene Mandant ist sichtbar, außer für system-admin.","operationId":"get_tenant_api_v1_tenants__tenant_id__get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tenant Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["tenants"],"summary":"Mandant aktualisieren","description":"Nur `name` ist editierbar. `plan`/Stripe-Felder werden ausschließlich über den Stripe-Webhook gepflegt.","operationId":"update_tenant_api_v1_tenants__tenant_id__patch","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tenant Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/tenants":{"get":{"tags":["admin"],"summary":"Alle Mandanten auflisten (nur system-admin)","operationId":"admin_list_tenants_api_v1_admin_tenants_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TenantResponse"},"type":"array","title":"Response Admin List Tenants Api V1 Admin Tenants Get"}}}}},"security":[{"OAuth2PasswordBearer":[]}]},"post":{"tags":["admin"],"summary":"Mandant anlegen (nur system-admin)","description":"Erstellt einen neuen Mandanten und dessen ersten Benutzer (Standardrolle\n`owner`, z.B. `developer` für die Sandbox des externen Programmierers) in\neinem Schritt. **Jetzt:** direkte Anlage mit Initialpasswort durch\nsystem-admin (nur Matthias) -- der Einladungs-Link-Flow (Nutzer setzt eigenes\nPasswort über einmaligen Token) kommt erst in der kommerziellen Version.\n\nBei `role=developer` wird zusätzlich ein Dummy-Testprojekt angelegt, damit\ndie Sandbox sofort nutzbar ist, ohne echte Kundendaten zu berühren.","operationId":"admin_create_tenant_api_v1_admin_tenants_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminCreateTenantRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminCreateTenantResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/api/v1/admin/tenants/{tenant_id}":{"delete":{"tags":["admin"],"summary":"Mandant löschen (nur system-admin)","description":"Löscht den Mandanten samt aller Benutzer, Projekte, Snapshots, Dokumente und\nChats (CASCADE). Gibt insbesondere den `slug` wieder frei -- das Löschen\neines einzelnen Benutzers (`DELETE /users/{id}`) löscht NICHT den Mandanten\nselbst, dessen Slug bliebe sonst dauerhaft blockiert.","operationId":"admin_delete_tenant_api_v1_admin_tenants__tenant_id__delete","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tenant Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/slokal/status":{"get":{"tags":["admin"],"summary":"Slokal-Adresse und Erreichbarkeit (nur system-admin)","description":"`host` ist die WireGuard-Tunnel-Adresse, unter der Scloud Slokal erreicht --\nkein öffentliches Internet-IP, da Slokal nicht direkt exponiert ist.\n\n`public_ip` ist die aktuelle öffentliche IP von Slokals Heimnetz-Anschluss\n(aus dem letzten WireGuard-Handshake, kann sich bei dynamischer IP ändern) --\nzum direkten SSH-Zugriff von unterwegs. `public_ip_last_seen` zeigt, wie\naktuell dieser Wert ist (Snapshot wird minütlich aktualisiert).","operationId":"slokal_status_api_v1_admin_slokal_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlokalStatusResponse"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/api/v1/admin/slokal/shutdown":{"post":{"tags":["admin"],"summary":"Slokal sauber herunterfahren (nur system-admin)","description":"Versucht zuerst den künftigen Slokal-eigenen `POST /api/v1/system/shutdown`\n(Teil der noch ausstehenden Slokal-Portierung) -- solange der nicht\nexistiert, per SSH-Fallback über den WireGuard-Tunnel (`SLOKAL_SSH_USER`\nmuss konfiguriert und der Public Key auf Slokal hinterlegt sein).\nKlappt keins von beidem: `503 temporarily_unavailable`.","operationId":"slokal_shutdown_api_v1_admin_slokal_shutdown_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/api/v1/users":{"get":{"tags":["users"],"summary":"Benutzer auflisten","description":"Tenant-Scope kommt implizit aus dem JWT -- es werden ausschließlich Benutzer\ndes eigenen Mandanten zurückgegeben.\n\n**Ausnahme system-admin:** optionaler Query-Parameter `tenant_id` zeigt\nBenutzer eines beliebigen Mandanten; ohne `tenant_id` werden Benutzer aller\nMandanten zurückgegeben (tenant-übergreifende Verwaltung).","operationId":"list_users_api_v1_users_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"tenant_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Tenant Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserResponse"},"title":"Response List Users Api V1 Users Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["users"],"summary":"Benutzer anlegen","description":"Wer welche Rolle anlegen darf, ist fest vorgegeben:\n\n- `system-admin`: jede Rolle, in jedem Mandanten.\n- `developer`: `owner`, `admin`, `member` -- nur im eigenen Mandanten.\n- `owner`: `admin`, `member` -- nur im eigenen Mandanten.\n- `admin`: nur `member` -- nur im eigenen Mandanten.\n- `member`: darf niemanden anlegen.\n\nEs gibt noch keinen Einladungs-Flow (kommt erst in der kommerziellen\nVersion), bei dem ein Tenant-Admin eigenständig Nutzer einladen kann.","operationId":"create_user_api_v1_users_post","security":[{"OAuth2PasswordBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/{user_id}":{"get":{"tags":["users"],"summary":"Benutzer abrufen","operationId":"get_user_api_v1_users__user_id__get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["users"],"summary":"Benutzer aktualisieren","description":"Eigenes Profil: `full_name`, `email`, `password`. Alles inkl. `role` und\n`is_active` bei fremden Benutzern erfordert owner/admin/system-admin des\neigenen Mandanten (system-admin tenant-übergreifend).","operationId":"update_user_api_v1_users__user_id__patch","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["users"],"summary":"Benutzer löschen (nur system-admin)","operationId":"delete_user_api_v1_users__user_id__delete","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"User Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/projects":{"get":{"tags":["projects"],"summary":"Projekte des eigenen Mandanten auflisten","operationId":"list_projects_api_v1_projects_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ProjectResponse"},"type":"array","title":"Response List Projects Api V1 Projects Get"}}}}},"security":[{"OAuth2PasswordBearer":[]}]},"post":{"tags":["projects"],"summary":"Projekt anlegen","operationId":"create_project_api_v1_projects_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/api/v1/projects/{project_id}":{"get":{"tags":["projects"],"summary":"Projekt abrufen","operationId":"get_project_api_v1_projects__project_id__get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Project Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["projects"],"summary":"Projekt aktualisieren","operationId":"update_project_api_v1_projects__project_id__patch","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Project Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProjectRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["projects"],"summary":"Projekt löschen","operationId":"delete_project_api_v1_projects__project_id__delete","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Project Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/projects/{project_id}/snapshots":{"get":{"tags":["projects"],"summary":"Snapshots eines Projekts auflisten","operationId":"list_snapshots_api_v1_projects__project_id__snapshots_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Project Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SnapshotResponse"},"title":"Response List Snapshots Api V1 Projects  Project Id  Snapshots Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["projects"],"summary":"Snapshot registrieren","description":"Registriert die Metadaten einer bereits abgeschlossenen Projektdaten-Übertragung.\n\n**Hinweis:** Für den eigentlichen Datei-Upload den regulären Weg nutzen:\n`POST /projects/upload` (siehe upload-endpunkt-spezifikation.md) -- der legt\nProjekt UND Snapshot in einem Schritt an und reicht die Datei per\nnginx-Streaming direkt an Slokal durch, ohne dass sie je vom\nFastAPI-Anwendungscode gelesen wird. Dieser Endpunkt hier bleibt für die\nmanuelle Nachregistrierung von Metadaten zu einem bereits anderweitig\nhochgeladenen Snapshot bestehen (z.B. Altfälle, Admin-Korrekturen), nimmt\nselbst keine Datei entgegen.","operationId":"register_snapshot_api_v1_projects__project_id__snapshots_post","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Project Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterSnapshotRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SnapshotResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/projects/{project_id}/snapshots/{snapshot_id}":{"get":{"tags":["projects"],"summary":"Snapshot abrufen","operationId":"get_snapshot_api_v1_projects__project_id__snapshots__snapshot_id__get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Project Id"}},{"name":"snapshot_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Snapshot Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SnapshotResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["projects"],"summary":"Snapshot löschen","description":"Löscht nur den Metadaten-Eintrag auf Scloud. Ob/wann die zugehörige Datei auf Slokal ebenfalls entfernt wird, ist noch offen (spätere Entscheidung).","operationId":"delete_snapshot_api_v1_projects__project_id__snapshots__snapshot_id__delete","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Project Id"}},{"name":"snapshot_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Snapshot Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/projects/{project_id}/chats":{"get":{"tags":["chats"],"summary":"Eigene Chats zu diesem Projekt auflisten","description":"Liefert alle Chats des angemeldeten Users zu diesem Projekt, neueste zuerst. Ein User kann mehrere Chats zum selben Projekt haben (z.B. um neu anzufangen).","operationId":"list_project_chats_api_v1_projects__project_id__chats_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Project Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ChatResponse"},"title":"Response List Project Chats Api V1 Projects  Project Id  Chats Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["chats"],"summary":"Neuen Chat zu diesem Projekt anlegen","description":"Legt immer einen neuen, leeren Chat an (nicht idempotent) -- damit lässt sich jederzeit ohne die Historie vorheriger Chats neu anfangen.","operationId":"create_project_chat_api_v1_projects__project_id__chats_post","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Project Id"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/{chat_id}":{"get":{"tags":["chats"],"summary":"Chat abrufen","operationId":"get_chat_api_v1_chats__chat_id__get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"chat_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Chat Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["chats"],"summary":"Chat aktualisieren (Titel)","operationId":"update_chat_api_v1_chats__chat_id__patch","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"chat_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Chat Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateChatRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["chats"],"summary":"Chatverlauf löschen","description":"Löscht den Chat und alle zugehörigen Nachrichten (CASCADE).","operationId":"delete_chat_api_v1_chats__chat_id__delete","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"chat_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Chat Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/{chat_id}/messages":{"get":{"tags":["chats"],"summary":"Chatverlauf abrufen","operationId":"list_messages_api_v1_chats__chat_id__messages_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"chat_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Chat Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MessageResponse"},"title":"Response List Messages Api V1 Chats  Chat Id  Messages Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["chats"],"summary":"Chat-Frage stellen (SSE-Streaming)","description":"Speichert die Frage des Users, leitet sie an die KI-Analyse auf Slokal weiter\nund **streamt** die Antwort Token für Token als Server-Sent-Events zurück\n(`data: {\"token\": \"...\"}\\n\\n`, Abschluss immer `data: [DONE]\\n\\n\"`). Die\nvollständige Antwort wird nach Abschluss des Streams im Chatverlauf\ngespeichert.\n\nKann Slokal beim Verbindungsaufbau nicht erreicht werden, antwortet der\nEndpunkt strukturiert mit `503` (bevor das Streaming beginnt). Bricht die\nVerbindung erst mitten im Stream ab, wird stattdessen ein\n`data: {\"error\": \"...\"}\\n\\n\"`-Event gesendet -- der HTTP-Status ist dann schon\ngesendet (200) und kann nicht mehr geändert werden.","operationId":"create_message_api_v1_chats__chat_id__messages_post","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"chat_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Chat Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMessageRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/{chat_id}/messages/{message_id}/feedback":{"post":{"tags":["chats"],"summary":"Feedback zu einer Antwort geben","description":"`rating`: `1` (hilfreich) oder `-1` (nicht hilfreich).","operationId":"give_feedback_api_v1_chats__chat_id__messages__message_id__feedback_post","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"chat_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Chat Id"}},{"name":"message_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Message Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/knowledge/documents":{"get":{"tags":["documents"],"summary":"Dokumente des eigenen Mandanten auflisten","operationId":"list_documents_api_v1_knowledge_documents_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/DocumentResponse"},"type":"array","title":"Response List Documents Api V1 Knowledge Documents Get"}}}}},"security":[{"OAuth2PasswordBearer":[]}]},"post":{"tags":["documents"],"summary":"Dokument registrieren","description":"Registriert die Metadaten eines bereits hochgeladenen Dokuments, analog zu\n`POST /projects/{project_id}/snapshots` -- Client spricht nur mit der\nScloud-API, die Datei selbst läuft per nginx-Streaming-Durchreichung zu\nSlokal durch, ohne im Scloud-Anwendungscode verarbeitet zu werden. Siehe\ndortige Beschreibung für Details.","operationId":"register_document_api_v1_knowledge_documents_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterDocumentRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/api/v1/knowledge/documents/{document_id}":{"get":{"tags":["documents"],"summary":"Dokument abrufen","operationId":"get_document_api_v1_knowledge_documents__document_id__get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"document_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Document Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["documents"],"summary":"Dokument löschen","operationId":"delete_document_api_v1_knowledge_documents__document_id__delete","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"document_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Document Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/uploads/{upload_id}":{"get":{"tags":["upload"],"summary":"Upload-/Verarbeitungsstatus abfragen","description":"`uploadId` ist die `id` des bei der Autorisierung angelegten Snapshots.\nFragt bei jedem Aufruf live bei Slokal nach dem aktuellen Verarbeitungsstand\n(Pull, kein Push von Slokal) und reicht die Antwort durch. Sobald Slokal die\n`manifest.json` gelesen hat, wird der darin enthaltene echte Projektname hier\nuebernommen (ersetzt den Platzhalter aus der Anlage).","operationId":"get_upload_status_api_v1_uploads__upload_id__get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"upload_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Upload Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/stripe/webhook":{"post":{"tags":["stripe"],"summary":"Stripe Webhook","operationId":"stripe_webhook_api_v1_stripe_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AdminCreateTenantRequest":{"properties":{"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"owner_email":{"type":"string","format":"email","title":"Owner Email"},"owner_password":{"type":"string","title":"Owner Password"},"owner_full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Owner Full Name"},"role":{"type":"string","title":"Role","default":"owner"}},"type":"object","required":["name","slug","owner_email","owner_password"],"title":"AdminCreateTenantRequest"},"AdminCreateTenantResponse":{"properties":{"tenant_id":{"type":"string","title":"Tenant Id"},"first_user_id":{"type":"string","title":"First User Id"}},"type":"object","required":["tenant_id","first_user_id"],"title":"AdminCreateTenantResponse"},"Body_login_api_v1_auth_login_post":{"properties":{"grant_type":{"anyOf":[{"type":"string","pattern":"password"},{"type":"null"}],"title":"Grant Type"},"username":{"type":"string","title":"Username"},"password":{"type":"string","title":"Password"},"scope":{"type":"string","title":"Scope","default":""},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id"},"client_secret":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Secret"}},"type":"object","required":["username","password"],"title":"Body_login_api_v1_auth_login_post"},"ChatResponse":{"properties":{"id":{"type":"string","title":"Id"},"project_id":{"type":"string","title":"Project Id"},"user_id":{"type":"string","title":"User Id"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"created_at":{"type":"string","title":"Created At"}},"type":"object","required":["id","project_id","user_id","title","created_at"],"title":"ChatResponse"},"CreateMessageRequest":{"properties":{"content":{"type":"string","title":"Content"}},"type":"object","required":["content"],"title":"CreateMessageRequest"},"CreateProjectRequest":{"properties":{"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"}},"type":"object","required":["name"],"title":"CreateProjectRequest"},"CreateUserRequest":{"properties":{"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"email":{"type":"string","format":"email","title":"Email"},"password":{"type":"string","title":"Password"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"role":{"type":"string","title":"Role","default":"member"}},"type":"object","required":["tenant_id","email","password"],"title":"CreateUserRequest"},"DocumentResponse":{"properties":{"id":{"type":"string","title":"Id"},"filename":{"type":"string","title":"Filename"},"size_bytes":{"type":"integer","title":"Size Bytes"},"checksum_sha256":{"type":"string","title":"Checksum Sha256"},"status":{"type":"string","title":"Status"},"uploaded_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Uploaded By"},"created_at":{"type":"string","title":"Created At"}},"type":"object","required":["id","filename","size_bytes","checksum_sha256","status","uploaded_by","created_at"],"title":"DocumentResponse"},"FeedbackRequest":{"properties":{"rating":{"type":"integer","title":"Rating"},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment"}},"type":"object","required":["rating"],"title":"FeedbackRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"MessageResponse":{"properties":{"id":{"type":"string","title":"Id"},"chat_id":{"type":"string","title":"Chat Id"},"role":{"type":"string","title":"Role"},"content":{"type":"string","title":"Content"},"rating":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rating"},"created_at":{"type":"string","title":"Created At"}},"type":"object","required":["id","chat_id","role","content","rating","created_at"],"title":"MessageResponse"},"ProjectResponse":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"status":{"type":"string","title":"Status"},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By"},"created_at":{"type":"string","title":"Created At"}},"type":"object","required":["id","name","description","status","created_by","created_at"],"title":"ProjectResponse"},"RegisterDocumentRequest":{"properties":{"filename":{"type":"string","title":"Filename"},"size_bytes":{"type":"integer","title":"Size Bytes"},"checksum_sha256":{"type":"string","title":"Checksum Sha256"}},"type":"object","required":["filename","size_bytes","checksum_sha256"],"title":"RegisterDocumentRequest"},"RegisterRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"password":{"type":"string","title":"Password"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"tenant_slug":{"type":"string","title":"Tenant Slug"}},"type":"object","required":["email","password","tenant_slug"],"title":"RegisterRequest"},"RegisterSnapshotRequest":{"properties":{"filename":{"type":"string","title":"Filename"},"size_bytes":{"type":"integer","title":"Size Bytes"},"checksum_sha256":{"type":"string","title":"Checksum Sha256"}},"type":"object","required":["filename","size_bytes","checksum_sha256"],"title":"RegisterSnapshotRequest"},"SlokalStatusResponse":{"properties":{"host":{"type":"string","title":"Host"},"port":{"type":"integer","title":"Port"},"erreichbar":{"type":"boolean","title":"Erreichbar"},"public_ip":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Public Ip"},"public_ip_last_seen":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Public Ip Last Seen"}},"type":"object","required":["host","port","erreichbar","public_ip","public_ip_last_seen"],"title":"SlokalStatusResponse"},"SnapshotResponse":{"properties":{"id":{"type":"string","title":"Id"},"project_id":{"type":"string","title":"Project Id"},"filename":{"type":"string","title":"Filename"},"size_bytes":{"type":"integer","title":"Size Bytes"},"checksum_sha256":{"type":"string","title":"Checksum Sha256"},"status":{"type":"string","title":"Status"},"uploaded_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Uploaded By"},"created_at":{"type":"string","title":"Created At"}},"type":"object","required":["id","project_id","filename","size_bytes","checksum_sha256","status","uploaded_by","created_at"],"title":"SnapshotResponse"},"TenantResponse":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"plan":{"type":"string","title":"Plan"},"is_active":{"type":"boolean","title":"Is Active"}},"type":"object","required":["id","name","slug","plan","is_active"],"title":"TenantResponse"},"TenantUpdateRequest":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"}},"type":"object","title":"TenantUpdateRequest"},"TokenResponse":{"properties":{"access_token":{"type":"string","title":"Access Token"},"token_type":{"type":"string","title":"Token Type","default":"bearer"},"user_id":{"type":"string","title":"User Id"},"tenant_id":{"type":"string","title":"Tenant Id"},"role":{"type":"string","title":"Role"}},"type":"object","required":["access_token","user_id","tenant_id","role"],"title":"TokenResponse"},"UpdateChatRequest":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"}},"type":"object","title":"UpdateChatRequest"},"UpdateProjectRequest":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},"type":"object","title":"UpdateProjectRequest"},"UpdateUserRequest":{"properties":{"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"email":{"anyOf":[{"type":"string","format":"email"},{"type":"null"}],"title":"Email"},"password":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Password"},"role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Role"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"}},"type":"object","title":"UpdateUserRequest"},"UploadStatusResponse":{"properties":{"upload_id":{"type":"string","title":"Upload Id"},"project_id":{"type":"string","title":"Project Id"},"status":{"type":"string","title":"Status"},"error":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Error"},"skipped_files":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Skipped Files"},"unknown_instructions":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Unknown Instructions"},"stats":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Stats"}},"type":"object","required":["upload_id","project_id","status"],"title":"UploadStatusResponse"},"UserResponse":{"properties":{"id":{"type":"string","title":"Id"},"email":{"type":"string","title":"Email"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"role":{"type":"string","title":"Role"},"tenant_id":{"type":"string","title":"Tenant Id"},"is_active":{"type":"boolean","title":"Is Active"},"created_at":{"type":"string","title":"Created At"}},"type":"object","required":["id","email","full_name","role","tenant_id","is_active","created_at"],"title":"UserResponse"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}},"securitySchemes":{"OAuth2PasswordBearer":{"type":"oauth2","flows":{"password":{"scopes":{},"tokenUrl":"/api/v1/auth/login"}}}}}}