{"openapi":"3.1.0","info":{"title":"AI-CORE V2 - API Gateway","description":"\nAPI-key authenticated gateway: the single public front door for external\nconsumers (Mindy, aipen, ...) to AI-CORE job services.\n\n**Start here: the [integration guide](.)** — authentication, sync vs async\nflows, polling, callbacks, error model.\n\n## Authentication\n\nEvery call needs `X-Api-Key` plus the identity headers `X-Tenant` /\n`X-User` (logged for usage accounting). The consumer system is derived\nfrom the API key itself.\n\n## Sync vs async\n\nJob routes accept the service's native request plus an optional `callback_url`:\n\n- `callback_url` set → immediate `202 {\"job_id\"}`; the terminal result is\n  POSTed to the callback (always delivered — errors and timeouts included).\n- `callback_url` absent, bounded task (metadata_extraction, summarization) →\n  the gateway parks the request up to 180 s and returns `200` with the native\n  response, degrading to `202` + `Location` poll header under load. Sync\n  callers MUST handle the `202` case.\n- `callback_url` absent, long task (pdf_anonymization) → fire-and-poll:\n  immediate `202` + `Location`, poll for the result (available 600 s after\n  completion).\n\n## The result payload\n\nA callback delivers a `JobCallback` envelope: `job_id`, `service`, `task`,\n`status`, then `response` when `status=ok` or `error` / `error_code` when\n`status=error`. Answer 2xx — a failed delivery is retried, then dead-lettered.\nEach route documents its own `response` shape under **Callbacks**, including the\nasync-only routes that never return it inline. A poll returns the same envelope.\n","contact":{"name":"Maggioli R&D - AI","email":"alessandro.neri@maggioli.it"},"version":"4.6.5"},"paths":{"/health":{"get":{"tags":["health"],"summary":"Health Check","description":"Health check endpoint for liveness probes","operationId":"health_check_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/pdf_anonymization":{"post":{"tags":["pdf_anonymization"],"summary":"Async Pdf Anonymization","description":"Async-only: pdf-anonymization jobs can run up to ~900 s, far beyond the sync-emulation park budget, so this always returns 202 {job_id} immediately. With a callback_url the result is POSTed there when done; without one the call is fire-and-poll — follow the Location header to GET /api/v1/jobs/{job_id}/result (the terminal result stays available 600 s after completion, so prefer a callback for unattended integrations).","operationId":"async_pdf_anonymization_api_v1_pdf_anonymization_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestPdfAnonymization"},"examples":{"async_callback":{"summary":"Submit with callback","description":"Returns 202 {job_id}; the anonymized PDF (base64) is POSTed to callback_url when done (jobs can run ~900 s). Exactly one of doc_reference / doc_content must be set.","value":{"doc_reference":"https://example.org/documents/referto.pdf","structured_detection":true,"embedded_files":"anonymize","callback_url":"https://consumer.example.org/webhooks/aicore"}},"fire_and_poll":{"summary":"Submit without callback (poll)","description":"Returns 202 {job_id} + Location; poll GET /api/v1/jobs/{job_id}/result until 200 (the result expires 600 s after completion).","value":{"doc_reference":"https://example.org/documents/referto.pdf"}}}}},"required":true},"responses":{"202":{"description":"Job accepted. With a callback_url the terminal result is POSTed there; without one, poll the Location header URL.","headers":{"Location":{"description":"Poll URL (GET /api/v1/jobs/{job_id}/result) — present only when the job was submitted without callback_url","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseJobAccepted"}}}},"400":{"description":"Missing mandatory identity headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for this service.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"502":{"description":"Job failed without a business status, or the job could not be enqueued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"503":{"description":"The job plane is unavailable on this gateway.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"callbacks":{"pdf_anonymization_result":{"{$request.body#/callback_url}":{"post":{"summary":"pdf_anonymization result to callback_url","description":"Sent once the job terminates, errors and timeouts included. Answer 2xx; a failed delivery is retried, then dead-lettered.","operationId":"pdf_anonymization_result__request_body__callback_url__post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobCallbackPdfAnonymization"}}},"required":true},"responses":{"200":{"description":"Consumer acknowledged the result.","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}},"security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}]}},"/api/v1/metadata_extraction":{"post":{"tags":["metadata_extraction"],"summary":"Async Metadata Extraction","description":"Submit a metadata-extraction job. With a callback_url the call is async: 202 {job_id} immediately and the result is POSTed to the callback. Without one (the pre-2.0 sync contract, restored in 2.1.0) the gateway parks the request up to sync_wait_seconds and returns the native response; if the budget expires it degrades to 202 {job_id} and the caller polls GET /api/v1/jobs/{job_id}/result. Note: the gateway caps parked waiters per replica and degrades the overflow to 202.","operationId":"async_metadata_extraction_api_v1_metadata_extraction_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestMetadataExtraction"},"examples":{"sync":{"summary":"Sync-emulation (no callback_url)","description":"Parks up to 180 s and returns 200 with the native response; may degrade to 202 + Location. Exactly one of doc_reference / doc_content must be set.","value":{"doc_reference":"https://example.org/documents/decreto.pdf","document_kind":"auto","max_pages":10}},"async_callback":{"summary":"Async with callback","description":"Returns 202 {job_id} immediately; the terminal result is POSTed to callback_url. doc_content carries the PDF as base64 (truncated here).","value":{"doc_content":"JVBERi0xLjcKJb/3ov4KMSAwIG9iago8PC...","document_kind":"jurisprudence","language":"it","callback_url":"https://consumer.example.org/webhooks/aicore"}}}}},"required":true},"responses":{"202":{"description":"Job accepted. With a callback_url the terminal result is POSTed there; without one, poll the Location header URL (also the sync-emulation degrade path on budget expiry/overflow).","headers":{"Location":{"description":"Poll URL (GET /api/v1/jobs/{job_id}/result) — present only when the job was submitted without callback_url","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseJobAccepted"}}}},"400":{"description":"Missing mandatory identity headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for this service.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"502":{"description":"Job failed without a business status, or the job could not be enqueued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"503":{"description":"The job plane is unavailable on this gateway.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"200":{"description":"Sync-emulation result (no callback_url): the service's native response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseMetadataExtraction"}}}},"504":{"description":"The job itself timed out (business timeout, error_code parity).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"callbacks":{"metadata_extraction_result":{"{$request.body#/callback_url}":{"post":{"summary":"metadata_extraction result to callback_url","description":"Sent once the job terminates, errors and timeouts included. Answer 2xx; a failed delivery is retried, then dead-lettered.","operationId":"metadata_extraction_result__request_body__callback_url__post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobCallbackMetadataExtraction"}}},"required":true},"responses":{"200":{"description":"Consumer acknowledged the result.","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}},"security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}]}},"/api/v1/summarization":{"post":{"tags":["summarization"],"summary":"Async Summarization","description":"Summarize a document already ingested into the platform, by `id_doc`. Three shapes: `concise` and `detailed` run a map-reduce over the whole document; `bullet_list` extracts per-topic points and reduces them to `nr_points` bullets (`nr_points` is required for `bullet_list` and rejected for the other two). With a callback_url the call is async: 202 {job_id} immediately and the result is POSTed to the callback. Without one the gateway parks the request up to sync_wait_seconds and returns the native response; a long document can exceed that budget, in which case it degrades to 202 {job_id} and the caller polls GET /api/v1/jobs/{job_id}/result. Note: the gateway caps parked waiters per replica and degrades the overflow to 202.","operationId":"async_summarization_api_v1_summarization_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestSummarization"},"examples":{"concise":{"summary":"Concise summary (sync-emulation)","description":"Map-reduce over the whole document. Parks up to 180 s and returns 200 with the native response; a long document may degrade to 202 + Location.","value":{"id_doc":12345,"language":"it","type":"concise"}},"bullet_list":{"summary":"Bulleted list, N points","description":"Extracts points per topic and reduces them to nr_points bullets. nr_points is mandatory for this type only.","value":{"id_doc":12345,"language":"it","type":"bullet_list","nr_points":5}},"async_callback":{"summary":"Detailed summary, async with callback","description":"Returns 202 {job_id} immediately; the terminal result is POSTed to callback_url. Preferred for long documents.","value":{"id_doc":12345,"language":"en","type":"detailed","callback_url":"https://consumer.example.org/webhooks/aicore"}}}}},"required":true},"responses":{"202":{"description":"Job accepted. With a callback_url the terminal result is POSTed there; without one, poll the Location header URL (also the sync-emulation degrade path on budget expiry/overflow).","headers":{"Location":{"description":"Poll URL (GET /api/v1/jobs/{job_id}/result) — present only when the job was submitted without callback_url","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseJobAccepted"}}}},"400":{"description":"Missing mandatory identity headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for this service.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"502":{"description":"Job failed without a business status, or the job could not be enqueued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"503":{"description":"The job plane is unavailable on this gateway.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"200":{"description":"Sync-emulation result (no callback_url): the service's native response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseSummarization"}}}},"504":{"description":"The job itself timed out (business timeout, error_code parity).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"callbacks":{"summarization_result":{"{$request.body#/callback_url}":{"post":{"summary":"summarization result to callback_url","description":"Sent once the job terminates, errors and timeouts included. Answer 2xx; a failed delivery is retried, then dead-lettered.","operationId":"summarization_result__request_body__callback_url__post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobCallbackSummarization"}}},"required":true},"responses":{"200":{"description":"Consumer acknowledged the result.","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}},"security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}]}},"/api/v1/quiz_generation":{"post":{"tags":["quiz_generation"],"summary":"Async Quiz Generation","description":"Generate a multiple-choice quiz per topic over a document already ingested into the platform, by `id_doc`. Each requested topic is retrieved against the document independently, so a topic the document does not cover comes back as an empty list rather than being omitted. `nr_answers` counts the correct answer, so `nr_answers: 4` asks for three wrong ones. With a callback_url the call is async: 202 {job_id} immediately and the result is POSTed to the callback. Without one the gateway parks the request up to sync_wait_seconds and returns the native response; if the budget expires it degrades to 202 {job_id} and the caller polls GET /api/v1/jobs/{job_id}/result — likely here, since the declared processing budget is 600 s.","operationId":"async_quiz_generation_api_v1_quiz_generation_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestQuizGeneration"},"examples":{"sync":{"summary":"Sync-emulation (no callback_url)","description":"Parks up to 180 s and returns 200 with the native response; degrades to 202 + Location when the quiz takes longer, which a long topic list will.","value":{"id_doc":13911,"language":"it","topics":["Registrazione alla piattaforma","Recupero credenziali"],"max_nr_quiz":3,"nr_answers":4}},"async_callback":{"summary":"Async with callback","description":"Returns 202 {job_id} immediately; the terminal result is POSTed to callback_url. Preferred for more than a handful of topics.","value":{"id_doc":13911,"language":"it","topics":["Obblighi del fornitore","Termini di pagamento","Penali"],"max_nr_quiz":5,"nr_answers":4,"callback_url":"https://consumer.example.org/webhooks/aicore"}}}}},"required":true},"responses":{"202":{"description":"Job accepted. With a callback_url the terminal result is POSTed there; without one, poll the Location header URL (also the sync-emulation degrade path on budget expiry/overflow).","headers":{"Location":{"description":"Poll URL (GET /api/v1/jobs/{job_id}/result) — present only when the job was submitted without callback_url","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseJobAccepted"}}}},"400":{"description":"Missing mandatory identity headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for this service.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"502":{"description":"Job failed without a business status, or the job could not be enqueued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"503":{"description":"The job plane is unavailable on this gateway.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"200":{"description":"Sync-emulation result (no callback_url): the service's native response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseQuizGeneration"}}}},"504":{"description":"The job itself timed out (business timeout, error_code parity).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"callbacks":{"quiz_generation_result":{"{$request.body#/callback_url}":{"post":{"summary":"quiz_generation result to callback_url","description":"Sent once the job terminates, errors and timeouts included. Answer 2xx; a failed delivery is retried, then dead-lettered.","operationId":"quiz_generation_result__request_body__callback_url__post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobCallbackQuizGeneration"}}},"required":true},"responses":{"200":{"description":"Consumer acknowledged the result.","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}},"security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}]}},"/api/v1/faqs_generation":{"post":{"tags":["faqs_generation"],"summary":"Async Faqs Generation","description":"Generate FAQ pairs over a document already ingested into the platform, by `id_doc`. The document is chunked and each chunk yields candidate pairs, which are then distilled down to at most `max_nr_faqs`. With a callback_url the call is async: 202 {job_id} immediately and the result is POSTed to the callback. Without one the gateway parks the request up to sync_wait_seconds and returns the native response, degrading to 202 {job_id} plus a poll Location if the budget expires.","operationId":"async_faqs_generation_api_v1_faqs_generation_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestFaqsGeneration"}}},"required":true},"responses":{"202":{"description":"Job accepted. With a callback_url the terminal result is POSTed there; without one, poll the Location header URL (also the sync-emulation degrade path on budget expiry/overflow).","headers":{"Location":{"description":"Poll URL (GET /api/v1/jobs/{job_id}/result) — present only when the job was submitted without callback_url","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseJobAccepted"}}}},"400":{"description":"Missing mandatory identity headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for this service.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"502":{"description":"Job failed without a business status, or the job could not be enqueued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"503":{"description":"The job plane is unavailable on this gateway.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"200":{"description":"Sync-emulation result (no callback_url): the service's native response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseFaqsGeneration"}}}},"504":{"description":"The job itself timed out (business timeout, error_code parity).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"callbacks":{"faqs_generation_result":{"{$request.body#/callback_url}":{"post":{"summary":"faqs_generation result to callback_url","description":"Sent once the job terminates, errors and timeouts included. Answer 2xx; a failed delivery is retried, then dead-lettered.","operationId":"faqs_generation_result__request_body__callback_url__post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobCallbackFaqsGeneration"}}},"required":true},"responses":{"200":{"description":"Consumer acknowledged the result.","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}},"security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}]}},"/api/v1/question_answering":{"post":{"tags":["question_answering"],"summary":"Async Question Answering","description":"Answer a question against a document already ingested into the platform, by `id_doc`. Retrieval is scoped to that document; when the answer language differs from the question's, a second call translates it. With a callback_url the call is async: 202 {job_id} immediately and the result is POSTed to the callback. Without one the gateway parks the request up to sync_wait_seconds and returns the native response, degrading to 202 {job_id} plus a poll Location if the budget expires.","operationId":"async_question_answering_api_v1_question_answering_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestQuestionAnswering"}}},"required":true},"responses":{"202":{"description":"Job accepted. With a callback_url the terminal result is POSTed there; without one, poll the Location header URL (also the sync-emulation degrade path on budget expiry/overflow).","headers":{"Location":{"description":"Poll URL (GET /api/v1/jobs/{job_id}/result) — present only when the job was submitted without callback_url","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseJobAccepted"}}}},"400":{"description":"Missing mandatory identity headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for this service.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"502":{"description":"Job failed without a business status, or the job could not be enqueued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"503":{"description":"The job plane is unavailable on this gateway.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"200":{"description":"Sync-emulation result (no callback_url): the service's native response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseQuestionAnswering"}}}},"504":{"description":"The job itself timed out (business timeout, error_code parity).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"callbacks":{"question_answering_result":{"{$request.body#/callback_url}":{"post":{"summary":"question_answering result to callback_url","description":"Sent once the job terminates, errors and timeouts included. Answer 2xx; a failed delivery is retried, then dead-lettered.","operationId":"question_answering_result__request_body__callback_url__post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobCallbackQuestionAnswering"}}},"required":true},"responses":{"200":{"description":"Consumer acknowledged the result.","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}},"security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}]}},"/api/v1/book_review":{"post":{"tags":["book_review"],"summary":"Async Book Review","description":"Produce a structured review of a document already ingested into the platform, by `id_doc` — an overview, per-topic descriptions and a summary — via a map-reduce over the whole document followed by a rewrite pass. With a callback_url the call is async: 202 {job_id} immediately and the result is POSTed to the callback. Without one the gateway parks the request up to sync_wait_seconds and returns the native response, degrading to 202 {job_id} plus a poll Location if the budget expires.","operationId":"async_book_review_api_v1_book_review_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestBookReview"}}},"required":true},"responses":{"202":{"description":"Job accepted. With a callback_url the terminal result is POSTed there; without one, poll the Location header URL (also the sync-emulation degrade path on budget expiry/overflow).","headers":{"Location":{"description":"Poll URL (GET /api/v1/jobs/{job_id}/result) — present only when the job was submitted without callback_url","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseJobAccepted"}}}},"400":{"description":"Missing mandatory identity headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for this service.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"502":{"description":"Job failed without a business status, or the job could not be enqueued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"503":{"description":"The job plane is unavailable on this gateway.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"200":{"description":"Sync-emulation result (no callback_url): the service's native response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseBookReview"}}}},"504":{"description":"The job itself timed out (business timeout, error_code parity).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"callbacks":{"book_review_result":{"{$request.body#/callback_url}":{"post":{"summary":"book_review result to callback_url","description":"Sent once the job terminates, errors and timeouts included. Answer 2xx; a failed delivery is retried, then dead-lettered.","operationId":"book_review_result__request_body__callback_url__post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobCallbackBookReview"}}},"required":true},"responses":{"200":{"description":"Consumer acknowledged the result.","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}},"security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}]}},"/api/v1/article_generation":{"post":{"tags":["article_generation"],"summary":"Async Article Generation","description":"Generate a newspaper-style article on `topic` from a document already ingested into the platform, by `id_doc`. Context is retrieved for the topic, map-reduced, then rewritten into the final article. With a callback_url the call is async: 202 {job_id} immediately and the result is POSTed to the callback. Without one the gateway parks the request up to sync_wait_seconds and returns the native response, degrading to 202 {job_id} plus a poll Location if the budget expires.","operationId":"async_article_generation_api_v1_article_generation_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestArticleGeneration"}}},"required":true},"responses":{"202":{"description":"Job accepted. With a callback_url the terminal result is POSTed there; without one, poll the Location header URL (also the sync-emulation degrade path on budget expiry/overflow).","headers":{"Location":{"description":"Poll URL (GET /api/v1/jobs/{job_id}/result) — present only when the job was submitted without callback_url","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseJobAccepted"}}}},"400":{"description":"Missing mandatory identity headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for this service.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"502":{"description":"Job failed without a business status, or the job could not be enqueued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"503":{"description":"The job plane is unavailable on this gateway.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"200":{"description":"Sync-emulation result (no callback_url): the service's native response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseArticleGeneration"}}}},"504":{"description":"The job itself timed out (business timeout, error_code parity).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"callbacks":{"article_generation_result":{"{$request.body#/callback_url}":{"post":{"summary":"article_generation result to callback_url","description":"Sent once the job terminates, errors and timeouts included. Answer 2xx; a failed delivery is retried, then dead-lettered.","operationId":"article_generation_result__request_body__callback_url__post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobCallbackArticleGeneration"}}},"required":true},"responses":{"200":{"description":"Consumer acknowledged the result.","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}},"security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}]}},"/api/v1/ai_overview":{"post":{"tags":["ai_overview"],"summary":"Async Ai Overview","description":"Generate a concise overview of a document already ingested into the platform, by `id_doc`. The whole document is read and summarised in one pass, so the response carries no sources. With a callback_url the call is async: 202 {job_id} immediately and the result is POSTed to the callback. Without one the gateway parks the request up to sync_wait_seconds and returns the native response, degrading to 202 {job_id} plus a poll Location if the budget expires.","operationId":"async_ai_overview_api_v1_ai_overview_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestAiOverviewByIdDoc"}}},"required":true},"responses":{"202":{"description":"Job accepted. With a callback_url the terminal result is POSTed there; without one, poll the Location header URL (also the sync-emulation degrade path on budget expiry/overflow).","headers":{"Location":{"description":"Poll URL (GET /api/v1/jobs/{job_id}/result) — present only when the job was submitted without callback_url","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseJobAccepted"}}}},"400":{"description":"Missing mandatory identity headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for this service.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"502":{"description":"Job failed without a business status, or the job could not be enqueued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"503":{"description":"The job plane is unavailable on this gateway.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"200":{"description":"Sync-emulation result (no callback_url): the service's native response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseAiOverview"}}}},"504":{"description":"The job itself timed out (business timeout, error_code parity).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"callbacks":{"ai_overview_result":{"{$request.body#/callback_url}":{"post":{"summary":"ai_overview result to callback_url","description":"Sent once the job terminates, errors and timeouts included. Answer 2xx; a failed delivery is retried, then dead-lettered.","operationId":"ai_overview_result__request_body__callback_url__post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobCallbackAiOverview"}}},"required":true},"responses":{"200":{"description":"Consumer acknowledged the result.","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}},"security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}]}},"/api/v1/ai_overview_rag":{"post":{"tags":["ai_overview_rag"],"summary":"Async Ai Overview Rag","description":"Answer a query against a set of documents already ingested into the platform, by `id_docs`, returning the answer together with the source chunks it was grounded in. `query_augmentation` optionally rewrites the query in a sector flavour before retrieval; the rewriter also rejects malicious input. With a callback_url the call is async: 202 {job_id} immediately and the result is POSTed to the callback. Without one the gateway parks the request up to sync_wait_seconds and returns the native response, degrading to 202 {job_id} plus a poll Location if the budget expires.","operationId":"async_ai_overview_rag_api_v1_ai_overview_rag_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestAiOverviewByQueryAndIdDocsList"}}},"required":true},"responses":{"202":{"description":"Job accepted. With a callback_url the terminal result is POSTed there; without one, poll the Location header URL (also the sync-emulation degrade path on budget expiry/overflow).","headers":{"Location":{"description":"Poll URL (GET /api/v1/jobs/{job_id}/result) — present only when the job was submitted without callback_url","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseJobAccepted"}}}},"400":{"description":"Missing mandatory identity headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for this service.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"502":{"description":"Job failed without a business status, or the job could not be enqueued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"503":{"description":"The job plane is unavailable on this gateway.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"200":{"description":"Sync-emulation result (no callback_url): the service's native response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseAiOverview"}}}},"504":{"description":"The job itself timed out (business timeout, error_code parity).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"callbacks":{"ai_overview_rag_result":{"{$request.body#/callback_url}":{"post":{"summary":"ai_overview_rag result to callback_url","description":"Sent once the job terminates, errors and timeouts included. Answer 2xx; a failed delivery is retried, then dead-lettered.","operationId":"ai_overview_rag_result__request_body__callback_url__post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobCallbackAiOverviewRag"}}},"required":true},"responses":{"200":{"description":"Consumer acknowledged the result.","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}},"security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}]}},"/api/v1/speech_to_text":{"post":{"tags":["speech_to_text"],"summary":"Async Speech To Text","description":"Async-only: transcription runs for as long as the audio takes (an hour-long recording is a normal input), and the worker scales to zero between jobs, so this always returns 202 {job_id} immediately. With a callback_url the result is POSTed there when done; without one the call is fire-and-poll — follow the Location header to GET /api/v1/jobs/{job_id}/result (the terminal result stays available 600 s after completion, so prefer a callback for unattended integrations).\n\n`file_reference` is required and is the only input shape — inline base64 was removed because an orchestrated request is persisted verbatim to the job stream. Pass a short-lived signed URL (Azure SAS or equivalent): it is a credential in a request body, so it is redacted from traces but still sits in the request stream for that stream's retention. Short expiry is the real control.\n\n`language` must be a complete BCP-47 locale — `it-IT`, not `it`. A bare code is a 422 here, before any job is created. The worker used to expand `it`/`en`/`es` and pass everything else through, so three codes worked and every other language failed inside a running job; that mapping is gone and the rule is now one rule. The check is on shape only, so a well-formed locale the configured engine does not serve still fails the job — `GET /api/v1/languages` was retired, so the engine's own list is the authority.\n\n`rag_augmentation` runs a second pass over the finished transcript: one LLM call that rewrites it into structured Markdown (headings, bolded entities, filler removed) for ingestion into a RAG index. **It retrieves nothing** — it prepares text *for* a RAG system rather than drawing on one, so it adds no facts the audio did not contain. It defaults to `true`, so every job pays for that LLM call unless you send `false`, which returns the raw transcription only. It is best-effort: if the LLM fails the transcription is still delivered and `rag_augmented_txt` is absent — so an absent value does not tell you whether the augmentation failed or was never requested. The prompt is written for webinar recordings and is not per-request configurable.","operationId":"async_speech_to_text_api_v1_speech_to_text_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestSttOffline"},"examples":{"async_callback":{"summary":"Submit with callback","description":"Returns 202 {job_id}; the transcription is POSTed to callback_url when done. Prefer this for anything unattended — the poll window is only 600 s after completion.","value":{"language":"it-IT","file_reference":"https://acct.blob.core.windows.net/audio/consiglio.wav?sv=2024-11-04&se=...&sig=...","rag_augmentation":true,"callback_url":"https://consumer.example.org/webhooks/aicore"}},"fire_and_poll":{"summary":"Submit without callback (poll)","description":"Returns 202 {job_id} + Location; poll GET /api/v1/jobs/{job_id}/result until 200. This example opts out of the LLM rewrite, so the result carries `text` and no `rag_augmented_txt`.","value":{"language":"it-IT","file_reference":"https://acct.blob.core.windows.net/audio/consiglio.wav?sv=2024-11-04&se=...&sig=...","rag_augmentation":false}}}}},"required":true},"responses":{"202":{"description":"Job accepted. With a callback_url the terminal result is POSTed there; without one, poll the Location header URL.","headers":{"Location":{"description":"Poll URL (GET /api/v1/jobs/{job_id}/result) — present only when the job was submitted without callback_url","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseJobAccepted"}}}},"400":{"description":"Missing mandatory identity headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for this service.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"502":{"description":"Job failed without a business status, or the job could not be enqueued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"503":{"description":"The job plane is unavailable on this gateway.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"callbacks":{"speech_to_text_result":{"{$request.body#/callback_url}":{"post":{"summary":"speech_to_text result to callback_url","description":"Sent once the job terminates, errors and timeouts included. Answer 2xx; a failed delivery is retried, then dead-lettered.","operationId":"speech_to_text_result__request_body__callback_url__post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobCallbackSpeechToText"}}},"required":true},"responses":{"200":{"description":"Consumer acknowledged the result.","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}},"security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}]}},"/api/v1/text_to_speech":{"post":{"tags":["text_to_speech"],"summary":"Async Text To Speech","description":"Synthesize text to speech. Bounded work (seconds), so a request without a callback_url is parked and normally returns 200 with the audio inline; on a cold start it can exceed the ~180 s park budget and degrade to 202 {job_id} + Location, which is then polled at GET /api/v1/jobs/{job_id}/result. With a callback_url the call returns 202 immediately and the result is POSTed when ready.\n\nThe audio comes back **inline**, base64, in `audio_content` — not as a URL. It is capped by service configuration (25 MB encoded by default, ~17 minutes of speech at 192 kbps); above the cap the job fails with 413 and the text should be split into shorter requests.\n\n`voice` is optional and leaves the provider default when omitted. When set, both halves are required: the provider takes them joined as `<locale>-<name>` (e.g. `it-IT-ElsaNeural`), so a half-specified voice selects nothing.\n\n**Language and voice catalog** — there is deliberately no catalog endpoint; the lists are quasi-static, so use the provider's published catalog. Which provider serves a deployment is configuration — check both or ask your platform contact:\n\n- [Azure: language and voice support](https://learn.microsoft.com/azure/ai-services/speech-service/language-support?tabs=tts)\n- [Google: supported voices and types](https://cloud.google.com/text-to-speech/docs/voices)\n\nBoth pages publish full voice names (`it-IT-ElsaNeural`, `it-IT-Wavenet-A`): split on the BCP-47 locale prefix to fill `voice` — e.g. `{\"locale\": \"it-IT\", \"name\": \"ElsaNeural\"}`. An unknown voice fails with the provider's own message.","operationId":"async_text_to_speech_api_v1_text_to_speech_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestTtsOffline"},"examples":{"sync":{"summary":"Synthesize and wait (no callback)","description":"Parked up to ~180 s and normally answers 200 with base64 audio in `audio_content`. Best for short prompts.","value":{"text":"Buongiorno, la seduta del consiglio comunale e aperta.","voice":{"name":"ElsaNeural","locale":"it-IT"}}},"async_callback":{"summary":"Synthesize with callback","description":"Returns 202 {job_id} immediately; the result is POSTed to callback_url. Prefer this for long texts, where the park would expire anyway.","value":{"text":"Testo lungo da sintetizzare...","voice":{"name":"DiegoNeural","locale":"it-IT"},"callback_url":"https://consumer.example.org/webhooks/aicore"}},"provider_default_voice":{"summary":"Let the provider pick the voice","description":"Omit `voice` entirely to use the provider's configured default.","value":{"text":"Hello world."}}}}},"required":true},"responses":{"202":{"description":"Job accepted. With a callback_url the terminal result is POSTed there; without one, poll the Location header URL (also the sync-emulation degrade path on budget expiry/overflow).","headers":{"Location":{"description":"Poll URL (GET /api/v1/jobs/{job_id}/result) — present only when the job was submitted without callback_url","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseJobAccepted"}}}},"400":{"description":"Missing mandatory identity headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for this service.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"502":{"description":"Job failed without a business status, or the job could not be enqueued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"503":{"description":"The job plane is unavailable on this gateway.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"200":{"description":"Sync-emulation result (no callback_url): the service's native response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseTtsOffline"}}}},"504":{"description":"The job itself timed out (business timeout, error_code parity).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"callbacks":{"text_to_speech_result":{"{$request.body#/callback_url}":{"post":{"summary":"text_to_speech result to callback_url","description":"Sent once the job terminates, errors and timeouts included. Answer 2xx; a failed delivery is retried, then dead-lettered.","operationId":"text_to_speech_result__request_body__callback_url__post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobCallbackTextToSpeech"}}},"required":true},"responses":{"200":{"description":"Consumer acknowledged the result.","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}},"security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}]}},"/api/v1/document_ingestion":{"post":{"tags":["document_ingestion"],"summary":"Async Document Ingestion","description":"Ingest one document into storage: extract its text page by page, merge the metadata you already know with what extraction finds, and store it under `id_doc`. This is the producer the `id_doc`-shaped tasks (topics extraction, bullet-list summarization) read back.\n\nAsync-only: the worker scales to zero and extraction is unbounded in the size of the document, so this always returns 202 {job_id} immediately. With a callback_url the result is POSTed there when done; without one the call is fire-and-poll — follow the Location header to GET /api/v1/jobs/{job_id}/result (the terminal result stays available 600 s after completion, so prefer a callback for unattended integrations).\n\n`id_doc` is caller-chosen and its namespace is currently **shared, not per-tenant**: storage keys on `id_doc` alone. Pick ids that cannot collide with another consumer's until that is fixed — a collision answers `already_ingested: true` for a document you have never seen, or destroys it under `overwrite`.\n\nExactly one of `doc_reference` and `doc_content` is required. Prefer `doc_reference` with a short-lived signed URL: an orchestrated request is persisted verbatim to the job stream, so a base64 body sits there for that stream's retention. Omit `file_type` to let extraction detect it.\n\nIngestion is idempotent by default — an `id_doc` that already exists comes back `already_ingested: true` with nothing re-ingested. Set `overwrite: true` to replace it.","operationId":"async_document_ingestion_api_v1_document_ingestion_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestDocumentIngestion"},"examples":{"async_callback":{"summary":"Ingest a referenced document with callback","description":"Returns 202 {job_id}; the ingestion result is POSTed to callback_url when done. Prefer this for anything unattended — the poll window is only 600 s after completion.","value":{"id_doc":481523,"doc_reference":"https://acct.blob.core.windows.net/docs/delibera.pdf?sv=2024-11-04&se=...&sig=...","file_type":"pdf","metadata":{"title":"Delibera di Giunta n. 42","document_type":"delibera","authors":["Comune di Rimini"]},"callback_url":"https://consumer.example.org/webhooks/aicore"}},"fire_and_poll":{"summary":"Re-ingest inline content (poll)","description":"Returns 202 {job_id} + Location; poll GET /api/v1/jobs/{job_id}/result until 200. `overwrite` replaces a document already stored under this `id_doc` instead of answering `already_ingested: true`.","value":{"id_doc":481523,"doc_content":"JVBERi0xLjQKJcfsj6IK...","file_type":"pdf","overwrite":true}}}}},"required":true},"responses":{"202":{"description":"Job accepted. With a callback_url the terminal result is POSTed there; without one, poll the Location header URL.","headers":{"Location":{"description":"Poll URL (GET /api/v1/jobs/{job_id}/result) — present only when the job was submitted without callback_url","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseJobAccepted"}}}},"400":{"description":"Missing mandatory identity headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for this service.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"502":{"description":"Job failed without a business status, or the job could not be enqueued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"503":{"description":"The job plane is unavailable on this gateway.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"callbacks":{"document_ingestion_result":{"{$request.body#/callback_url}":{"post":{"summary":"document_ingestion result to callback_url","description":"Sent once the job terminates, errors and timeouts included. Answer 2xx; a failed delivery is retried, then dead-lettered.","operationId":"document_ingestion_result__request_body__callback_url__post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobCallbackDocumentIngestion"}}},"required":true},"responses":{"200":{"description":"Consumer acknowledged the result.","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}},"security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}]}},"/api/v1/topics_extraction":{"post":{"tags":["topics_extraction"],"summary":"Async Topics Extraction","description":"Extract the topics of one document: chunk it, embed every chunk, cluster, and name the clusters. Whole-book inputs are normal, and the worker runs one job per pod.\n\nAsync-only: the worker scales to zero and clustering a long document outruns the sync budget on its own, so this always returns 202 {job_id} immediately. With a callback_url the result is POSTed there when done; without one the call is fire-and-poll — follow the Location header to GET /api/v1/jobs/{job_id}/result (the terminal result stays available 600 s after completion).\n\nExactly one of `id_doc`, `doc_reference` and `doc_content` is required — topics are available before a document is ever ingested.\n\n`persist` (default true) writes the topics back onto the stored document and **requires `id_doc`**: bullet-list summarization reads the topics off the stored metadata, so a document ingested without them summarizes worse. A request carrying `doc_reference`/`doc_content` has nothing to write to — leave `persist` unset there and it is normalized to false; setting it explicitly is rejected. `persisted` in the response reports what actually happened, it does not echo the request.\n\nChunking and clustering knobs are service configuration, not request fields.","operationId":"async_topics_extraction_api_v1_topics_extraction_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestTopicsExtraction"},"examples":{"async_callback":{"summary":"Topics of a stored document, written back (callback)","description":"The normal shape: `id_doc` plus the default `persist: true`, so bullet-list summarization of the same document picks the topics up afterwards. Returns 202 {job_id}; the result is POSTed to callback_url when done.","value":{"id_doc":481523,"language":"it","callback_url":"https://consumer.example.org/webhooks/aicore"}},"fire_and_poll":{"summary":"Topics of a document that is not stored (poll)","description":"Reference-only: there is no stored document to write to, so `persist` is normalized to false and the response's `persisted` is false. Returns 202 {job_id} + Location; poll GET /api/v1/jobs/{job_id}/result until 200.","value":{"doc_reference":"https://acct.blob.core.windows.net/docs/relazione.pdf?sv=2024-11-04&se=...&sig=...","language":"it"}}}}},"required":true},"responses":{"202":{"description":"Job accepted. With a callback_url the terminal result is POSTed there; without one, poll the Location header URL.","headers":{"Location":{"description":"Poll URL (GET /api/v1/jobs/{job_id}/result) — present only when the job was submitted without callback_url","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseJobAccepted"}}}},"400":{"description":"Missing mandatory identity headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for this service.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"502":{"description":"Job failed without a business status, or the job could not be enqueued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"503":{"description":"The job plane is unavailable on this gateway.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"callbacks":{"topics_extraction_result":{"{$request.body#/callback_url}":{"post":{"summary":"topics_extraction result to callback_url","description":"Sent once the job terminates, errors and timeouts included. Answer 2xx; a failed delivery is retried, then dead-lettered.","operationId":"topics_extraction_result__request_body__callback_url__post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobCallbackTopicsExtraction"}}},"required":true},"responses":{"200":{"description":"Consumer acknowledged the result.","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}},"security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}]}},"/api/v1/cover_extraction":{"post":{"tags":["cover_extraction"],"summary":"Async Cover Extraction","description":"Render the first page of a document as a cover image.\n\nAsync-only: the worker scales to zero, so this always returns 202 {job_id} immediately even though the render itself is quick. With a callback_url the result is POSTed there when done; without one the call is fire-and-poll — follow the Location header to GET /api/v1/jobs/{job_id}/result (the terminal result stays available 600 s after completion).\n\nThere is no `id_doc` input and there will not be one: storage keeps extracted text and metadata only and drops the document reference after extraction, so the cover of an already-ingested document needs the reference supplied again. Exactly one of `doc_reference` and `doc_content` is required, and `file_type` is mandatory — nothing auto-detects it here.\n\nThe image comes back **inline**, base64, in `cover_b64`, with its MIME type in `content_type`. It is large but not secret, so it is not redacted; do not persist it — re-render it when you need it.","operationId":"async_cover_extraction_api_v1_cover_extraction_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestCoverExtraction"},"examples":{"async_callback":{"summary":"Render a cover with callback","description":"Returns 202 {job_id}; the base64 image is POSTed to callback_url when done. Prefer this for anything unattended — the poll window is only 600 s after completion.","value":{"doc_reference":"https://acct.blob.core.windows.net/docs/manuale.pdf?sv=2024-11-04&se=...&sig=...","file_type":"pdf","callback_url":"https://consumer.example.org/webhooks/aicore"}},"fire_and_poll":{"summary":"Render a cover from inline content (poll)","description":"Returns 202 {job_id} + Location; poll GET /api/v1/jobs/{job_id}/result until 200. `file_type` is required on both input shapes.","value":{"doc_content":"JVBERi0xLjQKJcfsj6IK...","file_type":"pdf"}}}}},"required":true},"responses":{"202":{"description":"Job accepted. With a callback_url the terminal result is POSTed there; without one, poll the Location header URL.","headers":{"Location":{"description":"Poll URL (GET /api/v1/jobs/{job_id}/result) — present only when the job was submitted without callback_url","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseJobAccepted"}}}},"400":{"description":"Missing mandatory identity headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for this service.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"502":{"description":"Job failed without a business status, or the job could not be enqueued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"503":{"description":"The job plane is unavailable on this gateway.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"callbacks":{"cover_extraction_result":{"{$request.body#/callback_url}":{"post":{"summary":"cover_extraction result to callback_url","description":"Sent once the job terminates, errors and timeouts included. Answer 2xx; a failed delivery is retried, then dead-lettered.","operationId":"cover_extraction_result__request_body__callback_url__post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobCallbackCoverExtraction"}}},"required":true},"responses":{"200":{"description":"Consumer acknowledged the result.","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}},"security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}]}},"/api/v1/llm/aliases":{"get":{"tags":["llm"],"summary":"Async Llm Aliases","description":"List the LLM aliases this platform accepts, with a human-readable description each.\n\nAn alias is what a caller puts in an `llm_alias` field. The list comes from this gateway's\nown configuration, so it is the same set the services behind it resolve against.","operationId":"async_llm_aliases_api_v1_llm_aliases_get","responses":{"200":{"description":"Configured aliases, in configuration order.","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ResponseAlias"},"type":"array","title":"Response 200 Async Llm Aliases Api V1 Llm Aliases Get"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for `llm`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"503":{"description":"No LLM configured on this gateway.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}}},"security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}]}},"/api/v1/chats":{"get":{"tags":["chat"],"summary":"Async Get Lists Chats","description":"List this tenant's chats, newest activity first.\n\nUnpaginated: the retired proxy's `Page[...]` envelope sliced a list the chat service\nhad already returned in full, so it bounded the response body and nothing else. A caller that wants a window should filter by\n`start_dt`/`end_dt`, which chat applies at the storage query.","operationId":"async_get_lists_chats_api_v1_chats_get","security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}],"parameters":[{"name":"id_prompt","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Prompt identifier","title":"Id Prompt"},"description":"Prompt identifier"},{"name":"enabled","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Only enabled or only disabled chats","title":"Enabled"},"description":"Only enabled or only disabled chats"},{"name":"sample","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Only sampled or only unsampled chats","title":"Sample"},"description":"Only sampled or only unsampled chats"},{"name":"start_dt","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Start date (`YYYY-MM-DD`)","title":"Start Dt"},"description":"Start date (`YYYY-MM-DD`)"},{"name":"end_dt","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"End date (`YYYY-MM-DD`)","title":"End Dt"},"description":"End date (`YYYY-MM-DD`)"},{"name":"chat_type","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/ChatType"},{"type":"null"}],"description":"Chat type","title":"Chat Type"},"description":"Chat type"},{"name":"id_docs","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"integer"}},{"type":"null"}],"description":"Document identifiers","title":"Id Docs"},"description":"Document identifiers"},{"name":"id_docs_match_mode","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/DocsListMatchMode"},{"type":"null"}],"description":"How `id_docs` must match","title":"Id Docs Match Mode"},"description":"How `id_docs` must match"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ResponseChatSummaryItem"},"title":"Response Async Get Lists Chats Api V1 Chats Get"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for `chat`, or the chat belongs to another tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"404":{"description":"No such chat for this system and tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/{id_chat}":{"get":{"tags":["chat"],"summary":"Async Get Chat History","description":"The full conversation of one chat.","operationId":"async_get_chat_history_api_v1_chats__id_chat__get","security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}],"parameters":[{"name":"id_chat","in":"path","required":true,"schema":{"type":"string","description":"Chat identifier","title":"Id Chat"},"description":"Chat identifier"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseConversationItems"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for `chat`, or the chat belongs to another tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"404":{"description":"No such chat for this system and tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["chat"],"summary":"Async Delete Chat","description":"Delete a chat and its conversation.","operationId":"async_delete_chat_api_v1_chats__id_chat__delete","security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}],"parameters":[{"name":"id_chat","in":"path","required":true,"schema":{"type":"string","description":"Chat identifier","title":"Id Chat"},"description":"Chat identifier"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Async Delete Chat Api V1 Chats  Id Chat  Delete"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for `chat`, or the chat belongs to another tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"404":{"description":"No such chat for this system and tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/query":{"post":{"tags":["chat"],"summary":"Async Send Query","description":"Ask a question. Returns `id_chat` and `id_query`; stream the answer with the id_query.\n\n`id_chat` is a query parameter rather than a body field. The body is then the library's own `RequestSendQuery` with no wrapper model, so this\nroute cannot drift from the service it forwards to.","operationId":"async_send_query_api_v1_chats_query_post","security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}],"parameters":[{"name":"id_chat","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Chat to append to. Omit to start a new one.","title":"Id Chat"},"description":"Chat to append to. Omit to start a new one."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestSendQuery"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseSendQuery"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for `chat`, or the chat belongs to another tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"404":{"description":"No such chat for this system and tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/{id_chat}/update":{"patch":{"tags":["chat"],"summary":"Async Update Chat","description":"Change a chat's properties, e.g. its title.","operationId":"async_update_chat_api_v1_chats__id_chat__update_patch","security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}],"parameters":[{"name":"id_chat","in":"path","required":true,"schema":{"type":"string","description":"Chat identifier","title":"Id Chat"},"description":"Chat identifier"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestPatchProperties"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Async Update Chat Api V1 Chats  Id Chat  Update Patch"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for `chat`, or the chat belongs to another tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"404":{"description":"No such chat for this system and tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/enabled":{"patch":{"tags":["chat"],"summary":"Async Change Enabled By Id Prompt","description":"Enable or disable every chat of this tenant that uses a given prompt.","operationId":"async_change_enabled_by_id_prompt_api_v1_chats_enabled_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestChangeEnabledByIdPrompt"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Async Change Enabled By Id Prompt Api V1 Chats Enabled Patch"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for `chat`, or the chat belongs to another tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"404":{"description":"No such chat for this system and tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}]}},"/api/v1/chats/{id_chat}/feedback":{"patch":{"tags":["chat"],"summary":"Async Change Feedback","description":"Record a thumbs-up or thumbs-down on an answer.","operationId":"async_change_feedback_api_v1_chats__id_chat__feedback_patch","security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}],"parameters":[{"name":"id_chat","in":"path","required":true,"schema":{"type":"string","description":"Chat identifier","title":"Id Chat"},"description":"Chat identifier"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestFeedback"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Async Change Feedback Api V1 Chats  Id Chat  Feedback Patch"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for `chat`, or the chat belongs to another tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"404":{"description":"No such chat for this system and tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/{id_chat}/stream":{"get":{"tags":["chat"],"summary":"Async Stream Chat","description":"Stream one answer as Server-Sent Events.\n\nEach event's `data` payload is **base64-encoded** — see `app/business_logic/sse.py` for why\nthat is not optional. Framing, `event:` names and comments pass through untouched.\n\n**Pass `id_query`.** Omitting it makes chat resolve \"the newest query\", which is a race\nwhen two are in flight. `POST /chats/query` returns the id.\n\nNo ping is added here. Chat emits an application-level `nop` every 30 s and deliberately\nneutralises sse-starlette's own ping so `nop` is the only idle traffic; that beat is what\nkeeps the 240 s Azure LB idle timeout from closing the connection. A second keepalive would\ndouble idle traffic and buy nothing.","operationId":"async_stream_chat_api_v1_chats__id_chat__stream_get","security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}],"parameters":[{"name":"id_chat","in":"path","required":true,"schema":{"type":"string","description":"Chat identifier","title":"Id Chat"},"description":"Chat identifier"},{"name":"id_query","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query to stream, as returned by `POST /chats/query`. Omit to stream the chat's newest query.","title":"Id Query"},"description":"Query to stream, as returned by `POST /chats/query`. Omit to stream the chat's newest query."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for `chat`, or the chat belongs to another tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"404":{"description":"No such chat for this system and tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/collections":{"get":{"tags":["collections"],"summary":"Async Collections Get All Id Collections","description":"Every collection id this tenant owns.","operationId":"async_collections_get_all_id_collections_api_v1_collections_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"type":"integer"},"type":"array","title":"Response Async Collections Get All Id Collections Api V1 Collections Get"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for `collections`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"404":{"description":"No such collection for this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}}},"security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}]},"post":{"tags":["collections"],"summary":"Async Collections Create","description":"Create a collection. `id_collection` is caller-chosen, as it is on storage.","operationId":"async_collections_create_api_v1_collections_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BaseCollection","description":"The collection to create. `id_tenant` is ignored and replaced with the authenticated tenant."}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BaseCollection"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for `collections`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"404":{"description":"No such collection for this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}]}},"/api/v1/collections/{id_collection}":{"put":{"tags":["collections"],"summary":"Async Collections Update","description":"Replace a collection's contents. The id comes from the path, not the body.","operationId":"async_collections_update_api_v1_collections__id_collection__put","security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}],"parameters":[{"name":"id_collection","in":"path","required":true,"schema":{"type":"integer","description":"The collection to update","title":"Id Collection"},"description":"The collection to update"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BaseCollectionNoIdCollectionNoIdTenant","description":"The new collection contents"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BaseCollection"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for `collections`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"404":{"description":"No such collection for this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["collections"],"summary":"Async Collections Get","description":"One collection.","operationId":"async_collections_get_api_v1_collections__id_collection__get","security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}],"parameters":[{"name":"id_collection","in":"path","required":true,"schema":{"type":"integer","description":"The collection to retrieve","title":"Id Collection"},"description":"The collection to retrieve"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BaseCollection"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for `collections`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"404":{"description":"No such collection for this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["collections"],"summary":"Async Collections Delete","description":"Delete a collection. The documents in it are untouched.","operationId":"async_collections_delete_api_v1_collections__id_collection__delete","security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}],"parameters":[{"name":"id_collection","in":"path","required":true,"schema":{"type":"integer","description":"The collection to delete","title":"Id Collection"},"description":"The collection to delete"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Async Collections Delete Api V1 Collections  Id Collection  Delete"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for `collections`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"404":{"description":"No such collection for this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/jobs/{job_id}/result":{"get":{"tags":["jobs"],"summary":"Async Job Result","description":"Poll a parked (no-callback) job's result: 200 terminal result, 202 still\npending, 404 unknown/expired — the result stays available for the job plane's\nresult TTL after completion.\n\nThis reads ``orch:result:<job_id>`` directly instead of\nproxying an internal HTTP endpoint that then read the same key. The tenant\ncheck that used to exist on **both** sides now exists once, here, where the\ncaller is actually authenticated — which is the only place it was ever the\nsecurity boundary rather than defence in depth.\n\nAuthenticated without a fixed scope (jobs span services): the key's scope\nis checked against the job's own task after the lookup, and the result is\nonly served to the tenant that submitted it (results carry tenant document\ndata). Tenant mismatches return 404, not 403, so job ids can't be probed.","operationId":"async_job_result_api_v1_jobs__job_id__result_get","security":[{"ApiKeyAuth":[],"TenantHeader":[],"UserHeader":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","description":"The job_id returned by the 202 submit response","title":"Job Id"},"description":"The job_id returned by the 202 submit response"}],"responses":{"200":{"description":"Terminal result (status ok|error; errors keep their business error_code).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseJobStatus"}}}},"202":{"description":"Still pending — poll again (suggested interval 2-5 s).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseJobStatus"}}}},"401":{"description":"Missing, unknown, disabled or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"403":{"description":"Key not scoped for the polled job's task.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"404":{"description":"Unknown job_id or result expired (600 s window after completion).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"503":{"description":"The job plane is unavailable on this gateway.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/scopes":{"get":{"tags":["admin"],"summary":"List Scopes","description":"Catalog of scopes a key can be issued for (the UI builds its checkboxes from this).","operationId":"list_scopes_admin_scopes_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseScopes"}}}}},"security":[{"AdminKeyAuth":[]}]}},"/admin/api-keys":{"get":{"tags":["admin"],"summary":"List Api Keys","operationId":"list_api_keys_admin_api_keys_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ResponseApiKeyItem"},"type":"array","title":"Response List Api Keys Admin Api Keys Get"}}}}},"security":[{"AdminKeyAuth":[]}]},"post":{"tags":["admin"],"summary":"Issue Api Key","description":"Issue a new API key. The plaintext key is returned once and never stored.","operationId":"issue_api_key_admin_api_keys_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestIssueApiKey"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseIssueApiKey"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"AdminKeyAuth":[]}]}},"/admin/api-keys/{key_id}":{"patch":{"tags":["admin"],"summary":"Update Api Key","description":"Edit an issued key in place: permissions, expiry, enable/disable, description. Only the\nfields sent are changed; the key value/hash is never touched, so consumers keep working.","operationId":"update_api_key_admin_api_keys__key_id__patch","security":[{"AdminKeyAuth":[]}],"parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestUpdateApiKey"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseApiKeyItem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["admin"],"summary":"Revoke Api Key","operationId":"revoke_api_key_admin_api_keys__key_id__delete","security":[{"AdminKeyAuth":[]}],"parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/api-keys/{key_id}/usage":{"get":{"tags":["admin"],"summary":"Get Api Key Usage","description":"Usage history of a key, newest first: every call the key was identified for\n(successes, downstream errors and denied attempts), filterable.","operationId":"get_api_key_usage_admin_api_keys__key_id__usage_get","security":[{"AdminKeyAuth":[]}],"parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}},{"name":"service","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Exact-match filter on the service scope hit","title":"Service"},"description":"Exact-match filter on the service scope hit"},{"name":"tenant","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Exact-match filter on X-Tenant","title":"Tenant"},"description":"Exact-match filter on X-Tenant"},{"name":"user","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Exact-match filter on X-User","title":"User"},"description":"Exact-match filter on X-User"},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601 lower time bound (inclusive)","title":"From"},"description":"ISO-8601 lower time bound (inclusive)"},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601 upper time bound (inclusive)","title":"To"},"description":"ISO-8601 upper time bound (inclusive)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseUsage"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/usage/summary":{"get":{"tags":["admin"],"summary":"Get Usage Summary","description":"Chargeback view: tokens and cost per system/tenant/model over a time window.\n\nCost is joined from the versioned price table at read time, so correcting a\nprice fixes every past report instead of requiring history to be rewritten.\n``complete: false`` means at least one group had no applicable price and the\ntotal therefore understates reality — check it before invoicing anything.","operationId":"get_usage_summary_admin_usage_summary_get","security":[{"AdminKeyAuth":[]}],"parameters":[{"name":"system","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Exact-match filter on the consumer system","title":"System"},"description":"Exact-match filter on the consumer system"},{"name":"tenant","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Exact-match filter on the tenant","title":"Tenant"},"description":"Exact-match filter on the tenant"},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601 lower time bound (inclusive)","title":"From"},"description":"ISO-8601 lower time bound (inclusive)"},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601 upper time bound (inclusive)","title":"To"},"description":"ISO-8601 upper time bound (inclusive)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseUsageSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/jobs":{"get":{"tags":["admin"],"summary":"Get Jobs","description":"Job history with the token spend and cost of each job.\n\nBefore this existed a job left no readable trace once its result key expired:\nthe table was written but never read. Cost is joined from the\nversioned price table at read time by the same function the chargeback summary\nuses, so the two can never disagree.\n\n``stats`` describes the whole filtered window, not the returned page.","operationId":"get_jobs_admin_jobs_get","security":[{"AdminKeyAuth":[]}],"parameters":[{"name":"system","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Exact-match filter on the consumer system","title":"System"},"description":"Exact-match filter on the consumer system"},{"name":"tenant","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Exact-match filter on the tenant","title":"Tenant"},"description":"Exact-match filter on the tenant"},{"name":"service","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Exact-match filter on the service (== scope)","title":"Service"},"description":"Exact-match filter on the service (== scope)"},{"name":"event","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"submitted | completed | failed","title":"Event"},"description":"submitted | completed | failed"},{"name":"delivery","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"delivered | parked | dead_lettered","title":"Delivery"},"description":"delivered | parked | dead_lettered"},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601 lower bound on submitted_at (inclusive)","title":"From"},"description":"ISO-8601 lower bound on submitted_at (inclusive)"},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601 upper bound on submitted_at (inclusive)","title":"To"},"description":"ISO-8601 upper bound on submitted_at (inclusive)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseJobs"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/jobs/{job_id}":{"get":{"tags":["admin"],"summary":"Get Job Detail","description":"One job with every LLM call it made, each priced individually — the\narithmetic behind its cost, plus a link to its Langfuse session when one is\nconfigured.","operationId":"get_job_detail_admin_jobs__job_id__get","security":[{"AdminKeyAuth":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseJobDetail"}}}},"404":{"description":"No job history for that id (never submitted, or submitted while job events were disabled)"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/dlq":{"get":{"tags":["admin"],"summary":"Get Dlq Entries","description":"Dead-lettered results, oldest first, with the stream depth the alert watches.","operationId":"get_dlq_entries_admin_dlq_get","security":[{"AdminKeyAuth":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":50,"title":"Limit"}},{"name":"start","in":"query","required":false,"schema":{"type":"string","description":"Stream id to page from; pass the last entry_id seen","default":"-","title":"Start"},"description":"Stream id to page from; pass the last entry_id seen"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDlq"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/dlq/{entry_id}/replay":{"post":{"tags":["admin"],"summary":"Replay Dlq Entry","description":"Re-queue a dead-lettered result so COLLECT delivers it again.\n\nThis re-attempts **delivery of a result that already exists** — the job is not\nre-run, so no tokens are spent and the consumer receives the same answer it\noriginally missed. The README's job-plane section describes the replay flow.\n\n404 and 422 are real answers the console renders differently: already gone, and\nan entry that can only be discarded. Neither is a gateway fault, so neither\nbecomes a 502.","operationId":"replay_dlq_entry_admin_dlq__entry_id__replay_post","security":[{"AdminKeyAuth":[]}],"parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDlqAction"}}}},"404":{"description":"No such entry — already replayed, discarded or trimmed"},"422":{"description":"The entry never parsed as a result; discard it instead"}}}},"/admin/dlq/{entry_id}":{"delete":{"tags":["admin"],"summary":"Discard Dlq Entry","description":"Drop a dead-letter entry without re-queuing it — the only exit for an entry\nthat never parsed, and what lets the DLQ alert clear.","operationId":"discard_dlq_entry_admin_dlq__entry_id__delete","security":[{"AdminKeyAuth":[]}],"parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDlqAction"}}}},"404":{"description":"No such entry — already replayed, discarded or trimmed"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/usage/prices":{"get":{"tags":["admin"],"summary":"Get Usage Prices","operationId":"get_usage_prices_admin_usage_prices_get","security":[{"AdminKeyAuth":[]}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponsePrices"}}}}}},"put":{"tags":["admin"],"summary":"Put Usage Price","description":"Publish a unit price. Idempotent per (effective_from, provider, model, kind).\n\nPrices are configuration, not code: a provider price change is a call to this\nendpoint, never a deploy.\n\nThe row is recorded as ``manual``, which is what earns it a Retract button in the\nconsole — a hand-posted price outranks the sync forever, so being able to give the\nmodel back is not a nicety. `source` is not read from the body: provenance is\nrecorded by the writer, never asserted by the caller.","operationId":"put_usage_price_admin_usage_prices_put","security":[{"AdminKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsagePriceItem"}}}},"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["admin"],"summary":"Delete Usage Price","description":"Retract a published price. The counterpart to PUT, and not a nicety.\n\nA hand-written row outranks anything ``price_sync`` writes with an older\n``startDate``, and Langfuse dates managed definitions from the model's launch —\nso a row published once keeps overriding list price forever, and until now\nthere was no way to give a model back to the sync but to edit the database by\nhand. All four key parts are required: a price is retracted deliberately, one\nrow at a time, never by prefix.","operationId":"delete_usage_price_admin_usage_prices_delete","security":[{"AdminKeyAuth":[]}],"parameters":[{"name":"effective_from","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Exact date of the row to retract","title":"Effective From"},"description":"Exact date of the row to retract"},{"name":"model","in":"query","required":true,"schema":{"type":"string","description":"Exact model of the row to retract","title":"Model"},"description":"Exact model of the row to retract"},{"name":"kind","in":"query","required":true,"schema":{"type":"string","description":"input | output | cache_read | reasoning","title":"Kind"},"description":"input | output | cache_read | reasoning"},{"name":"provider","in":"query","required":false,"schema":{"type":"string","description":"Exact provider of the row; empty matches the provider-agnostic row","default":"","title":"Provider"},"description":"Exact provider of the row; empty matches the provider-agnostic row"}],"responses":{"204":{"description":"Successful Response"},"404":{"description":"No price row with that exact (effective_from, provider, model, kind)"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"BaseCollection":{"properties":{"name":{"type":"string","title":"Name","description":"The Collection name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"The Collection description"},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Metadata","description":"The Collection metadata"},"collection":{"items":{"$ref":"#/components/schemas/BaseCollectionElement"},"type":"array","title":"Collection"},"id_tenant":{"type":"string","title":"Id Tenant","description":"The Tenant ID"},"id_collection":{"type":"integer","title":"Id Collection","description":"The Collection ID"}},"type":"object","required":["name","collection","id_tenant","id_collection"],"title":"BaseCollection"},"BaseCollectionElement":{"properties":{"id_doc":{"type":"integer","title":"Id Doc","description":"The Document ID"},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Metadata","description":"The Document metadata for this collection"}},"type":"object","required":["id_doc"],"title":"BaseCollectionElement"},"BaseCollectionNoIdCollectionNoIdTenant":{"properties":{"name":{"type":"string","title":"Name","description":"The Collection name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"The Collection description"},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Metadata","description":"The Collection metadata"},"collection":{"items":{"$ref":"#/components/schemas/BaseCollectionElement"},"type":"array","title":"Collection"}},"type":"object","required":["name","collection"],"title":"BaseCollectionNoIdCollectionNoIdTenant"},"BaseDocumentCollaborator":{"properties":{"role":{"type":"string","title":"Role","description":"The role of the collaborator","default":""},"name":{"type":"string","title":"Name","description":"The name of the collaborator","default":""}},"type":"object","title":"BaseDocumentCollaborator"},"BaseDocumentMetadataNoFileType":{"properties":{"source_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Id","description":"The source ID of the document"},"document_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Document Type","description":"The type of the document"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title","description":"The title of the document"},"subtitle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subtitle","description":"The subtitle of the document"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"The description of the document"},"topics":{"items":{"type":"string"},"type":"array","title":"Topics","description":"The list of topics of the document","default":[]},"authors":{"items":{"type":"string"},"type":"array","title":"Authors","description":"The list of authors of the document","default":[]},"collaborators":{"items":{"$ref":"#/components/schemas/BaseDocumentCollaborator"},"type":"array","title":"Collaborators","description":"The list of collaborators of the document","default":[]},"updated_at":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Updated At","description":"The last update date of the document (ms timestamp)"},"reference_date":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reference Date","description":"The reference date of the document (ms timestamp)"},"custom":{"additionalProperties":true,"type":"object","title":"Custom","description":"The custom metadata of the document","default":{}}},"type":"object","title":"BaseDocumentMetadataNoFileType"},"ChatType":{"type":"string","enum":["normal","single_doc","on_the_fly_docs"],"title":"ChatType"},"Collaborator":{"properties":{"name":{"type":"string","title":"Name","description":"The collaborator's name, as printed in the document, never translated"},"role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Role","description":"Role in the document's own terminology, lowercase, never translated (e.g. 'curatore', 'translator'); null when not stated"}},"type":"object","required":["name"],"title":"Collaborator"},"CollaboratorDetails":{"properties":{"role":{"type":"string","title":"Role","default":""},"name":{"type":"string","title":"Name","default":""}},"type":"object","title":"CollaboratorDetails"},"DlqEntry":{"properties":{"entry_id":{"type":"string","title":"Entry Id"},"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Id"},"service":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Service"},"task":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Task"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"error_code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Code"},"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"system":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"System"},"tenant":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tenant"},"user":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User"},"key_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Id"},"parse_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parse Error"},"payload_preview":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payload Preview"},"payload_chars":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Payload Chars"},"payload_truncated":{"type":"boolean","title":"Payload Truncated","default":false}},"type":"object","required":["entry_id"],"title":"DlqEntry","description":"One ``orch:dlq`` entry, described as far as it can be.\n\nEvery field but ``entry_id`` is optional because the DLQ deliberately holds\ntwo different things: results whose delivery failed (fully described), and\nentries that never parsed as a result at all (``parse_error`` set, and the\nreason they can only be discarded). Hiding the second kind would hide the\nonly entries that never drain on their own.\n\nThe payload travels as a bounded preview: a dead-lettered result carries the\nworker's whole response, which for pdf-anonymization is an entire document\nin base64."},"DocsListMatchMode":{"type":"string","enum":["exact","subset","overlap"],"title":"DocsListMatchMode"},"DocumentKind":{"type":"string","enum":["auto","normative","jurisprudence","praxis","book","generic"],"title":"DocumentKind"},"EmbeddedFilesMode":{"type":"string","enum":["anonymize","remove","keep"],"title":"EmbeddedFilesMode"},"ExtractedMetadata":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"reference_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reference Date","description":"Emission/act date, ISO format YYYY-MM-DD"},"publication_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Publication Date","description":"Publication date (e.g. Gazzetta Ufficiale), ISO format YYYY-MM-DD"},"issuing_body":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Issuing Body","description":"Issuing body/authority (organo)"},"act_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Act Number","description":"Act/document number"},"act_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Act Type","description":"Act type, e.g. 'decreto legislativo', 'circolare'"},"isbn":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Isbn"},"authors":{"items":{"type":"string"},"type":"array","title":"Authors","default":[]},"collaborators":{"items":{"$ref":"#/components/schemas/Collaborator"},"type":"array","title":"Collaborators","description":"Secondary contributors (curatori, traduttori, illustratori, prefatori), distinct from the primary authors","default":[]}},"type":"object","title":"ExtractedMetadata"},"FileType":{"type":"string","enum":["pdf","docx","txt","md","epub","html","xlsx"],"title":"FileType"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"InformationDetails":{"properties":{"title":{"type":"string","title":"Title","default":""},"authors":{"items":{"type":"string"},"type":"array","title":"Authors","default":[]},"collaborators":{"items":{"$ref":"#/components/schemas/CollaboratorDetails"},"type":"array","title":"Collaborators","default":[]}},"type":"object","title":"InformationDetails"},"JobCallbackAiOverview":{"properties":{"job_id":{"type":"string","title":"Job Id"},"service":{"type":"string","title":"Service"},"task":{"type":"string","title":"Task"},"status":{"type":"string","title":"Status"},"response":{"anyOf":[{"$ref":"#/components/schemas/ResponseAiOverview"},{"type":"null"}],"description":"The service's native response when status=ok"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"error_code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Code"}},"type":"object","required":["job_id","service","task","status"],"title":"JobCallbackAiOverview"},"JobCallbackAiOverviewRag":{"properties":{"job_id":{"type":"string","title":"Job Id"},"service":{"type":"string","title":"Service"},"task":{"type":"string","title":"Task"},"status":{"type":"string","title":"Status"},"response":{"anyOf":[{"$ref":"#/components/schemas/ResponseAiOverview"},{"type":"null"}],"description":"The service's native response when status=ok"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"error_code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Code"}},"type":"object","required":["job_id","service","task","status"],"title":"JobCallbackAiOverviewRag"},"JobCallbackArticleGeneration":{"properties":{"job_id":{"type":"string","title":"Job Id"},"service":{"type":"string","title":"Service"},"task":{"type":"string","title":"Task"},"status":{"type":"string","title":"Status"},"response":{"anyOf":[{"$ref":"#/components/schemas/ResponseArticleGeneration"},{"type":"null"}],"description":"The service's native response when status=ok"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"error_code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Code"}},"type":"object","required":["job_id","service","task","status"],"title":"JobCallbackArticleGeneration"},"JobCallbackBookReview":{"properties":{"job_id":{"type":"string","title":"Job Id"},"service":{"type":"string","title":"Service"},"task":{"type":"string","title":"Task"},"status":{"type":"string","title":"Status"},"response":{"anyOf":[{"$ref":"#/components/schemas/ResponseBookReview"},{"type":"null"}],"description":"The service's native response when status=ok"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"error_code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Code"}},"type":"object","required":["job_id","service","task","status"],"title":"JobCallbackBookReview"},"JobCallbackCoverExtraction":{"properties":{"job_id":{"type":"string","title":"Job Id"},"service":{"type":"string","title":"Service"},"task":{"type":"string","title":"Task"},"status":{"type":"string","title":"Status"},"response":{"anyOf":[{"$ref":"#/components/schemas/ResponseCoverExtraction"},{"type":"null"}],"description":"The service's native response when status=ok"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"error_code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Code"}},"type":"object","required":["job_id","service","task","status"],"title":"JobCallbackCoverExtraction"},"JobCallbackDocumentIngestion":{"properties":{"job_id":{"type":"string","title":"Job Id"},"service":{"type":"string","title":"Service"},"task":{"type":"string","title":"Task"},"status":{"type":"string","title":"Status"},"response":{"anyOf":[{"$ref":"#/components/schemas/ResponseDocumentIngestion"},{"type":"null"}],"description":"The service's native response when status=ok"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"error_code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Code"}},"type":"object","required":["job_id","service","task","status"],"title":"JobCallbackDocumentIngestion"},"JobCallbackFaqsGeneration":{"properties":{"job_id":{"type":"string","title":"Job Id"},"service":{"type":"string","title":"Service"},"task":{"type":"string","title":"Task"},"status":{"type":"string","title":"Status"},"response":{"anyOf":[{"$ref":"#/components/schemas/ResponseFaqsGeneration"},{"type":"null"}],"description":"The service's native response when status=ok"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"error_code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Code"}},"type":"object","required":["job_id","service","task","status"],"title":"JobCallbackFaqsGeneration"},"JobCallbackMetadataExtraction":{"properties":{"job_id":{"type":"string","title":"Job Id"},"service":{"type":"string","title":"Service"},"task":{"type":"string","title":"Task"},"status":{"type":"string","title":"Status"},"response":{"anyOf":[{"$ref":"#/components/schemas/ResponseMetadataExtraction"},{"type":"null"}],"description":"The service's native response when status=ok"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"error_code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Code"}},"type":"object","required":["job_id","service","task","status"],"title":"JobCallbackMetadataExtraction"},"JobCallbackPdfAnonymization":{"properties":{"job_id":{"type":"string","title":"Job Id"},"service":{"type":"string","title":"Service"},"task":{"type":"string","title":"Task"},"status":{"type":"string","title":"Status"},"response":{"anyOf":[{"$ref":"#/components/schemas/ResponsePdfAnonymization"},{"type":"null"}],"description":"The service's native response when status=ok"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"error_code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Code"}},"type":"object","required":["job_id","service","task","status"],"title":"JobCallbackPdfAnonymization"},"JobCallbackQuestionAnswering":{"properties":{"job_id":{"type":"string","title":"Job Id"},"service":{"type":"string","title":"Service"},"task":{"type":"string","title":"Task"},"status":{"type":"string","title":"Status"},"response":{"anyOf":[{"$ref":"#/components/schemas/ResponseQuestionAnswering"},{"type":"null"}],"description":"The service's native response when status=ok"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"error_code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Code"}},"type":"object","required":["job_id","service","task","status"],"title":"JobCallbackQuestionAnswering"},"JobCallbackQuizGeneration":{"properties":{"job_id":{"type":"string","title":"Job Id"},"service":{"type":"string","title":"Service"},"task":{"type":"string","title":"Task"},"status":{"type":"string","title":"Status"},"response":{"anyOf":[{"$ref":"#/components/schemas/ResponseQuizGeneration"},{"type":"null"}],"description":"The service's native response when status=ok"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"error_code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Code"}},"type":"object","required":["job_id","service","task","status"],"title":"JobCallbackQuizGeneration"},"JobCallbackSpeechToText":{"properties":{"job_id":{"type":"string","title":"Job Id"},"service":{"type":"string","title":"Service"},"task":{"type":"string","title":"Task"},"status":{"type":"string","title":"Status"},"response":{"anyOf":[{"$ref":"#/components/schemas/ResponseSttOffline"},{"type":"null"}],"description":"The service's native response when status=ok"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"error_code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Code"}},"type":"object","required":["job_id","service","task","status"],"title":"JobCallbackSpeechToText"},"JobCallbackSummarization":{"properties":{"job_id":{"type":"string","title":"Job Id"},"service":{"type":"string","title":"Service"},"task":{"type":"string","title":"Task"},"status":{"type":"string","title":"Status"},"response":{"anyOf":[{"$ref":"#/components/schemas/ResponseSummarization"},{"type":"null"}],"description":"The service's native response when status=ok"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"error_code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Code"}},"type":"object","required":["job_id","service","task","status"],"title":"JobCallbackSummarization"},"JobCallbackTextToSpeech":{"properties":{"job_id":{"type":"string","title":"Job Id"},"service":{"type":"string","title":"Service"},"task":{"type":"string","title":"Task"},"status":{"type":"string","title":"Status"},"response":{"anyOf":[{"$ref":"#/components/schemas/ResponseTtsOffline"},{"type":"null"}],"description":"The service's native response when status=ok"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"error_code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Code"}},"type":"object","required":["job_id","service","task","status"],"title":"JobCallbackTextToSpeech"},"JobCallbackTopicsExtraction":{"properties":{"job_id":{"type":"string","title":"Job Id"},"service":{"type":"string","title":"Service"},"task":{"type":"string","title":"Task"},"status":{"type":"string","title":"Status"},"response":{"anyOf":[{"$ref":"#/components/schemas/ResponseTopicsExtraction"},{"type":"null"}],"description":"The service's native response when status=ok"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"error_code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Code"}},"type":"object","required":["job_id","service","task","status"],"title":"JobCallbackTopicsExtraction"},"JobLlmCall":{"properties":{"ts":{"type":"string","format":"date-time","title":"Ts"},"provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Provider"},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"llm_alias":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Llm Alias"},"call_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Call Type"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"tokens":{"$ref":"#/components/schemas/JobTokens"},"cost":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cost"}},"type":"object","required":["ts","tokens"],"title":"JobLlmCall","description":"One metered LLM call belonging to a job — the audit trail behind its cost."},"JobRow":{"properties":{"job_id":{"type":"string","title":"Job Id"},"submitted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Submitted At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration Ms","description":"completed_at - submitted_at; null while a job is still open or if its submitted event was lost"},"event":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Event"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"delivery":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Delivery"},"error_code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Code"},"service":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Service"},"task":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Task"},"system":{"type":"string","title":"System"},"tenant":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tenant"},"user":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User"},"key_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Id"},"tokens":{"$ref":"#/components/schemas/JobTokens"},"cost":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cost","description":"Null when at least one of the job's models had no applicable price — never a silent zero"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"}},"type":"object","required":["job_id","system"],"title":"JobRow","description":"One job, with the spend it caused.\n\n``event`` is the job's own outcome (submitted / completed / failed) while\n``delivery`` is how its result reached the consumer (delivered / parked /\ndead_lettered). They are independent: a failed job can be delivered perfectly\nand a successful one can end up dead-lettered."},"JobStats":{"properties":{"jobs":{"type":"integer","title":"Jobs","default":0},"by_event":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Event"},"by_delivery":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Delivery"},"tokens":{"$ref":"#/components/schemas/JobTokens"},"cost":{"type":"number","title":"Cost","default":0.0},"currency":{"type":"string","title":"Currency","default":"USD"},"complete":{"type":"boolean","title":"Complete","description":"False when some model in the window had no price, i.e. cost understates reality","default":true}},"type":"object","title":"JobStats","description":"Totals over the whole filtered window, NOT just the returned page — a\ndashboard number that changed with the page size would be a trap."},"JobTokens":{"properties":{"calls":{"type":"integer","title":"Calls","default":0},"input_tokens":{"type":"integer","title":"Input Tokens","default":0},"output_tokens":{"type":"integer","title":"Output Tokens","default":0},"cache_read_tokens":{"type":"integer","title":"Cache Read Tokens","default":0},"reasoning_tokens":{"type":"integer","title":"Reasoning Tokens","default":0},"total_tokens":{"type":"integer","title":"Total Tokens","default":0}},"type":"object","title":"JobTokens","description":"Token totals of one job, or of one LLM call inside it.\n\n``validate_assignment`` is load-bearing, not tidiness: these totals are\naccumulated field-by-field with ``setattr`` from Postgres aggregates, and\n``SUM()`` over a ``BigInteger`` column returns ``NUMERIC`` — a ``Decimal``.\nWithout validation on assignment the Decimal survives into the serializer and\nthe API answers ``\"input_tokens\": \"18992\"``, a string, for every non-zero\ntotal (a zero stays an int only because ``Decimal('0') or 0`` is falsy). It\nbelongs on the class rather than at each accumulation site because the next\nreader of the `jobs` table would have to remember the cast too."},"Language":{"type":"string","enum":["en","es","fr","de","zh","ja","ko","it","pt","ru","ar","nl","pl","tr","vi","hi","id","th","cs","sv","el","ro","hu","da","fi","no","uk","he","bn","ms","ta","ur","fa","bg","sk","hr","sr","lt","sl","et","lv","sw"],"title":"Language"},"OcrMode":{"type":"string","enum":["auto","force","off"],"title":"OcrMode"},"PdfAnonymizationStats":{"properties":{"redacted":{"type":"integer","title":"Redacted"},"unlocated":{"type":"integer","title":"Unlocated"},"embedded_files_anonymized":{"type":"integer","title":"Embedded Files Anonymized","default":0},"embedded_files_removed":{"type":"integer","title":"Embedded Files Removed","default":0}},"type":"object","required":["redacted","unlocated"],"title":"PdfAnonymizationStats"},"QueryAugmentation":{"type":"string","enum":["custom","corporate","ecommerce","general","hospital","municipality","municipal_mng_sw"],"title":"QueryAugmentation"},"RequestAiOverviewByIdDoc":{"properties":{"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"id_doc":{"type":"integer","title":"Id Doc"},"language":{"$ref":"#/components/schemas/Language"}},"type":"object","required":["id_doc","language"],"title":"RequestAiOverviewByIdDoc"},"RequestAiOverviewByQueryAndIdDocsList":{"properties":{"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"id_docs":{"items":{"type":"integer"},"type":"array","title":"Id Docs"},"query":{"type":"string","title":"Query"},"query_augmentation":{"anyOf":[{"$ref":"#/components/schemas/QueryAugmentation"},{"type":"null"}]},"custom_query_augmentation":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Custom Query Augmentation"},"language":{"$ref":"#/components/schemas/Language"},"rag_kwargs":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Rag Kwargs"}},"type":"object","required":["id_docs","query","language"],"title":"RequestAiOverviewByQueryAndIdDocsList"},"RequestArticleGeneration":{"properties":{"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"id_doc":{"type":"integer","title":"Id Doc"},"language":{"$ref":"#/components/schemas/Language"},"topic":{"type":"string","title":"Topic"}},"type":"object","required":["id_doc","language","topic"],"title":"RequestArticleGeneration"},"RequestBookReview":{"properties":{"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"id_doc":{"type":"integer","title":"Id Doc"},"language":{"$ref":"#/components/schemas/Language"}},"type":"object","required":["id_doc","language"],"title":"RequestBookReview"},"RequestChangeEnabledByIdPrompt":{"properties":{"id_prompt":{"type":"integer","title":"Id Prompt"},"enabled":{"type":"boolean","title":"Enabled"}},"type":"object","required":["id_prompt","enabled"],"title":"RequestChangeEnabledByIdPrompt"},"RequestCoverExtraction":{"properties":{"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"doc_reference":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Doc Reference","description":"URL or file:// reference to the document; exactly one of this and doc_content"},"doc_content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Doc Content","description":"Base64-encoded document content; exactly one of this and doc_reference"},"file_type":{"$ref":"#/components/schemas/FileType","description":"Type of the document file"}},"type":"object","required":["file_type"],"title":"RequestCoverExtraction","description":"Render the first page of one document as a cover image.\n\nNo `id_doc`, and it cannot grow one: storage retains extracted text and metadata only\nand drops `doc_reference` after extraction, so a stored document's cover needs the\nreference re-supplied. `file_type` is required — nothing auto-detects it on this path."},"RequestDocumentIngestion":{"properties":{"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"id_doc":{"type":"integer","title":"Id Doc","description":"The store key the document is addressed by afterwards; caller-chosen"},"doc_reference":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Doc Reference","description":"URL or file:// reference to the document; exactly one of this and doc_content"},"doc_content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Doc Content","description":"Base64-encoded document content; exactly one of this and doc_reference"},"file_type":{"anyOf":[{"$ref":"#/components/schemas/FileType"},{"type":"null"}],"description":"Type of the document file (pdf, docx, epub, ...); None lets the storage service auto-detect it"},"metadata":{"anyOf":[{"$ref":"#/components/schemas/BaseDocumentMetadataNoFileType"},{"type":"null"}],"description":"Metadata the caller already knows, merged into what ingestion extracts"},"overwrite":{"type":"boolean","title":"Overwrite","description":"Re-ingest a document that already exists instead of skipping it","default":false}},"type":"object","required":["id_doc"],"title":"RequestDocumentIngestion","description":"Ingest one document into storage — the producer the six `id_doc` tasks read back.\n\n`id_doc` is caller-chosen and its namespace is shared, not per-tenant: storage keys on\n`id_doc` alone and no document read filters on tenant. Ids must not\ncollide across consumers until that lands.\n\nDistinct from the call-plane `RequestDocumentIngestion` in\n`models.clients.storage.documents.requests`, which carries `id_tenant` and requires\n`metadata`."},"RequestFaqsGeneration":{"properties":{"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"id_doc":{"type":"integer","title":"Id Doc"},"language":{"$ref":"#/components/schemas/Language"},"max_nr_faqs":{"type":"integer","title":"Max Nr Faqs"}},"type":"object","required":["id_doc","language","max_nr_faqs"],"title":"RequestFaqsGeneration"},"RequestFeedback":{"properties":{"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id","description":"Conversation identifier"},"position":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Position","description":"Conversation block 0-based position"},"feedback":{"anyOf":[{"anyOf":[{"type":"boolean"},{"type":"integer"}],"ge":0,"le":5},{"type":"null"}],"title":"Feedback","description":"Feedback"},"feedback_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Feedback Text","description":"Feedback text"}},"type":"object","title":"RequestFeedback"},"RequestIssueApiKey":{"properties":{"system":{"type":"string","minLength":1,"title":"System","description":"Consumer system the key is issued to"},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes","description":"Services the key can call: ['*'] or a subset of the known scopes"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By","description":"Who is requesting the key (free text)"},"expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires At","description":"Optional ISO-8601 expiry (UTC); null = never expires"},"rate_limit_per_min":{"anyOf":[{"type":"integer","exclusiveMinimum":0.0},{"type":"null"}],"title":"Rate Limit Per Min","description":"Max requests per minute; null = unlimited"},"max_concurrent_jobs":{"anyOf":[{"type":"integer","exclusiveMinimum":0.0},{"type":"null"}],"title":"Max Concurrent Jobs","description":"Max jobs in flight; null = unlimited"}},"type":"object","required":["system","scopes"],"title":"RequestIssueApiKey"},"RequestMetadataExtraction":{"properties":{"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"doc_content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Doc Content","description":"Base64-encoded document content"},"doc_reference":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Doc Reference","description":"URL or file:// reference to the document"},"file_type":{"anyOf":[{"$ref":"#/components/schemas/FileType"},{"type":"null"}],"description":"Type of the document file (pdf, docx, epub, ...); None lets the storage service auto-detect it"},"document_kind":{"$ref":"#/components/schemas/DocumentKind","description":"Document taxonomy hint; 'auto' lets the service classify the document itself","default":"auto"},"language":{"anyOf":[{"$ref":"#/components/schemas/Language"},{"type":"null"}],"description":"Output language for the generated free-text fields (e.g. description). A specific language forces that language; omit/null keeps the document's own language."},"max_pages":{"type":"integer","maximum":30.0,"minimum":1.0,"title":"Max Pages","description":"Maximum number of leading pages analyzed for extraction","default":10}},"type":"object","title":"RequestMetadataExtraction"},"RequestPatchProperties":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title","description":"Chat title"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled","description":"Chat enabled flag"}},"type":"object","title":"RequestPatchProperties"},"RequestPdfAnonymization":{"properties":{"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"doc_content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Doc Content","description":"Base64-encoded PDF content"},"doc_reference":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Doc Reference","description":"URL or file:// reference to the PDF"},"categories_prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Categories Prompt","description":"Custom category-definitions prompt; default categories are used when omitted"},"structured_detection":{"type":"boolean","title":"Structured Detection","description":"Enable checksum-validated detectors for Italian fiscal codes and VAT numbers","default":true},"ocr_mode":{"$ref":"#/components/schemas/OcrMode","default":"auto"},"embedded_files":{"$ref":"#/components/schemas/EmbeddedFilesMode","description":"How to handle PDF embedded files (e.g. CDA/HL7 XML attachments): anonymize their textual content, remove them, or keep them untouched. In anonymize mode, non-textual attachments are removed (fail-closed).","default":"anonymize"},"scrub_metadata":{"type":"boolean","title":"Scrub Metadata","description":"Clear the PDF document metadata (Info dictionary and XMP), which may contain personal data such as patient identifiers.","default":true}},"type":"object","title":"RequestPdfAnonymization"},"RequestQuestionAnswering":{"properties":{"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"id_doc":{"type":"integer","title":"Id Doc"},"language":{"$ref":"#/components/schemas/Language"},"question":{"type":"string","title":"Question"}},"type":"object","required":["id_doc","language","question"],"title":"RequestQuestionAnswering"},"RequestQuizGeneration":{"properties":{"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"id_doc":{"type":"integer","title":"Id Doc"},"language":{"$ref":"#/components/schemas/Language"},"topics":{"items":{"type":"string"},"type":"array","title":"Topics"},"max_nr_quiz":{"type":"integer","title":"Max Nr Quiz"},"nr_answers":{"type":"integer","title":"Nr Answers"}},"type":"object","required":["id_doc","language","topics","max_nr_quiz","nr_answers"],"title":"RequestQuizGeneration"},"RequestSendQuery":{"properties":{"language":{"anyOf":[{"$ref":"#/components/schemas/Language"},{"type":"null"}],"description":"Language of the answer. If not set, the language will be detected from the query."},"id_prompt":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Id Prompt","description":"System prompt identifier"},"prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prompt","description":"System prompt text to use"},"query":{"type":"string","minLength":1,"title":"Query","description":"User query text"},"sources":{"anyOf":[{"items":{"$ref":"#/components/schemas/SourceInfo"},"type":"array"},{"type":"null"}],"title":"Sources","description":"List of document identifiers and metadata to use for the RAG"},"collections":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Collections","description":"List of collection identifiers to use for the RAG"},"sample":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Sample","description":"If `true` the query will be used without history and will not be stored","default":false},"query_metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Query Metadata","description":"Metadata assigned to the query"},"llm_alias":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Llm Alias","description":"Alias of the LLM to use. If not set, the default LLM will be used"}},"type":"object","required":["query"],"title":"RequestSendQuery"},"RequestSttOffline":{"properties":{"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"language":{"type":"string","pattern":"^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})+$","title":"Language","description":"BCP-47 locale, e.g. `it-IT`. A bare language code such as `it` is rejected: the engine needs the region subtag. The pattern checks shape only — the configured provider still rejects a well-formed locale it does not support."},"file_reference":{"type":"string","title":"File Reference","description":"URL the audio is fetched from; expected to be short-lived and signed"},"rag_augmentation":{"type":"boolean","title":"Rag Augmentation","description":"Post-process the transcript with one LLM pass that rewrites it into structured Markdown for RAG ingestion, returned in `rag_augmented_txt`. It retrieves nothing — it prepares text *for* a RAG index rather than drawing on one. Defaults to true, so every job pays for an LLM call; set it false to get the raw transcription only. Best-effort: on failure the transcription is still returned and `rag_augmented_txt` is absent, so an absent value does not distinguish a failed augmentation from one that was never asked for.","default":true}},"type":"object","required":["language","file_reference"],"title":"RequestSttOffline","description":"Offline transcription of one audio file, fetched by reference.\n\nReference-only: an orchestrated request is persisted verbatim to\n``orch:req:speech-to-text``, and inline base64 audio would put hours of it there.\n\nThe reference is expected to be signed and short-lived. It is a credential in a request\nbody, so it reaches the request stream regardless of redaction — a short expiry is the\nonly real bound. ``file://`` cannot work on the job path, where no filesystem is shared\nwith the caller.\n\n``language`` must be a complete BCP-47 locale, which is what both engines take. The\nworker used to expand a bare ``it``/``en``/``es`` and pass everything else through, so\nthree codes worked and the other 38 ``Language`` values did not — a contract no schema\nstated and no caller could discover. Rejecting the short form here trades that for one\nrule, and moves the failure from inside a running job (a pod cold start, a download and\nan ffmpeg pass before the engine sees the value) to the validation boundary."},"RequestSummarization":{"properties":{"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"id_doc":{"type":"integer","title":"Id Doc"},"language":{"$ref":"#/components/schemas/Language"},"type":{"$ref":"#/components/schemas/SummarizationType"},"nr_points":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Nr Points"}},"type":"object","required":["id_doc","language","type"],"title":"RequestSummarization"},"RequestTopicsExtraction":{"properties":{"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"id_doc":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Id Doc","description":"Read the document from storage; exactly one of this, doc_reference and doc_content"},"doc_reference":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Doc Reference","description":"URL or file:// reference to the document; exactly one of this, id_doc and doc_content"},"doc_content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Doc Content","description":"Base64-encoded document content; exactly one of this, id_doc and doc_reference"},"language":{"$ref":"#/components/schemas/Language","description":"Language the topics are produced in"},"persist":{"type":"boolean","title":"Persist","description":"Write the topics back to the stored document; requires id_doc","default":true}},"type":"object","required":["language"],"title":"RequestTopicsExtraction","description":"Topics for one document, from the store or from a supplied file.\n\nThe write-back is load-bearing: bullet-list `summarization` reads\n`original_document.metadata.topics`, so returning topics without persisting them\ndegrades it silently. `persist` therefore needs `id_doc` — normalized to false at its\ndefault, rejected when set explicitly, so the queued payload states what will happen.\n\nChunking and clustering knobs stay settings-driven; the fused job passes all four as\n`None`."},"RequestTtsOffline":{"properties":{"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"text":{"type":"string","maxLength":50000,"title":"Text","description":"Text to synthesize"},"voice":{"anyOf":[{"$ref":"#/components/schemas/VoiceInfo"},{"type":"null"}],"description":"Voice to use; null leaves the provider default"}},"type":"object","required":["text"],"title":"RequestTtsOffline","description":"Synthesize one block of text to an audio file.\n\n``voice`` is optional: omitting it leaves the provider on its configured default,\nwhich is what the pre-migration service did when the field was absent.\n\nThere is no output destination field: the audio comes back **inline** in\n``ResponseTtsOffline``, bounded by a configured cap. See that model for why inline beat\na signed reference here even though stt's *input* went the other way — the short version\nis that a minute of speech is PDF-sized, and no provider credential in this service can\nsign a URL."},"RequestUpdateApiKey":{"properties":{"scopes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Scopes"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled"},"expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires At"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"rate_limit_per_min":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rate Limit Per Min"},"max_concurrent_jobs":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Concurrent Jobs"}},"type":"object","title":"RequestUpdateApiKey","description":"Partial update of an issued key. Only the fields present are changed; the key value is\nnever touched. Send ``expires_at: null`` to clear an expiry (make the key never expire)."},"ResponseAiOverview":{"properties":{"result":{"type":"string","title":"Result"},"sources":{"anyOf":[{"items":{"$ref":"#/components/schemas/Source"},"type":"array"},{"type":"null"}],"title":"Sources"},"augmented_query":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Augmented Query"}},"type":"object","required":["result"],"title":"ResponseAiOverview"},"ResponseAlias":{"properties":{"llm_alias":{"type":"string","title":"Llm Alias"},"description":{"type":"string","title":"Description"}},"type":"object","required":["llm_alias","description"],"title":"ResponseAlias"},"ResponseApiKeyItem":{"properties":{"id":{"type":"string","title":"Id"},"system":{"type":"string","title":"System"},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes"},"enabled":{"type":"boolean","title":"Enabled"},"created_at":{"type":"string","title":"Created At"},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By"},"last_used_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Used At"},"expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires At"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"rate_limit_per_min":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rate Limit Per Min"},"max_concurrent_jobs":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Concurrent Jobs"}},"type":"object","required":["id","system","scopes","enabled","created_at"],"title":"ResponseApiKeyItem"},"ResponseArticleGeneration":{"properties":{"title":{"type":"string","title":"Title"},"content":{"items":{"type":"string"},"type":"array","title":"Content"}},"type":"object","required":["title","content"],"title":"ResponseArticleGeneration"},"ResponseBookReview":{"properties":{"information":{"$ref":"#/components/schemas/InformationDetails"},"overview":{"type":"string","title":"Overview","default":""},"topics":{"items":{"$ref":"#/components/schemas/TopicReviewDetails"},"type":"array","title":"Topics","default":[]},"summary":{"type":"string","title":"Summary","default":""}},"type":"object","required":["information"],"title":"ResponseBookReview"},"ResponseChatSummaryItem":{"properties":{"chat_type":{"$ref":"#/components/schemas/ChatType","description":"Chat type"},"id_system":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id System","description":"System identifier that owns the chat"},"id_tenant":{"type":"string","title":"Id Tenant","description":"Tenant identifier"},"id_user":{"type":"string","title":"Id User","description":"User identifier"},"id_chat":{"type":"string","title":"Id Chat","description":"Chat identifier"},"title":{"type":"string","title":"Title","description":"Chat title"},"id_prompt":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Id Prompt","description":"System prompt identifier"},"enabled":{"type":"boolean","title":"Enabled","description":"Indicates whether the chat is enabled or not"},"start_ts":{"type":"string","title":"Start Ts","description":"Chat creation timestamp (`YYYY-MM-DD hh:mm:ss.mmm` format)"},"last_ts":{"type":"string","title":"Last Ts","description":"Last chat conversation timestamp (`YYYY-MM-DD hh:mm:ss.mmm` format)"}},"type":"object","required":["id_tenant","id_user","id_chat","title","enabled","start_ts","last_ts"],"title":"ResponseChatSummaryItem"},"ResponseConversationItem":{"properties":{"type":{"type":"string","enum":["human","ai"],"title":"Type","description":"Indicates whether it is the user query or the LLM response"},"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id","description":"Interaction identifier"},"position":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Position","description":"Position of the interaction in the conversation (0-based)"},"text":{"type":"string","title":"Text","description":"Contains the text of the interaction"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title","description":"Chat title"},"id_prompt":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Id Prompt","description":"System prompt identifier"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled","description":"Indicates whether the chat is enabled or not"},"feedback":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Feedback","description":"Feedback of the AI answer"},"feedback_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Feedback Text","description":"Feedback text of the AI answer"},"context":{"items":{"anyOf":[{"type":"integer"},{"type":"string"},{"$ref":"#/components/schemas/SourceAi"}]},"type":"array","title":"Context","description":"Contextual information about the interaction"},"query_metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Query Metadata","description":"Metadata assigned to the query"},"timestamp":{"type":"string","title":"Timestamp","description":"Interaction timestamp (`YYYY-MM-DD hh:mm:ss.mmm` format)"}},"type":"object","required":["type","position","text","context","timestamp"],"title":"ResponseConversationItem"},"ResponseConversationItems":{"items":{"$ref":"#/components/schemas/ResponseConversationItem"},"type":"array","title":"ResponseConversationItems","description":"List of conversation items"},"ResponseCoverExtraction":{"properties":{"cover_b64":{"type":"string","title":"Cover B64","description":"Base64-encoded cover image, rendered from the first page"},"content_type":{"type":"string","title":"Content Type","description":"MIME type of the encoded image","default":"image/jpeg"}},"type":"object","required":["cover_b64"],"title":"ResponseCoverExtraction","description":"Inline base64, the `ResponsePdfAnonymization.doc_content` precedent — storage holds\nno binaries, so a reference-shaped response has nowhere to point.\n\nConsumers must not persist `cover_b64`; it is large but not secret, so `redact` leaves\nit alone and the producer keeps it out of traced output."},"ResponseDetail":{"properties":{"detail":{"type":"string","title":"Detail"}},"type":"object","required":["detail"],"title":"ResponseDetail","description":"FastAPI HTTPException body — auth/validation failures raised by the\nrequire_scope / admin-key dependencies (endpoints/__init__.py)."},"ResponseDlq":{"properties":{"entries":{"items":{"$ref":"#/components/schemas/DlqEntry"},"type":"array","title":"Entries"},"depth":{"type":"integer","title":"Depth"}},"type":"object","required":["entries","depth"],"title":"ResponseDlq"},"ResponseDlqAction":{"properties":{"entry":{"$ref":"#/components/schemas/DlqEntry"},"depth":{"type":"integer","title":"Depth"}},"type":"object","required":["entry","depth"],"title":"ResponseDlqAction","description":"Outcome of a replay or discard, including the resulting depth so a caller\ncan tell whether the alert should clear."},"ResponseDocumentIngestion":{"properties":{"id_doc":{"type":"integer","title":"Id Doc","description":"The store key the document is addressed by"},"pages":{"type":"integer","title":"Pages","description":"Number of pages/texts the document was split into"},"file_type":{"$ref":"#/components/schemas/FileType","description":"The file type ingestion used, whether supplied or auto-detected"},"already_ingested":{"type":"boolean","title":"Already Ingested","description":"The document existed and was left alone; nothing was re-ingested"}},"type":"object","required":["id_doc","pages","file_type","already_ingested"],"title":"ResponseDocumentIngestion","description":"`already_ingested` is the idempotency guard, not a convenience: delivery is\nat-least-once and storage hard-fails on an existing document without `overwrite`.\n\nWhile the `id_doc` namespace is shared it means \"an `id_doc` row exists\",\nnot \"yours does\"."},"ResponseError":{"properties":{"code":{"type":"integer","title":"Code"},"message":{"type":"string","title":"Message"}},"type":"object","required":["code","message"],"title":"ResponseError"},"ResponseFaqsGeneration":{"properties":{"faqs":{"items":{"prefixItems":[{"type":"string"},{"type":"string"}],"type":"array","maxItems":2,"minItems":2},"type":"array","title":"Faqs"}},"type":"object","required":["faqs"],"title":"ResponseFaqsGeneration"},"ResponseIssueApiKey":{"properties":{"id":{"type":"string","title":"Id"},"system":{"type":"string","title":"System"},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes"},"api_key":{"type":"string","title":"Api Key","description":"The plaintext API key. Shown only once, at issue time."},"created_at":{"type":"string","title":"Created At"},"expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires At"}},"type":"object","required":["id","system","scopes","api_key","created_at"],"title":"ResponseIssueApiKey"},"ResponseJobAccepted":{"properties":{"job_id":{"type":"string","title":"Job Id","examples":["1f0c1e0a-7b6e-4b9e-9b1a-2a7c8d9e0f11"]}},"type":"object","required":["job_id"],"title":"ResponseJobAccepted","description":"202 submit body (job_routes.py): the job was accepted; the result\narrives on callback_url, or via GET /api/v1/jobs/{job_id}/result when the\nsubmit degraded from sync-emulation (Location header)."},"ResponseJobDetail":{"properties":{"job":{"$ref":"#/components/schemas/JobRow"},"calls":{"items":{"$ref":"#/components/schemas/JobLlmCall"},"type":"array","title":"Calls","description":"Oldest first: the order the job made them"},"langfuse_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Langfuse Url","description":"Deep link to this job's Langfuse session, when a template is configured"}},"type":"object","required":["job","calls"],"title":"ResponseJobDetail"},"ResponseJobStatus":{"properties":{"job_id":{"type":"string","title":"Job Id"},"service":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Service","description":"Worker service slug, e.g. 'metadata-extraction'"},"task":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Task","description":"Task / scope name, e.g. 'metadata_extraction'"},"status":{"type":"string","title":"Status","description":"\"pending\" | \"ok\" | \"error\""},"response":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response","description":"The service's native response when status=ok"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Error message when status=error"},"error_code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Code","description":"The job's business HTTP status when status=error (e.g. 504 timeout)"}},"type":"object","required":["job_id","status"],"title":"ResponseJobStatus","description":"Poll body (jobs.py): 200 terminal (status ok|error), 202 pending.\nMirrors JobResult (app/models/orchestrator.py) minus id_tenant, which is\nnever exposed; None fields are omitted."},"ResponseJobs":{"properties":{"rows":{"items":{"$ref":"#/components/schemas/JobRow"},"type":"array","title":"Rows","description":"Newest first"},"stats":{"$ref":"#/components/schemas/JobStats"}},"type":"object","required":["rows","stats"],"title":"ResponseJobs"},"ResponseMetadataExtraction":{"properties":{"detected_kind":{"$ref":"#/components/schemas/DocumentKind"},"detected_language":{"anyOf":[{"$ref":"#/components/schemas/Language"},{"type":"null"}],"description":"ISO code of the document's main/original language, independent of the output language; null if undetermined"},"metadata":{"$ref":"#/components/schemas/ExtractedMetadata"},"extracted_fields":{"items":{"type":"string"},"type":"array","title":"Extracted Fields","description":"Names of metadata fields with a non-empty extracted value","default":[]},"pages_analyzed":{"type":"integer","title":"Pages Analyzed"}},"type":"object","required":["detected_kind","metadata","pages_analyzed"],"title":"ResponseMetadataExtraction"},"ResponsePdfAnonymization":{"properties":{"doc_content":{"type":"string","title":"Doc Content"},"stats":{"$ref":"#/components/schemas/PdfAnonymizationStats"}},"type":"object","required":["doc_content","stats"],"title":"ResponsePdfAnonymization"},"ResponsePrices":{"properties":{"prices":{"items":{"$ref":"#/components/schemas/UsagePriceRow"},"type":"array","title":"Prices"}},"type":"object","required":["prices"],"title":"ResponsePrices"},"ResponseQuestionAnswering":{"properties":{"answer":{"type":"string","title":"Answer"}},"type":"object","required":["answer"],"title":"ResponseQuestionAnswering"},"ResponseQuizGeneration":{"properties":{"topics":{"additionalProperties":{"items":{"$ref":"#/components/schemas/TopicDetails"},"type":"array"},"type":"object","title":"Topics"}},"type":"object","required":["topics"],"title":"ResponseQuizGeneration"},"ResponseScopes":{"properties":{"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes"}},"type":"object","required":["scopes"],"title":"ResponseScopes"},"ResponseSendQuery":{"properties":{"id_chat":{"type":"string","title":"Id Chat","description":"Chat identifier"},"id_query":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id Query","description":"Query identifier, for the matching stream"}},"type":"object","required":["id_chat"],"title":"ResponseSendQuery"},"ResponseSttOffline":{"properties":{"sentences":{"items":{"$ref":"#/components/schemas/Sentence"},"type":"array","title":"Sentences"},"text":{"type":"string","title":"Text"},"rag_augmented_txt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rag Augmented Txt"}},"type":"object","required":["sentences","text","rag_augmented_txt"],"title":"ResponseSttOffline"},"ResponseSummarization":{"properties":{"text":{"type":"string","title":"Text"}},"type":"object","required":["text"],"title":"ResponseSummarization"},"ResponseTopicsExtraction":{"properties":{"topics":{"items":{"type":"string"},"type":"array","title":"Topics","description":"The extracted topics"},"chunks_analyzed":{"type":"integer","title":"Chunks Analyzed","description":"Number of text chunks the clustering ran over"},"persisted":{"type":"boolean","title":"Persisted","description":"The topics were written back to the stored document"}},"type":"object","required":["topics","chunks_analyzed","persisted"],"title":"ResponseTopicsExtraction","description":"`persisted` reports the write-back rather than echoing the request; only `id_doc`\nmode can do one."},"ResponseTtsOffline":{"properties":{"audio_content":{"type":"string","title":"Audio Content","description":"Base64-encoded synthesized audio"},"content_type":{"type":"string","title":"Content Type","description":"MIME type of the encoded audio","default":"audio/mpeg"},"duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration Ms","description":"Audio duration when the provider reports it"}},"type":"object","required":["audio_content"],"title":"ResponseTtsOffline","description":"The synthesized audio, inline and base64-encoded.\n\nInline rather than a reference, which is the opposite of the stt *input* decision in\nthis same release. The asymmetry is deliberate and it is about magnitude, not\nconsistency: stt ingests hours of audio (tens of MB, and a WAV of ~115 MB per hour on\ndisk), while tts emits what someone asked to have read aloud — at 192 kbps that is\n~24 KB per second, so a 10-second prompt is ~240 KB and a full minute ~1.4 MB. That is\nPDF-sized, and ``pdf_anonymization`` already carries base64 PDFs up to a 25 MB cap on\nthis same plane.\n\nWhat inline buys, and why it won over a signed URL:\n\n- No bucket, no storage credential, and no signing. GCS signed URLs need a\n  service-account private key, and the audio service's storage credential may be a bare\n  API key, which cannot sign — while the Azure provider path carries no storage settings\n  at all. A reference-shaped response would have been unimplementable on half the\n  provider matrix.\n- No expiry to reason about. A reference has two clocks — the job's result window and\n  the URL's TTL — and a caller polling late would get a valid reference to a dead URL.\n- Nothing to garbage-collect. A bucket of synthesized audio is a retention policy\n  somebody has to own.\n\nThe cost, which the service must handle rather than discover: the payload is bounded by\nconfiguration (a ``413`` above the cap) rather than by nature, and it must be kept out\nof trace and log sinks — it is not a *secret*, so ``redaction.py`` will not touch it;\nthe producer has to exclude it explicitly the way stt excludes its own large fields."},"ResponseUsage":{"properties":{"events":{"items":{"$ref":"#/components/schemas/UsageEvent"},"type":"array","title":"Events","description":"Matching usage events, newest first"}},"type":"object","required":["events"],"title":"ResponseUsage"},"ResponseUsageSummary":{"properties":{"rows":{"items":{"$ref":"#/components/schemas/UsageSummaryRow"},"type":"array","title":"Rows","description":"Groups, heaviest token spend first"},"total_cost":{"type":"number","title":"Total Cost","description":"Sum of the priced rows only"},"currency":{"type":"string","title":"Currency"},"complete":{"type":"boolean","title":"Complete","description":"False when at least one group had no applicable price, i.e. total_cost understates reality"}},"type":"object","required":["rows","total_cost","currency","complete"],"title":"ResponseUsageSummary"},"Sentence":{"properties":{"text":{"type":"string","title":"Text"},"offset_ms":{"type":"integer","title":"Offset Ms"},"duration_ms":{"type":"integer","title":"Duration Ms"}},"type":"object","required":["text","offset_ms","duration_ms"],"title":"Sentence"},"Source":{"properties":{"text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text"},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Metadata"}},"type":"object","title":"Source"},"SourceAi":{"properties":{"id_doc":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Id Doc","description":"Identifier of the document to which the source belongs"},"text":{"type":"string","title":"Text","description":"Source text"},"page":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Page","description":"Page number within the document where the source text is present"},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Metadata","description":"Metadata of the source"}},"type":"object","required":["text"],"title":"SourceAi"},"SourceInfo":{"properties":{"id_doc":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Id Doc","description":"Document identifier"},"url_doc":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url Doc","description":"Document URL (for 'on-the-fly' documents only)"},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Metadata","description":"Document metadata"}},"type":"object","title":"SourceInfo"},"SummarizationType":{"type":"string","enum":["detailed","concise","bullet_list"],"title":"SummarizationType"},"TopicDetails":{"properties":{"question":{"type":"string","title":"Question"},"correct_answer":{"type":"string","title":"Correct Answer"},"wrong_answers":{"items":{"type":"string"},"type":"array","title":"Wrong Answers"},"explanation":{"type":"string","title":"Explanation"}},"type":"object","required":["question","correct_answer","wrong_answers","explanation"],"title":"TopicDetails"},"TopicReviewDetails":{"properties":{"title":{"type":"string","title":"Title","default":""},"description":{"type":"string","title":"Description","default":""}},"type":"object","title":"TopicReviewDetails"},"UsageEvent":{"properties":{"ts":{"type":"string","title":"Ts","description":"ISO-8601 timestamp of the call (UTC)"},"system":{"type":"string","title":"System","description":"Consumer system the API key was issued to"},"tenant":{"type":"string","title":"Tenant","description":"X-Tenant header of the call"},"user":{"type":"string","title":"User","description":"X-User header of the call"},"service":{"type":"string","title":"Service","description":"Service scope hit, e.g. 'pdf_anonymization'"},"status":{"type":"integer","title":"Status","description":"HTTP status returned to the caller"},"duration_ms":{"type":"integer","title":"Duration Ms"},"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Id","description":"Job the call created, when it created one — the join key to the job and its token spend"}},"type":"object","required":["ts","system","tenant","user","service","status","duration_ms"],"title":"UsageEvent","description":"One authenticated gateway call, as recorded in the per-key usage stream."},"UsagePriceItem":{"properties":{"effective_from":{"type":"string","format":"date","title":"Effective From","description":"First day this price applies; a change is a new row, so past reports stay reproducible"},"provider":{"type":"string","title":"Provider","description":"Provider label, e.g. googleGenAi (empty to price a model regardless of provider). Free-form on purpose: it is whatever an emitter reported, so retired labels stay priceable for past reports"},"model":{"type":"string","title":"Model"},"kind":{"type":"string","title":"Kind","description":"Token kind billed: input | output | cache_read | reasoning"},"unit_price":{"type":"number","minimum":0.0,"title":"Unit Price","description":"Cost of one token"},"currency":{"type":"string","maxLength":3,"minLength":3,"title":"Currency","description":"Defaults to USD, the currency Langfuse publishes list prices in","default":"USD"}},"type":"object","required":["effective_from","provider","model","kind","unit_price"],"title":"UsagePriceItem","description":"One versioned unit price. Prices are per SINGLE token — providers quote\nper million, so divide before posting."},"UsagePriceRow":{"properties":{"effective_from":{"type":"string","format":"date","title":"Effective From","description":"First day this price applies; a change is a new row, so past reports stay reproducible"},"provider":{"type":"string","title":"Provider","description":"Provider label, e.g. googleGenAi (empty to price a model regardless of provider). Free-form on purpose: it is whatever an emitter reported, so retired labels stay priceable for past reports"},"model":{"type":"string","title":"Model"},"kind":{"type":"string","title":"Kind","description":"Token kind billed: input | output | cache_read | reasoning"},"unit_price":{"type":"number","minimum":0.0,"title":"Unit Price","description":"Cost of one token"},"currency":{"type":"string","maxLength":3,"minLength":3,"title":"Currency","description":"Defaults to USD, the currency Langfuse publishes list prices in","default":"USD"},"source":{"type":"string","title":"Source","description":"langfuse (synced) | manual (published here) | unknown (predates this field)"}},"type":"object","required":["effective_from","provider","model","kind","unit_price","source"],"title":"UsagePriceRow","description":"A published price as it is read back, with who published it.\n\n``source`` lives here and not on ``UsagePriceItem`` on purpose: that model is the\nPUT body, and provenance is not a caller's to assert. A request claiming\n``source: \"langfuse\"`` is ignored, and the row it creates is ``manual``."},"UsageSummaryRow":{"properties":{"system":{"type":"string","title":"System"},"tenant":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tenant"},"provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Provider"},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"calls":{"type":"integer","title":"Calls"},"input_tokens":{"type":"integer","title":"Input Tokens"},"output_tokens":{"type":"integer","title":"Output Tokens"},"cache_read_tokens":{"type":"integer","title":"Cache Read Tokens"},"reasoning_tokens":{"type":"integer","title":"Reasoning Tokens"},"total_tokens":{"type":"integer","title":"Total Tokens"},"cost":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cost","description":"Cost from the versioned price table; null when no price covers this model"}},"type":"object","required":["system","calls","input_tokens","output_tokens","cache_read_tokens","reasoning_tokens","total_tokens"],"title":"UsageSummaryRow","description":"Token totals for one (system, tenant, provider, model) group."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VoiceInfo":{"properties":{"name":{"type":"string","title":"Name","description":"Voice name without the locale prefix, e.g. `ElsaNeural`"},"locale":{"type":"string","title":"Locale","description":"BCP-47 locale of the voice, e.g. `it-IT`"}},"type":"object","required":["name","locale"],"title":"VoiceInfo","description":"One synthesis voice, split the way the Azure Speech SDK wants it reassembled.\n\nThe SDK takes a single ``speech_synthesis_voice_name`` of the form\n``<locale>-<name>`` (e.g. ``it-IT-ElsaNeural``), so ``locale`` and ``name`` are two\nhalves of one identifier rather than independent fields — ``name`` alone selects\nnothing. Kept split because that is the shape the voices listing returns, and joining\nthem here would make the catalog and the request disagree."}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","description":"API key issued by the gateway admin. 401 if missing, unknown, disabled or expired.","in":"header","name":"X-Api-Key"},"TenantHeader":{"type":"apiKey","description":"Tenant on whose behalf the call is made. 400 if missing. Job results are only visible to this tenant. Identity context, not a credential: it grants no authority.","in":"header","name":"X-Tenant"},"UserHeader":{"type":"apiKey","description":"End user on whose behalf the call is made. 400 if missing. Recorded for usage accounting. Identity context, not a credential: it grants no authority.","in":"header","name":"X-User"},"AdminKeyAuth":{"type":"apiKey","description":"Static master key protecting the API-key administration endpoints.","in":"header","name":"X-Admin-Key"}}},"tags":[{"name":"metadata_extraction","description":"Extract structured metadata from a PDF. Sync-emulation or callback."},{"name":"pdf_anonymization","description":"Anonymize a PDF. Always 202 (jobs can run ~900 s): result via callback_url, or poll without one."},{"name":"summarization","description":"Summarize an ingested document (concise, detailed or bulleted list). Sync-emulation or callback."},{"name":"jobs","description":"Poll parked (no-callback) job results. Tenant-guarded: another tenant's job reads as 404."},{"name":"admin","description":"API-key administration (X-Admin-Key). Web console at admin/ui."},{"name":"health","description":"Liveness probe."}]}