{"openapi":"3.1.0","info":{"title":"Doclenz API","description":"Document intelligence API for text extraction, OCR, vision, audio transcription\nand LLM-based structured enrichment.\n\n**Authentication:** Send your API key in the `X-API-Key` header. Admin endpoints\nadditionally require `X-Admin-Key`.\n\n**Multi-tenancy:** Each API key is bound to a tenant. Files are isolated per\ntenant in MinIO and billing is tracked per tenant via Unidades de Processamento (UPs).\n\n**Content negotiation:** Use `Accept: application/json` (default),\n`Accept: application/x-toon` for 30–50% smaller LLM-friendly payloads, or\n`Accept: text/markdown` for human-readable output.\n","contact":{"name":"Doclenz Support","url":"https://doclenz.dev","email":"suporte@doclenz.dev"},"license":{"name":"Proprietary","url":"https://doclenz.dev/terms"},"version":"1.0.0"},"servers":[{"url":"https://doclenz.dev","description":"Production"},{"url":"http://localhost:8080","description":"Local development"}],"security":[{"ApiKeyAuth":[]}],"tags":[{"name":"Admin","description":"API key management (requires X-Admin-Key)"},{"name":"Jobs","description":"Async job status and cancellation"},{"name":"Extraction","description":"Document extraction endpoints"},{"name":"System","description":"Health, metrics and supported formats"}],"paths":{"/v1/admin/settings/{key}":{"put":{"tags":["Admin"],"summary":"Change a platform setting","description":"Takes effect on the next request, with no restart. Returns 409 when the environment pinned the setting and 400 when the value fails validation.","operationId":"updateSetting","parameters":[{"name":"X-Admin-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSettingRequest"}}},"required":true},"responses":{"400":{"description":"Bad Request","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object"}}}}},"security":[{"AdminKeyAuth":[]}]},"delete":{"tags":["Admin"],"summary":"Reset a setting to its environment value","description":"Removes the platform override so the environment floor applies again.","operationId":"resetSetting","parameters":[{"name":"X-Admin-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object"}}}}},"security":[{"AdminKeyAuth":[]}]}},"/v1/admin/keys/{key}":{"put":{"tags":["Admin"],"summary":"Update an API key","description":"Replaces the record for an existing key.","operationId":"updateKey","parameters":[{"name":"X-Admin-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRecord"}}},"required":true},"responses":{"400":{"description":"Bad Request","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object"}}}}},"security":[{"AdminKeyAuth":[]}]},"delete":{"tags":["Admin"],"summary":"Revoke an API key","description":"Permanently deletes the key. Cannot be undone.","operationId":"deleteKey","parameters":[{"name":"X-Admin-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object"}}}}},"security":[{"AdminKeyAuth":[]}]}},"/v1/extract":{"post":{"tags":["Extraction"],"summary":"Extract content from a document","description":"Uploads a file and extracts its content as text chunks, optionally enriched with\nstructured data via a JSON schema. Supports PDF, DOCX, XLSX, HTML, CSV, images and text.\n\n**Pipelines:** `AUTO` (default, MIME-based routing), `TEXT`, `OCR`, `VISION`, `AUDIO`.\n\n**Async mode:** Pass `async=true` to queue the job; poll `/v1/jobs/{jobId}` for the result\nor configure a webhook in your API key to be notified on completion.\n\n**Content negotiation:** Set the `Accept` header to `application/json` (default),\n`application/x-toon`, or `text/markdown`.\n","operationId":"extract","parameters":[{"name":"async","in":"query","description":"If true, queues the job and returns 202 Accepted with a job_id","required":false,"schema":{"type":"boolean","default":false}},{"name":"pipeline","in":"query","description":"Extraction pipeline. AUTO routes based on MIME type + schema.","required":false,"schema":{"type":"string","default":"AUTO","enum":["AUTO","TEXT","OCR","VISION","AUDIO"]}},{"name":"vision_tier","in":"query","description":"Vision model tier: local, api, or api_premium","required":false,"schema":{"type":"string","default":"local"}},{"name":"vision_mode","in":"query","description":"Vision mode: auto, describe, detect_objects, extract_fields, classify","required":false,"schema":{"type":"string","default":"auto"}},{"name":"confidence_min","in":"query","description":"Minimum confidence for vision fallback","required":false,"schema":{"type":"number","format":"float","default":0.6}},{"name":"extractor","in":"query","description":"Extraction engine for this request: stub, xberg or tika. Requires the 'extractor_choice' feature. Omit to use the configured default.","required":false,"schema":{"type":"string"}},{"name":"X-DocLens-Password","in":"header","description":"Password to decrypt the PDF if it's encrypted","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"Document file to extract (PDF, image, audio, etc.)"},"schema":{"type":"string","description":"Optional JSON schema for LLM Enrichment (structured extraction)"}},"required":["file"]}}}},"responses":{"400":{"description":"Bad Request","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"200":{"description":"Synchronous extraction completed","content":{"*/*":{"schema":{"type":"object"}}}},"202":{"description":"Async job queued","content":{"*/*":{"schema":{"type":"object"}}}},"401":{"description":"Missing or invalid API key","content":{"*/*":{"schema":{"type":"object"}}}},"415":{"description":"DL-001: unsupported MIME type for sync endpoint","content":{"*/*":{"schema":{"type":"object"}}}},"422":{"description":"DL-002 (encrypted PDF) or DL-003 (corrupted file)","content":{"*/*":{"schema":{"type":"object"}}}},"429":{"description":"DL-010: monthly UP quota exceeded","content":{"*/*":{"schema":{"type":"object"}}}}}}},"/v1/admin/keys":{"get":{"tags":["Admin"],"summary":"List all API keys","description":"Keys are returned masked (prefix + last 4 chars).","operationId":"listKeys","parameters":[{"name":"X-Admin-Key","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object"}}}}},"security":[{"AdminKeyAuth":[]}]},"post":{"tags":["Admin"],"summary":"Create a new API key","description":"Generates a key in the format `dlens_prod_{tenant}_{random}`. The full key is returned in the response only once — store it safely, it cannot be retrieved later.","operationId":"createKey","parameters":[{"name":"X-Admin-Key","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKeyRequest"}}},"required":true},"responses":{"400":{"description":"Bad Request","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object"}}}}},"security":[{"AdminKeyAuth":[]}]}},"/v1/metrics":{"get":{"tags":["System"],"summary":"Consumo do tenant autenticado","description":"Total e mês corrente do tenant da X-API-Key usada. A visão de todos os tenants é do administrador, em GET /v1/admin/usage.","operationId":"metrics","responses":{"400":{"description":"Bad Request","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object","additionalProperties":{}}}}}}}},"/v1/metrics/workers":{"get":{"tags":["System"],"summary":"Worker metrics","description":"Returns real-time metrics from the audio and vision workers, published to Redis by each worker process. Returns empty maps if Redis is unavailable or workers are not running.","operationId":"workerMetrics","responses":{"400":{"description":"Bad Request","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object","additionalProperties":{}}}}}}}},"/v1/jobs/{jobId}":{"get":{"tags":["Jobs"],"summary":"Get async job result","description":"Returns the ExtractionResult for a job. Returns 404 if the job does not exist or has already been deleted. While the job is still processing, returns a placeholder result with status=processing.","operationId":"getJob","parameters":[{"name":"jobId","in":"path","description":"UUID of the job returned by /v1/extract?async=true","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"200":{"description":"Job found (completed, processing or failed)","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ExtractionResult"}}}},"404":{"description":"DL-404: job not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ExtractionResult"}}}},"408":{"description":"DL-006: job still processing after 10-minute limit","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ExtractionResult"}}}}}},"delete":{"tags":["Jobs"],"summary":"Delete async job","description":"Cancels a pending job and removes its temporary artifacts from MinIO. Completed jobs can also be deleted to free storage.","operationId":"deleteJob","parameters":[{"name":"jobId","in":"path","description":"UUID of the job to delete","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"204":{"description":"Job deleted"},"404":{"description":"DL-404: job not found"}}}},"/v1/health":{"get":{"tags":["System"],"summary":"Health check","description":"Public endpoint (no auth required). Reports API status and, when Redis is available, the heartbeat status of the audio and vision workers.","operationId":"health","responses":{"400":{"description":"Bad Request","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object","additionalProperties":{}}}}}}}},"/v1/formats":{"get":{"tags":["formats-controller"],"summary":"Formatos aceitos e limites em vigor","description":"Derivado do motor e da configuração ativos, não de uma lista fixa. Consulte antes de decidir entre o caminho síncrono e o assíncrono.","operationId":"formats","responses":{"400":{"description":"Bad Request","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object","additionalProperties":{}}}}}}}},"/v1/admin/usage":{"get":{"tags":["Admin"],"summary":"UP usage per tenant","description":"Consumption joined with each tenant's quota, so a consumer can show usage against the limit. Lives under /v1/admin because /v1/metrics requires a tenant X-API-Key, which an administrator does not have.","operationId":"usage","parameters":[{"name":"X-Admin-Key","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object"}}}}},"security":[{"AdminKeyAuth":[]}]}},"/v1/admin/settings":{"get":{"tags":["Admin"],"summary":"Effective platform settings","description":"Every runtime-configurable setting with its effective value AND the plane it came from (ADR-007 D-1). Settings pinned by the environment are flagged `locked` and cannot be changed here (D-3). `environment_value` is what a reset would restore.","operationId":"listSettings","parameters":[{"name":"X-Admin-Key","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object"}}}}},"security":[{"AdminKeyAuth":[]}]}},"/v1/admin/jobs":{"get":{"tags":["Admin"],"summary":"List recent jobs","description":"Newest first. Only covers the retention window (1 h while processing, 24 h once complete) — this is an operational view, not an audit log.","operationId":"listJobs","parameters":[{"name":"X-Admin-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"tenantId","in":"query","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","format":"int32","default":50}}],"responses":{"400":{"description":"Bad Request","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object"}}}}},"security":[{"AdminKeyAuth":[]}]}},"/v1/admin/audit":{"get":{"tags":["Admin"],"summary":"Administrative audit trail","description":"Newest first. Records who changed what, with previous and new values, both masked (ADR-007 D-7). Note that the actor is the admin key's fingerprint, not a person — the admin authenticates with a shared secret and has no individual identity yet, so the source IP is the only signal separating two holders of the same key.","operationId":"listAudit","parameters":[{"name":"X-Admin-Key","in":"header","required":false,"schema":{"type":"string"}},{"name":"action","in":"query","required":false,"schema":{"type":"string"}},{"name":"tenantId","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","format":"int32","default":50}}],"responses":{"400":{"description":"Bad Request","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object"}}}}},"security":[{"AdminKeyAuth":[]}]}}},"components":{"schemas":{"ApiError":{"properties":{"code":{"type":"string"},"message":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}}},"UpdateSettingRequest":{"properties":{"value":{"type":"string"}}},"ApiKeyRecord":{"properties":{"tenant_id":{"type":"string"},"product":{"type":"string"},"requests_per_minute":{"type":"integer","format":"int32"},"ups_per_day":{"type":"integer","format":"int32"},"ups_per_month":{"type":"integer","format":"int32"},"features":{"type":"array","items":{"type":"string"}},"webhook_url":{"type":"string"},"status":{"type":"string"},"settings":{"type":"object","additionalProperties":{"type":"string"}},"active":{"type":"boolean"}}},"CreateKeyRequest":{"properties":{"tenant_id":{"type":"string"},"product":{"type":"string"},"requests_per_minute":{"type":"integer","format":"int32"},"ups_per_day":{"type":"integer","format":"int32"},"ups_per_month":{"type":"integer","format":"int32"},"features":{"type":"array","items":{"type":"string"}},"webhook_url":{"type":"string"},"settings":{"type":"object","additionalProperties":{"type":"string"}}}},"BillingInfo":{"properties":{"tenant_id":{"type":"string"},"ups_consumed":{"type":"integer","format":"int32"}}},"Chunk":{"properties":{"index":{"type":"integer","format":"int32"},"text":{"type":"string"},"type":{"type":"string"},"page":{"type":"integer","format":"int32"},"tokens":{"type":"integer","format":"int32"},"hash":{"type":"string"}}},"DocumentMetadata":{"properties":{"language":{"type":"string"},"language_confidence":{"type":"number","format":"double"},"created_at":{"type":"string","format":"date-time"},"author":{"type":"string"}}},"ExtractedTable":{"properties":{"page":{"type":"integer","format":"int32"},"rows":{"type":"array","items":{"type":"array","items":{"type":"string"}}},"markdown":{"type":"string"}}},"ExtractionResult":{"properties":{"job_id":{"type":"string"},"status":{"type":"string"},"source":{"$ref":"#/components/schemas/SourceInfo"},"metadata":{"$ref":"#/components/schemas/DocumentMetadata"},"chunks":{"type":"array","items":{"$ref":"#/components/schemas/Chunk"}},"structured":{"$ref":"#/components/schemas/JsonNode"},"billing":{"$ref":"#/components/schemas/BillingInfo"},"processing_ms":{"type":"integer","format":"int64"},"error_code":{"type":"string"},"error_message":{"type":"string"},"tables":{"type":"array","items":{"$ref":"#/components/schemas/ExtractedTable"}}}},"JsonNode":{"properties":{"empty":{"type":"boolean"},"array":{"type":"boolean"},"null":{"type":"boolean"},"object":{"type":"boolean"},"float":{"type":"boolean"},"container":{"type":"boolean"},"value_node":{"type":"boolean"},"missing_node":{"type":"boolean"},"node_type":{"type":"string","enum":["ARRAY","BINARY","BOOLEAN","MISSING","NULL","NUMBER","OBJECT","POJO","STRING"]},"string":{"type":"boolean"},"integral_number":{"type":"boolean"},"pojo":{"type":"boolean"},"floating_point_number":{"type":"boolean"},"short":{"type":"boolean"},"int":{"type":"boolean"},"long":{"type":"boolean"},"double":{"type":"boolean"},"big_decimal":{"type":"boolean"},"big_integer":{"type":"boolean"},"textual":{"type":"boolean","deprecated":true},"boolean":{"type":"boolean"},"binary":{"type":"boolean"},"number":{"type":"boolean"},"embedded_value":{"type":"boolean"}}},"SourceInfo":{"properties":{"filename":{"type":"string"},"mime_type":{"type":"string"},"size_bytes":{"type":"integer","format":"int64"},"hash_sha256":{"type":"string"},"pages":{"type":"integer","format":"int32"}}}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","description":"Tenant API key issued via the admin portal.","name":"X-API-Key","in":"header"},"AdminKeyAuth":{"type":"apiKey","description":"Admin key for managing API keys. Separate from regular API keys.","name":"X-Admin-Key","in":"header"}}}}