7.3 KiB
7.3 KiB
API Contract
Base URL prefix: /api/v1
Primary implementation modules:
backend/app/api/router.pybackend/app/api/routes_auth.pybackend/app/api/routes_health.pybackend/app/api/routes_documents.pybackend/app/api/routes_search.pybackend/app/api/routes_processing_logs.pybackend/app/api/routes_settings.py
Authentication And Authorization
- Authentication is session-based bearer auth.
- Clients authenticate with
POST /auth/loginusing username and password. - Backend issues per-user bearer session tokens and stores hashed session state server-side.
- Login brute-force protection enforces Redis-backed throttle checks keyed by username and source IP.
- Clients send issued tokens as
Authorization: Bearer <token>. GET /auth/mereturns current identity and role.POST /auth/logoutrevokes current session token.
Role matrix:
documents/*:adminorusersearch/*:adminorusersettings/*:adminonlyprocessing/logs/*:adminonly
Ownership rules:
userrole is restricted to its own documents.adminrole can access all documents.
Auth
POST /auth/login- Body model:
AuthLoginRequest - Response model:
AuthLoginResponse - Additional responses:
401for invalid credentials429for throttled login attempts, with stable message andRetry-Afterheader503when the login rate-limiter backend is unavailable
- Body model:
GET /auth/me- Response model:
AuthSessionResponse
- Response model:
POST /auth/logout- Response model:
AuthLogoutResponse
- Response model:
Health
GET /health- Purpose: liveness check
- Response:
{ "status": "ok" }
Documents
Collection and metadata helpers
GET /documents- Query:
offset,limit,include_trashed,only_trashed,path_prefix,path_filter,tag_filter,type_filter,processed_from,processed_to - Response model:
DocumentsListResponse
- Query:
GET /documents/tags- Query:
include_trashed - Response:
{ "tags": string[] }
- Query:
GET /documents/paths- Query:
include_trashed - Response:
{ "paths": string[] }
- Query:
GET /documents/types- Query:
include_trashed - Response:
{ "types": string[] }
- Query:
POST /documents/content-md/export- Body model:
ContentExportRequest - Response: ZIP stream containing one markdown file per matched document
- Limits:
- hard cap on matched document count (
CONTENT_EXPORT_MAX_DOCUMENTS) - hard cap on cumulative markdown bytes (
CONTENT_EXPORT_MAX_TOTAL_BYTES) - per-user rate limit (
CONTENT_EXPORT_RATE_LIMIT_PER_MINUTE)
- hard cap on matched document count (
- Behavior: archive is streamed from spool file instead of unbounded in-memory buffer
- Body model:
Per-document operations
GET /documents/{document_id}- Response model:
DocumentDetailResponse
- Response model:
GET /documents/{document_id}/download- Response: original file bytes
GET /documents/{document_id}/preview- Response: inline preview stream only for safe MIME types
- Behavior: script-capable MIME types are forced to attachment responses with
X-Content-Type-Options: nosniff
GET /documents/{document_id}/thumbnail- Response: generated thumbnail image when available
GET /documents/{document_id}/content-md- Response: extracted markdown content for one document
PATCH /documents/{document_id}- Body model:
DocumentUpdateRequest - Response model:
DocumentResponse
- Body model:
POST /documents/{document_id}/trash- Response model:
DocumentResponse
- Response model:
POST /documents/{document_id}/restore- Response model:
DocumentResponse
- Response model:
DELETE /documents/{document_id}- Behavior: permanent delete, requires document to be trashed first
- Response: deletion counters
POST /documents/{document_id}/reprocess- Response model:
DocumentResponse - Behavior: requeues asynchronous processing task
- Response model:
Upload
POST /documents/upload- Multipart form fields:
files[](required)relative_paths[](optional)logical_path(optional, defaults toInbox)tags(optional CSV)conflict_mode(ask,replace,duplicate)
- Response model:
UploadResponse - Behavior:
ask: returnsconflictsif duplicate checksum is detected for caller-visible documentsreplace: creates new document linked to replaced document idduplicate: creates additional document record- upload
POSTrequest rejected with411whenContent-Lengthis missing OPTIONS /documents/uploadCORS preflight bypasses uploadContent-Lengthenforcement- request rejected with
413when file count, per-file size, or total request size exceeds configured limits
Search
GET /search- Query:
query(min length 2),offset,limit,include_trashed,only_trashed,path_filter,tag_filter,type_filter,processed_from,processed_to - Response model:
SearchResponse - Behavior: PostgreSQL full-text and metadata ranking with role-based ownership scope
Processing Logs
-
Access: admin only
-
GET /processing/logs- Query:
offset,limit,document_id - Response model:
ProcessingLogListResponse limitis capped by runtime configuration- sensitive fields are redacted in API responses
- Query:
-
POST /processing/logs/trim- Query: optional
keep_document_sessions,keep_unbound_entries - Behavior: omitted query values fall back to persisted
/settings.processing_log_retention - query values are capped by runtime retention limits
- Response: trim counters
- Query: optional
-
POST /processing/logs/clear- Response: clear counters
Persistence mode:
- default is metadata-only logging (
PROCESSING_LOG_STORE_MODEL_IO_TEXT=false,PROCESSING_LOG_STORE_PAYLOAD_TEXT=false) - full prompt/response or payload content storage requires explicit operator opt-in
Settings
-
Access: admin only
-
GET /settings- Response model:
AppSettingsResponse - persisted providers with invalid base URLs are ignored during read sanitization; response falls back to remaining valid providers or secure defaults
- provider API keys are exposed only as
api_key_setandapi_key_masked
- Response model:
-
PATCH /settings- Body model:
AppSettingsUpdateRequest - Response model:
AppSettingsResponse - rejects invalid provider base URLs with
400when scheme, allowlist, or network safety checks fail - provider API keys are persisted encrypted at rest (
api_key_encrypted) and plaintext keys are not written to storage
- Body model:
-
POST /settings/reset- Response model:
AppSettingsResponse
- Response model:
-
PATCH /settings/handwriting- Body model:
HandwritingSettingsUpdateRequest - Response model:
AppSettingsResponse
- Body model:
-
GET /settings/handwriting- Response model:
HandwritingSettingsResponse
- Response model:
Schema Families
Auth schemas in backend/app/schemas/auth.py:
AuthLoginRequestAuthUserResponseAuthSessionResponseAuthLoginResponseAuthLogoutResponse
Document schemas in backend/app/schemas/documents.py:
DocumentResponseDocumentDetailResponseDocumentsListResponseUploadConflictUploadResponseDocumentUpdateRequestSearchResponseContentExportRequest
Processing log schemas in backend/app/schemas/processing_logs.py:
ProcessingLogEntryResponseProcessingLogListResponse
Settings schemas in backend/app/schemas/settings.py:
- provider, task, upload-default, display, processing-log retention, predefined paths or tags, handwriting-style, and legacy handwriting models grouped under
AppSettingsResponseandAppSettingsUpdateRequest.