{"openapi":"3.1.0","info":{"title":"TaxHarvest API","description":"\n**Italian legal & fiscal RAG system** — retrieval ibrido + Voyage rerank + LLM synthesis\nsu 215.000+ documenti normativi e giurisprudenziali italiani.\n\n## Pipeline\n1. **Cache Redis** (TTL 10 min) — cache hit ~100ms\n2. **Fast-path check** (v15) — pattern cheat-sheet → timeout 30s/90s\n3. **Hybrid retrieval** — Vector HNSW + FTS italiano + LIKE trigram + Reference + Article + Entity NER\n4. **Voyage Rerank-2.5-lite** — top 50 → top 15 (cross-encoder multilingue, 200M token free)\n5. **Cheat-sheet injection** — 89 pattern hardcoded + auto-cheatsheet dai top chunks (v18)\n6. **LLM synthesis** — DeepSeek-chat, temperatura 0.1, max_tokens 350\n\n## Authentication\nTutte le richieste richiedono `X-API-Key` header. Demo key: `taxharvest-demo-2026`.\n\n## Rate limit\n60 richieste/minuto per IP.\n\n## Production status\nv18 — 99% perfette (multi-judge median 3x) su 610 query test (testset + holdout).\n\n## UI\n- `/api-docs` — documentazione stile Anthropic/OpenAI\n- `/docs` — questa pagina (Swagger UI)\n- `/redoc` — alternativa ReDoc\n- `/graph` — knowledge graph interattivo\n","contact":{"name":"TaxHarvest Team","email":"dilan@getlingua.com"},"license":{"name":"Proprietary"},"version":"0.4.0"},"servers":[{"url":"https://bancadati.doczoom.ai","description":"Produzione (HTTPS)"}],"paths":{"/api/chat":{"post":{"tags":["Search"],"summary":"Chat","operationId":"chat_api_chat_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/search":{"post":{"tags":["Search"],"summary":"Search","operationId":"search_api_search_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/tool/{tool_name}":{"post":{"tags":["DocZoom"],"summary":"Execute Tool","operationId":"execute_tool_api_tool__tool_name__post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tool_name","in":"path","required":true,"schema":{"type":"string","title":"Tool Name"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sources":{"get":{"tags":["Sources"],"summary":"Api Sources","description":"Lista TUTTI i source con metadata per il filtering DocZoom.\n\nv25.97 — Enriched response: per ogni source ritorna `country`, `doc_count`,\n`modules` (lista moduli che la includono). DocZoom può filtrare per country\no per modulo direttamente lato API senza hardcoding.\n\nQuery params:\n    country: \"IT\" | \"AE\" | \"all\" (default all) — filtro paese\n    module: module_id (default None) — solo le source di un modulo specifico\n\nBackward compat: la chiave \"sources\" continua a essere lista di stringhe\n(per i client v1). Le info enriched sono in \"sources_detail\".\n\nResponse:\n    {\n      \"sources\": [\"abf\", \"ade_circolari\", ...],            // backward compat\n      \"sources_detail\": [\n        {\n          \"name\": \"uae_cbuae\",\n          \"country\": \"AE\",\n          \"doc_count\": 190,\n          \"modules\": [\"uae-banking-amlcft\"]\n        },\n        ...\n      ],\n      \"registered_scrapers\": [...],\n      \"manual_ingest_only\": [...],\n      \"count\": 67,\n      \"country_filter\": \"all\"\n    }","operationId":"api_sources_api_sources_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"country","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"}},{"name":"module","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Module"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/countries":{"get":{"tags":["Modules"],"summary":"Api Countries List","description":"Lista paesi disponibili nelle banche dati + statistiche.\n\nFrontend DocZoom usa questa per popolare Settings → Banche Dati senza\nhardcodare. Ogni paese include:\n  - code (ISO-2): \"IT\" | \"AE\"\n  - name: nome leggibile\n  - flag: emoji\n  - module_count: quanti moduli per quel paese (10 IT / 5 UAE)\n  - sources_count: quante distinct sources coperte dai moduli\n  - has_data: bool — true se c'è almeno 1 doc nel corpus per quel paese\n              (false = paese definito ma corpus vuoto)\n\nResponse:\n    {\n      \"countries\": [\n        {\"code\": \"IT\", \"name\": \"Italia\", \"flag\": \"🇮🇹\",\n         \"module_count\": 10, \"sources_count\": 35, \"has_data\": true},\n        {\"code\": \"AE\", \"name\": \"Emirati Arabi Uniti\", \"flag\": \"🇦🇪\",\n         \"module_count\": 5, \"sources_count\": 6, \"has_data\": true}\n      ],\n      \"count\": 2\n    }","operationId":"api_countries_list_api_countries_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/modules":{"get":{"tags":["Modules"],"summary":"Api Modules List","description":"Lista i moduli macro-area disponibili (Lexroom-style).\n\n10 moduli IT (Diritto Civile, Tributario, Penale, ecc. — mappati 1:1 ai\nmoduli Lexroom) + 5 moduli UAE (Tax, Banking AML/CFT, DIFC, ADGM,\nCommon Law). Ogni modulo aggrega source + metadata filter per restringere\nil retrieval a una specifica area giuridica.\n\nQuery params:\n    country (optional): \"IT\" | \"AE\" | \"all\" (default \"all\").\n                        Filtra l'elenco moduli per paese.\n\nExample:\n    GET /api/modules            → tutti i moduli (15 totali oggi)\n    GET /api/modules?country=IT → solo i 10 IT\n    GET /api/modules?country=AE → solo i 5 UAE\n\nResponse:\n    {\n      \"modules\": [\n        {\"id\": \"diritto-civile\", \"country\": \"IT\", \"name\": \"Diritto Civile\",\n         \"icon\": \"⚖️\", \"short\": \"Civile\", \"description\": \"...\",\n         \"filter_count\": 6, \"sources\": [\"cassazione\", \"normattiva\", ...]},\n        ...\n      ],\n      \"count\": 10,\n      \"usage_hint\": \"...\"\n    }","operationId":"api_modules_list_api_modules_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"country","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/modules/{module_id}":{"get":{"tags":["Modules"],"summary":"Api Module Detail","description":"Dettaglio di un singolo modulo + count documenti per source.\n\nEsempio: GET /api/modules/diritto-tributario\n\nResponse include:\n  - id, name, icon, short, description\n  - filters: lista raw delle (source, metadata) clauses\n  - sources: lista deduplicata source coperti\n  - doc_counts: {source -> N docs} che matchano il modulo (live count)\n  - total_docs: somma doc_counts","operationId":"api_module_detail_api_modules__module_id__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"module_id","in":"path","required":true,"schema":{"type":"string","title":"Module Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/legal-codes":{"get":{"tags":["Browse"],"summary":"Api Legal Codes","description":"Catalogo dei CODICI / LEGGI per il Codici Navigator di DocZoom.\n\nCountry-aware: stessa shape per IT e AE. Drop-in compatibile col\npattern usato dal frontend DocZoom (hardcoded CODES_CATALOG con\nslug + name + count + taxharvestCategory + UI metadata).\n\nPattern d'uso DocZoom:\n1. GET /api/legal-codes?country=IT  → lista codici per la pagina hub\n2. Click su codice → GET /api/source/{taxharvestSource}/documents\n   ?category={taxharvestCategory} → lista articoli paginati\n3. Click su articolo → GET /api/document/{id} → testo + detail\n\nResponse:\n    {\n      \"country\": \"IT\",\n      \"categories\": [\n        {\n          \"slug\": \"cc\",\n          \"name\": \"Codice Civile\",\n          \"shortName\": \"CC\",\n          \"description\": \"Diritto privato, contratti, famiglia, successioni\",\n          \"category\": \"civile\",\n          \"articles\": 3029,\n          \"taxharvestSource\": \"normattiva\",\n          \"taxharvestCategory\": \"Codice Civile\",\n          \"country\": \"IT\"\n        },\n        ...\n      ],\n      \"count\": 64\n    }\n\nPer country=AE: 4 catalog entries (1 per source UAE legislation),\nognuno con count = N laws nella source. Click su una entry → lista\nlaws via /api/source/{source}/documents.","operationId":"api_legal_codes_api_legal_codes_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"country","in":"query","required":false,"schema":{"type":"string","default":"IT","title":"Country"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/legal-judgments":{"get":{"tags":["Browse"],"summary":"Api Legal Judgments","description":"Lista strutturata delle SENTENZE / JUDGMENTS organizzate per browsing.\n\nCountry-aware: stessa shape per IT e AE. Supporta filter + paginazione.\nCf. anche gli endpoint shortcut /api/cassazione, /api/tar, /api/adgm, ecc.\n\nFilters:\n- country: IT | AE (default IT). Se omesso e authority è specificato,\n           viene dedotto da _AUTHORITY_MAP.\n- authority: slug autorità (cassazione|tar|cds|cgue|cedu|corte-costituzionale|\n             abf|agcm|agcom|anac|banca-italia|ivass|tributaria|adgm|difc|...)\n             Filtra automaticamente sources + title pattern. Sostituisce\n             il vecchio uso libero di `court`.\n- court: filtro libero su title (es. \"Roma\" dentro TAR, \"Sezione 6\")\n- year: filtro su anno (estratto da metadata o slug)\n- parties: substring match su title (es. \"Salehiya\", \"NMC Healthcare\")\n- q: ricerca full-text su title (fallback se parties non basta)\n- page: 1-indexed\n- per_page: default 20, max 100\n\nResponse:\n    {\n      \"country\": \"AE\",\n      \"judgments\": [\n        {\n          \"id\": 12345,\n          \"title\": \"Sowwah Square Investment v Forever Rose Retail\",\n          \"court\": \"ADGM Court of First Instance\",\n          \"court_tier\": \"CFI\",\n          \"year\": 2026,\n          \"case_number\": \"ADGMCFI-2025-262\",\n          \"parties\": [\"Sowwah Square Investment\", \"Forever Rose Retail\"],\n          \"source\": \"uae_adgm_courts\",\n          \"url\": \"https://...\",\n          \"chunks\": 12,\n          \"country\": \"AE\"\n        },\n        ...\n      ],\n      \"pagination\": {\"page\": 1, \"per_page\": 20, \"total\": 102, \"pages\": 6},\n      \"filters_applied\": {...}\n    }","operationId":"api_legal_judgments_api_legal_judgments_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"country","in":"query","required":false,"schema":{"type":"string","default":"IT","title":"Country"}},{"name":"court","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Court"}},{"name":"authority","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authority"}},{"name":"year","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year"}},{"name":"parties","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parties"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Per Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cassazione":{"get":{"tags":["Browse"],"summary":"Api Cassazione","description":"Lista Cassazione (336K+ docs: civile + penale + costituzionale).\n\nFiltro `court` opzionale per sub-tipo (es. \"Civile\", \"Penale\", \"SU\").\nShortcut di GET /api/legal-judgments?authority=cassazione.","operationId":"api_cassazione_api_cassazione_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"year","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year"}},{"name":"court","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Court"}},{"name":"parties","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parties"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Per Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cassazione/{numero}/{anno}":{"get":{"tags":["Browse"],"summary":"Api Cassazione Lookup","description":"Lookup di una Cassazione specifica per numero+anno.\n\nEsempio: GET /api/cassazione/12345/2024.\nRitorna il primo match nel title (pattern \"n. NUMERO/ANNO\").","operationId":"api_cassazione_lookup_api_cassazione__numero___anno__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"numero","in":"path","required":true,"schema":{"type":"integer","title":"Numero"}},{"name":"anno","in":"path","required":true,"schema":{"type":"integer","title":"Anno"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/tar":{"get":{"tags":["Browse"],"summary":"Api Tar","description":"Lista TAR (~260K provvedimenti). Filtro `sede` per sede TAR\n(es. \"ROMA\", \"MILANO\", \"NAPOLI\"). Match su title prefix.","operationId":"api_tar_api_tar_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"sede","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sede"}},{"name":"year","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year"}},{"name":"parties","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parties"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Per Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/consiglio-stato":{"get":{"tags":["Browse"],"summary":"Api Consiglio Stato","description":"Lista Consiglio di Stato + CGA + Adunanza Plenaria.","operationId":"api_consiglio_stato_api_consiglio_stato_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"year","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year"}},{"name":"parties","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parties"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Per Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/corte-costituzionale":{"get":{"tags":["Browse"],"summary":"Api Corte Costituzionale","description":"Lista Corte Costituzionale (3.808 docs: sentenze, ordinanze, decreti).","operationId":"api_corte_costituzionale_api_corte_costituzionale_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"year","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year"}},{"name":"parties","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parties"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Per Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cgue":{"get":{"tags":["Browse"],"summary":"Api Cgue","description":"Lista Corte di Giustizia UE — sentenze + comunicati in IT (1.410 docs).","operationId":"api_cgue_api_cgue_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"year","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year"}},{"name":"parties","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parties"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Per Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cedu":{"get":{"tags":["Browse"],"summary":"Api Cedu","description":"Lista Corte EDU (HUDOC) — 9 sentenze (parziale, in espansione).","operationId":"api_cedu_api_cedu_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"year","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year"}},{"name":"parties","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parties"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Per Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/giustizia-tributaria":{"get":{"tags":["Browse"],"summary":"Api Giustizia Tributaria","description":"Lista Giustizia Tributaria — Massimario CGT (1.109 massime).","operationId":"api_giustizia_tributaria_api_giustizia_tributaria_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"year","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year"}},{"name":"parties","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parties"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Per Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/abf":{"get":{"tags":["Browse"],"summary":"Api Abf","description":"Lista decisioni ABF (Arbitro Bancario Finanziario) — 280 docs.","operationId":"api_abf_api_abf_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"year","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year"}},{"name":"parties","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parties"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Per Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/agcm":{"get":{"tags":["Browse"],"summary":"Api Agcm","description":"Lista bollettini AGCM (Antitrust) — 808 docs.","operationId":"api_agcm_api_agcm_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"year","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year"}},{"name":"parties","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parties"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Per Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/agcom":{"get":{"tags":["Browse"],"summary":"Api Agcom","description":"Lista delibere AGCOM (Comunicazioni) — 537 docs.","operationId":"api_agcom_api_agcom_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"year","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year"}},{"name":"parties","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parties"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Per Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sentenze-uae":{"get":{"tags":["Browse"],"summary":"Api Sentenze Uae","description":"Lista sentenze UAE court (102 docs: 80 ADGM CFI + 22 DIFC).\n\nFiltro `court` opzionale per nome (es. \"Court of Appeal\", \"Small Claims\").","operationId":"api_sentenze_uae_api_sentenze_uae_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"year","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year"}},{"name":"parties","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parties"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"court","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Court"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Per Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/adgm":{"get":{"tags":["Browse"],"summary":"Api Adgm","description":"Lista sentenze ADGM Courts (Abu Dhabi Global Market) — 80 docs CFI.","operationId":"api_adgm_api_adgm_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"year","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year"}},{"name":"parties","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parties"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Per Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/difc":{"get":{"tags":["Browse"],"summary":"Api Difc","description":"Lista sentenze DIFC Courts (Dubai International Financial Centre) — 22 docs.","operationId":"api_difc_api_difc_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"year","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year"}},{"name":"parties","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parties"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Per Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/tools":{"get":{"tags":["DocZoom"],"summary":"Api Tools","operationId":"api_tools_api_tools_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/alerts/subscriptions":{"post":{"tags":["Alerts"],"summary":"Api Alert Subscribe","description":"Crea una subscription alert.\n\nEsempi:\n  {\"sub_type\": \"source\", \"target\": \"ade_circolari\"}\n  {\"sub_type\": \"module\", \"target\": \"diritto-tributario\"}\n  {\"sub_type\": \"collection\", \"target\": \"forfettario\"}\n  {\"sub_type\": \"reference\", \"target\": \"art. 2 D.Lgs. 74/2000\"}\n  {\"sub_type\": \"keyword\", \"target\": \"superbonus\"}\n  {\"sub_type\": \"country\", \"target\": \"AE\"}","operationId":"api_alert_subscribe_api_alerts_subscriptions_post","security":[{"APIKeyHeader":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlertSubscribeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Alerts"],"summary":"Api Alert List Subs","description":"Lista le subscription dell'utente (identificato via X-API-Key hash).","operationId":"api_alert_list_subs_api_alerts_subscriptions_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"active_only","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Active Only"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/alerts/subscriptions/{sub_id}":{"delete":{"tags":["Alerts"],"summary":"Api Alert Delete Sub","operationId":"api_alert_delete_sub_api_alerts_subscriptions__sub_id__delete","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"sub_id","in":"path","required":true,"schema":{"type":"integer","title":"Sub Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Alerts"],"summary":"Api Alert Toggle Sub","description":"Attiva/disattiva una subscription senza eliminarla.","operationId":"api_alert_toggle_sub_api_alerts_subscriptions__sub_id__patch","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"sub_id","in":"path","required":true,"schema":{"type":"integer","title":"Sub Id"}},{"name":"active","in":"query","required":true,"schema":{"type":"boolean","title":"Active"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/alerts":{"get":{"tags":["Alerts"],"summary":"Api Alert List Events","description":"Lista alert events per l'utente. Default: scan + list.\n\n- scan=true (default): scan documents creati dopo last_polled_at e\n  crea nuovi alert_events per le sub attive. Atomico per subscription.\n- scan=false: solo list events già esistenti (no scan).\n- unread_only: filtra solo events non letti.\n- limit/offset: paginazione.","operationId":"api_alert_list_events_api_alerts_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"unread_only","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Unread Only"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"scan","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Scan"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/alerts/{event_id}/read":{"post":{"tags":["Alerts"],"summary":"Api Alert Mark Read","operationId":"api_alert_mark_read_api_alerts__event_id__read_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"event_id","in":"path","required":true,"schema":{"type":"integer","title":"Event Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/alerts/mark-all-read":{"post":{"tags":["Alerts"],"summary":"Api Alert Mark All Read","operationId":"api_alert_mark_all_read_api_alerts_mark_all_read_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/alerts/stats":{"get":{"tags":["Alerts"],"summary":"Api Alert Stats","description":"Counts utili per badge UI: subs attive + events totali + non-letti.","operationId":"api_alert_stats_api_alerts_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/scheduler/status":{"get":{"tags":["System"],"summary":"Api Scheduler Status","description":"Stato scheduler: per ogni source ritorna ultimo doc scrapato + delta.\n\nPermette a DocZoom (e all'admin UI) di sapere quando ogni source ha\nricevuto l'ultimo update — fondamentale per debug \"perché non vedo\nsentenze nuove?\" e per calibrare le subscription alert.\n\nResponse:\n    {\n      \"sources\": [\n        {\"source\": \"ade_circolari\", \"last_doc_at\": \"2026-05-19T08:15:00\",\n         \"hours_since_last\": 1.5, \"total_docs\": 781,\n         \"scheduled\": true, \"cron_hint\": \"08:15 daily\"},\n        ...\n      ],\n      \"count\": 67,\n      \"stale_threshold_h\": 48,\n      \"stale_sources\": [...]\n    }","operationId":"api_scheduler_status_api_scheduler_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/harvest":{"post":{"tags":["System"],"summary":"Api Harvest","operationId":"api_harvest_api_harvest_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HarvestRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/doczoom/query":{"post":{"tags":["DocZoom"],"summary":"Doczoom Query","description":"Ricerca ibrida ottimizzata per Doczoom (vector + entities + ontology).","operationId":"doczoom_query_api_doczoom_query_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DoczoomQueryRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DoczoomQueryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/doczoom/retrieve-legal":{"post":{"tags":["DocZoom"],"summary":"Legal-aware retrieve con practice_area routing + must_include anchors","description":"v25.77 — Legal-aware retrieve.\n\nPipeline:\n1. Routing: practice_area + sub_area -> (sources, codici, macro_aree, must_include, exclude)\n2. Issue classifier: pattern matching su query -> tag issues\n3. Query expansion: aggiunta query rewriting tematiche dal routing\n4. Must include resolver: fetch deterministico anchor articoli\n5. Hybrid retrieval con filter sources + post-filter exclude_topics\n6. Authority mix balancing: top_k distribuito tra norms / case_law / practice\n7. Explain: ritorna metadata debug del retrieval (se requested)\n\nEsempio:\n    POST {\n        \"query\": \"clausola non concorrenza 36 mesi italia/europa €500\",\n        \"practice_area\": \"lavoro\",\n        \"sub_area\": \"patto_non_concorrenza\",\n        \"explain\": true\n    }\nRitorna chunks con Art. 2125 c.c. SEMPRE incluso + Cassazione lavoro\npertinente + 0 chunks pubblico impiego/previdenza.","operationId":"doczoom_retrieve_legal_api_doczoom_retrieve_legal_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DoczoomRetrieveLegalRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DoczoomRetrieveLegalResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/doczoom/practice-areas":{"get":{"tags":["DocZoom"],"summary":"Lista practice_area + sub_area supportate da /retrieve-legal","description":"Ritorna la tassonomia completa practice_area -> sub_areas con routing.\n\nUse case: client DocZoom carica questo all'avvio per popolare i picker\nUI e validare practice_area prima di chiamare /retrieve-legal.\n\nv25.80: strutturato per practice_area con lista di sub_areas + dettaglio\nrouting (sources, codici, macro_aree, must_include defaults, exclude_topics).","operationId":"doczoom_practice_areas_api_doczoom_practice_areas_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/doczoom/retrieve":{"post":{"tags":["DocZoom"],"summary":"BYOL retrieve (chunks pronti per il TUO LLM)","description":"Retrieve-only endpoint con default ottimizzati per Bring Your Own LLM.\n\nDifferenze rispetto a /api/doczoom/query:\n- top_k=15 default (vs 8) — più chunks per LLM\n- min_relevance=0.4 default (vs 0.0) — filtra rumore\n- context_format='llm_xml' default — pronto per Anthropic/OpenAI tool use\n- group_by_document=true default — riduce context size\n\nLo stesso engine di /query (hybrid + cross-encoder rerank + junk filter\n+ title boost + source hierarchy + vigenza penalty + dedup + confidence).\nNessuna synthesis LLM — il client compone la risposta col proprio LLM.\n\nVedi sezione \"BYOL flow\" in /api-docs per esempio Python end-to-end.","operationId":"doczoom_retrieve_api_doczoom_retrieve_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DoczoomRetrieveRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DoczoomQueryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/doczoom/answer":{"post":{"tags":["DocZoom"],"summary":"Doczoom Answer","description":"End-to-end RAG: retrieval + LLM synthesis con prompt strict no-hallucination.\n\nv19: tool intent routing — query \"verifica P.IVA X\", \"tasso cambio X\",\n\"DPR X/Y\", \"codice fiscale X\" bypassano RAG e chiamano direttamente il tool\ncorrispondente (~1-2s vs 6-15s, output preciso da fonte ufficiale realtime).","operationId":"doczoom_answer_api_doczoom_answer_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DoczoomAnswerRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DoczoomAnswerResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/doczoom/stream":{"post":{"tags":["DocZoom"],"summary":"Doczoom Stream","description":"End-to-end RAG con eventi SSE tipizzati per timeline UI Onyx-like.\n\nEventi emessi:\n  - stage   : {\"id\", \"state\": start|done|skipped, \"label\"?, \"icon\"?, \"ms\"?, \"details\"?, \"summary\"?}\n  - sources : {\"sources\", \"chunks\", \"confidence_level\", \"top_score\", ...}\n  - delta   : {\"text\"} — token incrementali del LLM\n  - meta    : {\"timing_ms\", \"llm_provider\", \"llm_model\", ...}\n  - done    : {} — fine stream\n  - error   : {\"message\"}","operationId":"doczoom_stream_api_doczoom_stream_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DoczoomAnswerRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/doczoom/tool":{"post":{"tags":["DocZoom"],"summary":"Doczoom Tool","operationId":"doczoom_tool_api_doczoom_tool_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DoczoomToolRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/doczoom/capabilities":{"get":{"tags":["DocZoom"],"summary":"Doczoom Capabilities","operationId":"doczoom_capabilities_api_doczoom_capabilities_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/doczoom/reference":{"post":{"tags":["Reference"],"summary":"Doczoom Reference","description":"Cerca documenti per estremi esatti. Accetta stringa libera o campi strutturati.\n\nv25.28 (3 mag 2026): fix bug critico — il parser ora estrae anche:\n- articolo specifico (\"art. X\", \"articolo X\") con varianti -bis/-ter\n- codici abbreviati (c.c., c.p., c.p.c., c.p.p., tuir, ecc.) → nome canonico\n- filtering article-specific quando presente (no più \"primi 10 ORDER BY id DESC\")","operationId":"doczoom_reference_api_doczoom_reference_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferenceSearchRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/doczoom/articles":{"post":{"tags":["Reference"],"summary":"Doczoom Articles","description":"Fetch articoli specifici di una legge o codice (audit-friendly).\n\nSchema strutturato per use case \"audit di conformità su tipo contratto\":\ninvece di sperare che il semantic search trovi le norme imperative,\nil client predefinisce la whitelist di articoli e li ottiene esattamente.\n\nEsempi:\n```\nPOST /api/doczoom/articles\n{\"law\": \"L. 392/1978\", \"articles\": [27, 32, 34, 36]}\n→ 4 articoli del titolo IV equo canone\n\nPOST /api/doczoom/articles\n{\"codice\": \"c.c.\", \"articles\": [1384, 2043, 2112]}\n→ 3 articoli del Codice Civile\n\nPOST /api/doczoom/articles\n{\"codice\": \"TUIR\", \"articles\": [\"54\", \"85\"]}\n→ 2 articoli TUIR\n```\n\nReturns: lista di oggetti articolo, in ORDINE preservato.\nOgni articolo ha: title, url, source, text (se include_text=true), citation.\n\nArticoli non trovati ritornano `{found: false}` invece di errore.\n\n**v25.31**: Redis cache TTL 24h (output deterministico, alto hit rate\natteso da plugin che ripete stessi articoli per ContractType).","operationId":"doczoom_articles_api_doczoom_articles_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArticlesRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/compliance/contracts":{"get":{"tags":["Compliance"],"summary":"Lista tipi contratto disponibili per audit conformità","description":"Ritorna la lista dei `ContractType` curati con label + description.\n\nUse case: il plugin DocZoom Word popola la dropdown \"Tipo contratto\"\nnella UI. Quando l'utente seleziona un tipo, il plugin chiama\n`GET /api/compliance/contracts/{type}` per ottenere la whitelist\ndi norme imperative.","operationId":"list_contract_types_api_compliance_contracts_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/compliance/contracts/{contract_type}":{"get":{"tags":["Compliance"],"summary":"Whitelist norme imperative per audit conformità su tipo contratto","description":"Ritorna la whitelist di norme imperative + tipiche violazioni per un\ntipo di contratto. Pensato per il flow Conformità del plugin DocZoom Word.\n\nArgs:\n    contract_type: chiave del tipo contratto (vedi\n        `/api/compliance/contracts` per la lista).\n    fetch_text: se True, fetcha il testo letterale degli articoli via\n        chiamata interna a /articles. Default False per response leggera.\n        Se True: latenza ~200-500ms (con cache 24h dopo la prima call).\n\nReturns: imperative_norms + tipiche_violazioni + (opzionale) full articles.","operationId":"get_contract_compliance_api_compliance_contracts__contract_type__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"contract_type","in":"path","required":true,"schema":{"type":"string","title":"Contract Type"}},{"name":"fetch_text","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Fetch Text"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/reference/{normalized_ref}":{"get":{"tags":["Reference"],"summary":"Reference Lookup","description":"Lookup diretto per riferimento normalizzato (es. dpr_633_1972).","operationId":"reference_lookup_api_reference__normalized_ref__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"normalized_ref","in":"path","required":true,"schema":{"type":"string","title":"Normalized Ref"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/reference/{normalized_ref}/history":{"get":{"tags":["Reference"],"summary":"Reference History","description":"Catena di modifiche per un riferimento normativo.","operationId":"reference_history_api_reference__normalized_ref__history_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"normalized_ref","in":"path","required":true,"schema":{"type":"string","title":"Normalized Ref"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/reference/{normalized_ref}/cited-by":{"get":{"tags":["Reference"],"summary":"Reference Cited By","description":"Chi cita questo riferimento normativo.","operationId":"reference_cited_by_api_reference__normalized_ref__cited_by_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"normalized_ref","in":"path","required":true,"schema":{"type":"string","title":"Normalized Ref"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/articles/{article_number}":{"get":{"tags":["Reference"],"summary":"Article Search","description":"Cerca un articolo per numero (es. '13' restituisce Art. 13 da tutti gli atti).","operationId":"article_search_api_articles__article_number__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"article_number","in":"path","required":true,"schema":{"type":"string","title":"Article Number"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/stats":{"get":{"tags":["System"],"summary":"Api Stats","description":"Stats globali + per source. Cache Redis 60s per evitare ~30 COUNT su\nogni refresh del frontend (prima: 1.1s con 1.7M chunks, ora: <10ms da cache).","operationId":"api_stats_api_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/audit/stats":{"get":{"tags":["System"],"summary":"Api Audit Stats","description":"Stats audit log: tasso abstention, avg score, factuality issues.\n\nUtile per monitoring qualità retrieval in produzione. Esempio:\n`{\"total_queries\": 1523, \"abstain_rate\": 0.04, \"avg_top_score\": 0.67}`","operationId":"api_audit_stats_api_audit_stats_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/audit/recent":{"get":{"tags":["System"],"summary":"Api Audit Recent","description":"Ultime N entry audit log (per dashboard / debug). Max 200.","operationId":"api_audit_recent_api_audit_recent_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"abstained_only","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Abstained Only"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/audit/analytics":{"get":{"tags":["System"],"summary":"Api Audit Analytics","description":"Analytics avanzate per dashboard quality monitoring.\n\nRestituisce: latency p50/p95/p99, score distribution, abstain rate,\nfactuality issue rate, top API keys, calls per hour 24h.","operationId":"api_audit_analytics_api_audit_analytics_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/audit/trending":{"get":{"tags":["System"],"summary":"Api Audit Trending","description":"Trending DAILY di hallu_rate, judge_rate, re-prompt_rate, latency.\n\nv25.34 (3 mag 2026): aggregato per giorno per detection di regressioni\nin real-time. Sostituisce la necessità di lanciare baseline 305q ($8)\nad ogni cambio del sistema — basta guardare le metriche giornaliere.\n\nReturns: lista per giorno con counts + percentuali.","operationId":"api_audit_trending_api_audit_trending_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":14,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/dashboard":{"get":{"tags":["System"],"summary":"Admin Dashboard","description":"Dashboard quality monitoring (HTML + JS client-side).\n\nPubblico ma carica dati solo se utente fornisce X-API-Key in header.","operationId":"admin_dashboard_admin_dashboard_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/source/{source_name}/categories":{"get":{"tags":["Sources"],"summary":"Source Categories","description":"Per una source, ritorna le macro-categorie (es. per normattiva: CC, CP, TUIR, ecc.).\n\nPer normattiva usa il campo `metadata_json.codice` per raggruppare.\nPer altre source raggruppa per `doc_type` (default).\n\nQuery param `level` (opzionale, v25.13):\n- `sede`: per giustizia_amministrativa, ritorna distribuzione per sede TAR\n  (Roma, Milano, Napoli, ...) con count per ognuna.\n- `sezione`: distribuzione per sezione (es. \"SEZIONE 4T\")\n- `year`: distribuzione per anno (estratto da metadata num_provvedimento)\n\nv25.8 (30 apr 2026):\n- Ogni entry contiene `slug` (per normattiva) usabile come parametro stable\n  su `/api/source/normattiva/documents?category=<slug>`.\n- I doc_type \"tecnici\" non codificati (decreto_legislativo, legge, dpr,\n  decreto_legge, ...) vengono raggruppati sotto un'unica voce con\n  slug=`altri` invece di inquinare la dropdown frontend.","operationId":"source_categories_api_source__source_name__categories_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"source_name","in":"path","required":true,"schema":{"type":"string","title":"Source Name"}},{"name":"level","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Level"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/source/{source_name}/documents":{"get":{"tags":["Sources"],"summary":"Source Documents","description":"Lista documenti di una source, filtrabile per categoria + filtri secondari.\n\nPer normattiva, `category` accetta:\n  - Slug stable (es. `cc`, `cp`, `tuir`, `tub`) — vedi NORMATTIVA_SLUG_MAP\n  - Nome canonico esteso (es. \"Codice Civile\", \"TUIR - Testo Unico...\")\n  - `altri` per gli atti non codificati (decreto_legislativo, legge, dpr, ...)\n  - Doc_type tecnico singolo (es. `decreto_legge`) per filtro fine\n\nPer altre source, `category` matcha `doc_type` come prima.\n\nFiltri secondari (v25.13, solo `giustizia_amministrativa`):\n  - `sede=ROMA|MILANO|...` filtra per sede TAR (case-insensitive)\n  - `sezione=4T|II|...` filtra per sezione (substring match)\n  - `year=2026|2025|...` filtra per anno (estratto da num_provvedimento)\n  - `tipo=SENTENZA|ORDINANZA|DECRETO|PARERE` filtra per tipo provvedimento\n\nv25.8: aliasing slug → nome canonico per normattiva.\nv25.13: aggiunti filtri sede/sezione/year/tipo per giustizia_amministrativa.","operationId":"source_documents_api_source__source_name__documents_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"source_name","in":"path","required":true,"schema":{"type":"string","title":"Source Name"}},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"}},{"name":"sede","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sede"}},{"name":"sezione","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sezione"}},{"name":"year","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Year"}},{"name":"tipo","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tipo"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/document/{doc_id}":{"get":{"tags":["Sources"],"summary":"Get Document Full","description":"Ritorna metadati + TUTTI i chunks (testo completo) di un documento.\n\nv25.8.2 (30 apr 2026): aggiunto perche' il frontend DocZoom (Codici\nNavigator) chiamava /api/source/<X>/documents che ritorna SOLO\nmetadata (no text). UI mostrava \"Testo non disponibile per questo\nchunk\" anche quando il DB aveva i chunks. Questo endpoint chiude\nquel gap esponendo:\n  - metadati documento (title, url, codice, articolo, ecc.)\n  - lista chunks ordered by chunk_index\n  - full_text concatenato per rendering diretto","operationId":"get_document_full_api_document__doc_id__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/doc/{doc_id}":{"get":{"tags":["Sources"],"summary":"Document Viewer","description":"Lettore pubblico del testo completo di un documento.\n\nTarget consigliato del bottone 'Apri fonte' in DocZoom: a differenza della\nSPA ItalGiure (#id=...) — che costringe l'utente a reinserire il codice della\nsentenza — questa pagina apre direttamente il testo che abbiamo in DB, con in\nfondo il link alla fonte ufficiale + un bottone Scarica (PDF/Word). Pubblico\n(no API key): i contenuti sono fonti legali pubbliche.","operationId":"document_viewer_doc__doc_id__get","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}},{"name":"highlight","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Highlight"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/doc/{doc_id}/download":{"get":{"tags":["Sources"],"summary":"Download Document","description":"Scarica il documento come PDF o Word (.docx), formattato stile DocZoom.\n\nPubblico (come il viewer). `format` = pdf | docx.","operationId":"download_document_doc__doc_id__download_get","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}},{"name":"format","in":"query","required":false,"schema":{"type":"string","default":"pdf","title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/collections":{"get":{"tags":["Sources"],"summary":"Api List Collections","description":"Lista piatta di tutte le raccolte.\n\nParametri:\n  level: filtra per livello 1 (profilo), 2 (sotto-specializzazione), 3 (materia)\n  with_stats: include conteggio documenti per ogni raccolta (piu' lento)","operationId":"api_list_collections_api_collections_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"level","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Level"}},{"name":"with_stats","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"With Stats"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/collections/tree":{"get":{"tags":["Sources"],"summary":"Api Collections Tree","description":"Struttura ad albero per UI di selezione: profili -> sotto-specializzazioni.\n\nLe materie trasversali (livello 3) sono esposte in una lista separata.","operationId":"api_collections_tree_api_collections_tree_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/collections/{slug}":{"get":{"tags":["Sources"],"summary":"Api Collection Detail","description":"Dettaglio di una raccolta con statistiche e breakdown per source.","operationId":"api_collection_detail_api_collections__slug__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"with_stats","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"With Stats"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/collections/{slug}/documents":{"get":{"tags":["Sources"],"summary":"Api Collection Documents","description":"Lista documenti di una raccolta, paginata.","operationId":"api_collection_documents_api_collections__slug__documents_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/recent-documents":{"get":{"tags":["Sources"],"summary":"Recent Documents","description":"Ultimi documenti inseriti nel DB.","operationId":"recent_documents_api_recent_documents_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":10,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/health":{"get":{"tags":["System"],"summary":"Health","description":"Liveness check (no auth, accept GET + HEAD per UptimeRobot).","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"head":{"tags":["System"],"summary":"Health","description":"Liveness check (no auth, accept GET + HEAD per UptimeRobot).","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health/full":{"get":{"tags":["System"],"summary":"Health Full","description":"Comprehensive health check per UptimeRobot (no API key, token URL).\n\nAggrega: liveness API + 35 sources DB check + Cloudflare proxy 5 fonti.\nUptimeRobot usa keyword \"ok\":false per detectare degradation.\n\nToken: usa env HEALTH_TOKEN. Default fallback = api_key (legacy).\nURL pattern: /health/full?token=<HEALTH_TOKEN>","operationId":"health_full_health_full_get","parameters":[{"name":"token","in":"query","required":false,"schema":{"type":"string","default":"","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"head":{"tags":["System"],"summary":"Health Full","description":"Comprehensive health check per UptimeRobot (no API key, token URL).\n\nAggrega: liveness API + 35 sources DB check + Cloudflare proxy 5 fonti.\nUptimeRobot usa keyword \"ok\":false per detectare degradation.\n\nToken: usa env HEALTH_TOKEN. Default fallback = api_key (legacy).\nURL pattern: /health/full?token=<HEALTH_TOKEN>","operationId":"health_full_health_full_get","parameters":[{"name":"token","in":"query","required":false,"schema":{"type":"string","default":"","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/health":{"get":{"tags":["System"],"summary":"Admin Health Dashboard","description":"Dashboard HTML interna — monitoring real-time TUTTO TaxHarvest.\n\nMostra in 1 pagina:\n- Sources health: ultimi runs, errori, days since last new doc\n- Proxy health: trend ultimi 7 giorni dei 5 domini bloccati\n- Audit log: query stats ultime 24h (avg confidence, abstention rate)\n- Disk usage + DB size + container status\n\nSelf-refresh ogni 60s. No JS dependencies (charts in inline SVG).","operationId":"admin_health_dashboard_admin_health_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/health/sources":{"get":{"tags":["System"],"summary":"Health Sources","description":"Health check di TUTTE le 35 fonti registered via harvest_runs.\n\nPer ogni source verifica:\n- last_run: timestamp ultima esecuzione\n- last_new: ultima volta che ha aggiunto nuovi documenti\n- days_since_run: giorni dall'ultimo run (alert se > expected_freq*2)\n- days_since_new: giorni dall'ultimo doc nuovo\n\nSoglie soft per source (in giorni):\n- daily sources (cass, GA, GU, AdE): warn 2gg, critical 5gg\n- weekly sources (CNEL, OIC, dogi): warn 10gg, critical 20gg\n- rare sources (TUEL, EUR-Lex): warn 30gg, critical 60gg\n\nHTTP 200 = tutte healthy\nHTTP 207 = 1-3 source warn\nHTTP 503 = >=4 source critical OR proxy down\n\nUsabile da UptimeRobot per alert centrale.","operationId":"health_sources_api_health_sources_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/health/proxy":{"get":{"tags":["System"],"summary":"Health Proxy","description":"Health check del Cloudflare Worker proxy + 5 fonti bloccate.\n\nv25.63: legge l'ultimo run del cron `proxy_health_check` da\n`proxy_health_log` invece di fare probe live.\n- Risponde in <100ms (UptimeRobot 10s timeout safe)\n- No auth required (è un public health endpoint, no PII esposto)\n- Cron 4×/giorno (ogni 6h) tiene il dato aggiornato\n- Se l'ultimo run è > 12h fa, ritorna 503 (cron stuck/morto)\n\nHTTP 200 = tutto OK\nHTTP 207 = warning (1-2 fonti con problemi, es. WAF intermittente)\nHTTP 503 = critico (>=3 fonti down, oppure cron data stale)","operationId":"health_proxy_api_health_proxy_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/system-info":{"get":{"tags":["System"],"summary":"System Info","description":"Restituisce la config dinamica del sistema (modello LLM, embedder, reranker).\nUsato dal frontend per mostrare info reali nell'header.","operationId":"system_info_api_system_info_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/":{"get":{"summary":"Serve Ui","description":"Serve l'interfaccia chat di test.","operationId":"serve_ui__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/graph":{"get":{"summary":"Serve Graph","description":"Serve il Knowledge Graph interattivo.","operationId":"serve_graph_graph_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api-docs":{"get":{"summary":"Serve Api Docs","description":"Serve la documentazione API stile Anthropic/OpenAI.","operationId":"serve_api_docs_api_docs_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/alerts":{"get":{"summary":"Serve Alerts Page","description":"Serve la UI standalone Norm Alerts (v25.101).","operationId":"serve_alerts_page_alerts_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/benchmark":{"get":{"summary":"Serve Benchmark Page","description":"Serve la UI Modules Baseline Benchmark (v25.102).\n\nMostra i risultati del retrieval benchmark sui 15 moduli con drill-down\nsu tutte le 2250 query (150 × modulo). Dati caricati da\n/static/benchmark-data.json (~530 KB).","operationId":"serve_benchmark_page_benchmark_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AlertSubscribeRequest":{"properties":{"sub_type":{"type":"string","title":"Sub Type","description":"source | module | collection | reference | keyword | country"},"target":{"type":"string","maxLength":500,"title":"Target"},"name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Name"},"target_meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Target Meta"}},"type":"object","required":["sub_type","target"],"title":"AlertSubscribeRequest"},"ArticlesRequest":{"properties":{"law":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Law","description":"Legge speciale (es. 'L. 392/1978', 'D.Lgs. 81/2008')"},"codice":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Codice","description":"Codice (es. 'c.c.', 'Codice Civile', 'TUIR')"},"articles":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","maxItems":50,"minItems":1,"title":"Articles","description":"Lista numeri articoli da fetchare (es. [27, 32, 34] o ['1384', '2112-bis'])"},"include_text":{"type":"boolean","title":"Include Text","description":"Se True, include il testo completo dell'articolo. Se False, solo metadata.","default":true}},"type":"object","required":["articles"],"title":"ArticlesRequest"},"ChatRequest":{"properties":{"message":{"type":"string","maxLength":10000,"title":"Message","description":"Domanda dell'utente"},"history":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"History"}},"type":"object","required":["message"],"title":"ChatRequest"},"ChatResponse":{"properties":{"response":{"type":"string","title":"Response"},"sources":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Sources"}},"type":"object","required":["response"],"title":"ChatResponse"},"DoczoomAnswerRequest":{"properties":{"query":{"type":"string","maxLength":2000,"title":"Query"},"top_k":{"type":"integer","maximum":25.0,"minimum":1.0,"title":"Top K","default":10},"sources":{"items":{"type":"string"},"type":"array","title":"Sources"},"collections":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Collections","description":"Slug di collections (es. 'diritto-tributario', 'privacy-data-protection'). Espande la whitelist sources. Vedi GET /api/collections per la lista."},"min_relevance":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Min Relevance","default":0.3},"document_context":{"anyOf":[{"type":"string","maxLength":2000000},{"type":"null"}],"title":"Document Context","description":"Testo opzionale di un documento allegato dall'utente (contratto, fattura, circolare aziendale). Il LLM lo analizzerà usando la banca dati TaxHarvest come riferimento normativo. Max 2M caratteri (~500K token); supportato grazie al context window 1M di DeepSeek-V4-Pro."},"document_name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Document Name","description":"Nome file del documento allegato (es. 'contratto_locazione.pdf')."},"profile":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Profile","description":"Profilo professionale dell'utente (es. 'Avvocato Tributarista', 'Commercialista', 'Avvocato Civilista', 'Notaio'). Se passato, il LLM judge applica controlli verticali sul dominio."},"materia":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Materia","description":"Materia giuridica/fiscale (es. 'IVA', 'IRPEF', 'Lavoro', 'Successioni'). Affina il grounding del LLM judge sul corpus filtrato."},"prefer_speed":{"type":"boolean","title":"Prefer Speed","description":"Se True, skippa re-prompt automatico in caso di ISSUE: ritorna v1 + needs_verification=true. Risparmia 15-25s; trade-off qualità/velocità. Default False (qualità prima, default storico).","default":false},"max_output_tokens":{"anyOf":[{"type":"integer","maximum":8192.0,"minimum":100.0},{"type":"null"}],"title":"Max Output Tokens","description":"Override del limite token output del LLM. Default server: 4096. Min 100, Max 8192 (cap hard per cost protection). Default generoso per evitare risposte troncate; LLM tipicamente produce 1000-1500 token median anche con cap alto. Usa 8192 solo per redaction multi-clausola o memo lunghi (latency potenziale 2-3min su /stream)."}},"type":"object","required":["query"],"title":"DoczoomAnswerRequest"},"DoczoomAnswerResponse":{"properties":{"query":{"type":"string","title":"Query"},"answer":{"type":"string","title":"Answer","description":"Risposta sintetizzata dal LLM, basata sui chunks"},"disclaimer":{"type":"string","title":"Disclaimer","description":"Disclaimer di verifica fonte (separato da answer per evitare bias judging)","default":""},"sources":{"items":{"$ref":"#/components/schemas/DoczoomSource"},"type":"array","title":"Sources"},"chunks":{"items":{"$ref":"#/components/schemas/DoczoomChunk"},"type":"array","title":"Chunks"},"confidence_level":{"type":"string","title":"Confidence Level","default":"unknown"},"top_score":{"type":"number","title":"Top Score","default":0.0},"needs_verification":{"type":"boolean","title":"Needs Verification","default":false},"has_ai_summary":{"type":"boolean","title":"Has Ai Summary","description":"True se almeno un chunk e' AI-summary (necessita warning UI)","default":false},"timing_ms":{"additionalProperties":true,"type":"object","title":"Timing Ms"}},"type":"object","required":["query","answer"],"title":"DoczoomAnswerResponse"},"DoczoomChunk":{"properties":{"text":{"type":"string","title":"Text"},"source":{"$ref":"#/components/schemas/DoczoomSource"},"chunk_index":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Chunk Index","default":0},"entities":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Entities"},"formal_citation":{"type":"string","title":"Formal Citation","description":"Citazione formale: Art. 13, comma 2, DPR 633/72","default":""},"article_path":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Article Path","description":"art_13.comma_2"},"authority":{"type":"string","title":"Authority","description":"Stato / AdE / Cassazione","default":""},"document_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Document Date"},"validity":{"type":"string","title":"Validity","default":"vigente"},"last_verified":{"type":"string","title":"Last Verified","default":""},"category":{"type":"string","title":"Category","description":"legislazione / prassi / giurisprudenza","default":""},"is_ai_summary":{"type":"boolean","title":"Is Ai Summary","description":"True se chunk e' un riassunto AI di un articolo non disponibile verbatim in DB. Da mostrare con warning all'utente: 'Verifica testo ufficiale alla fonte'.","default":false},"published_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Published Date","description":"ISO date di pubblicazione del documento parent (es. '2022-01-14'). Estratta dal doc_metadata. Permette filtro temporale client-side e distinguere Cassazione 2010 da 2025."},"section":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Section","description":"Sezione Cassazione (Sez. III, Sez. Lavoro, SU) o sezione organo (es. CdS Sez. V). Estratta da metadata.section o metadata.sezione."},"macro_area":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Macro Area","description":"Macro-area Cassazione (lavoro_previdenza, tributario, contratti_obbligazioni, ecc.) se taggata. Usato per filtering downstream. Null per chunks senza tagging (~59% Cassazione)."},"is_abrogated":{"type":"boolean","title":"Is Abrogated","description":"True se la norma e' stata abrogata (per Normattiva docs).","default":false},"total_chunks_in_doc":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Chunks In Doc","description":"Numero totale di chunks nel documento parent. Permette al client di capire la posizione del chunk."},"score_breakdown":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Score Breakdown","description":"Componenti del relevance_score: vector, fts, reference, article, title_boost, source_boost, vigenza_penalty + optional flag anchor/match_type. Popolato solo se include_score_breakdown=true."}},"type":"object","required":["text","source"],"title":"DoczoomChunk"},"DoczoomQueryRequest":{"properties":{"query":{"type":"string","maxLength":2000,"title":"Query"},"top_k":{"type":"integer","maximum":30.0,"minimum":1.0,"title":"Top K","default":8},"sources":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Sources"},"collections":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Collections"},"modules":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Modules","description":"Moduli macro-area Lexroom-style (es. ['diritto-civile', 'diritto-tributario']). Restringe retrieval ai chunks dei documenti pertinenti al modulo (source + metadata). Vedi GET /api/modules per la lista completa."},"country":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Country","description":"Country filter ISO 2-letter (default ['IT'] se non passato). Per UAE chunks (voyage-3-large embedding, chunks_uae table) passare ['AE']. Solo IT e AE supportati oggi."},"min_relevance":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Min Relevance","default":0.0},"include_text":{"type":"boolean","title":"Include Text","default":true},"use_hybrid":{"type":"boolean","title":"Use Hybrid","description":"Usa ricerca ibrida (vector + entities + ontology)","default":true},"context_format":{"type":"string","title":"Context Format","description":"Formato del campo `context` ritornato: 'compact' (default, pre-formatted string), 'llm_xml' (structured XML con <source id=N><title/><citation/><text/></source>, ottimo per Anthropic/OpenAI), 'markdown' (with footnote refs [^1], ottimo per visualizzazione).","default":"compact"},"group_by_document":{"type":"boolean","title":"Group By Document","description":"Se true, raggruppa chunks per documento (chunks consecutivi dello stesso doc uniti) — riduce il context da N×500B a M×2KB. Utile per LLM con context window limitato.","default":false}},"type":"object","required":["query"],"title":"DoczoomQueryRequest"},"DoczoomQueryResponse":{"properties":{"query":{"type":"string","title":"Query"},"chunks":{"items":{"$ref":"#/components/schemas/DoczoomChunk"},"type":"array","title":"Chunks"},"sources":{"items":{"$ref":"#/components/schemas/DoczoomSource"},"type":"array","title":"Sources"},"entities_found":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Entities Found"},"concepts_activated":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Concepts Activated"},"total_results":{"type":"integer","title":"Total Results"},"timing_ms":{"additionalProperties":true,"type":"object","title":"Timing Ms","description":"Breakdown tempi per step"},"context":{"type":"string","title":"Context"},"confidence_level":{"type":"string","title":"Confidence Level","description":"high (top score >=0.7), medium (0.4-0.7), low (<0.4), no_results (0 chunks)","default":"unknown"},"top_score":{"type":"number","title":"Top Score","description":"Score del chunk top-1","default":0.0},"needs_verification":{"type":"boolean","title":"Needs Verification","description":"True se confidence < high. Frontend deve mostrare warning.","default":false},"no_results_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"No Results Message","description":"Messaggio honesty quando confidence=no_results: 'Non ho trovato fonti...'"}},"type":"object","required":["query","chunks","sources","total_results","context"],"title":"DoczoomQueryResponse"},"DoczoomRetrieveLegalRequest":{"properties":{"query":{"type":"string","maxLength":2000,"title":"Query"},"practice_area":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Practice Area","description":"Materia: 'lavoro' | 'civile' | 'tributario' | 'penale' | 'amministrativo' | 'privacy'. Routing automatico verso norme + giurisprudenza + prassi mirate."},"sub_area":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sub Area","description":"Sotto-area opzionale per routing più specifico, es. 'patto_non_concorrenza', 'licenziamento', 'forfettario', 'responsabilita_medica', 'appalti'."},"must_include":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Must Include","description":"Anchor deterministici: lista di formal_citation che DEVONO uscire in risposta. Es: ['Art. 2125 Codice Civile']. Override anche se il vector search non li avrebbe pescati."},"authority_mix":{"anyOf":[{"additionalProperties":{"type":"integer"},"type":"object"},{"type":"null"}],"title":"Authority Mix","description":"Distribuzione desiderata top_k: {'norms': 3, 'case_law': 7, 'practice': 2}. Se omesso, usa default del routing practice_area."},"exclude_topics":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Exclude Topics","description":"Hard filter: penalizza chunks che contengono queste keyword nel testo. Override il default del routing."},"explain":{"type":"boolean","title":"Explain","description":"Se true, ritorna campo 'explain' con metadata debug: filtri applicati, query rewritten, must_include risolti, ranking strategy, sources escluse.","default":false},"top_k":{"type":"integer","maximum":30.0,"minimum":1.0,"title":"Top K","default":12},"min_relevance":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Min Relevance","default":0.3},"context_format":{"type":"string","title":"Context Format","default":"llm_xml"},"must_include_mode":{"type":"string","title":"Must Include Mode","description":"Modalità must_include: 'anchor' (default, default v25.77 behavior — inietta come chunk #1 con score 0.99 se non già nei retrieved), 'boost' (se trovato applica +0.20 score, non inietta), 'strict' (se 0 must_include matchano in DB ritorna 404).","default":"anchor"},"max_chunks_per_document":{"type":"integer","maximum":10.0,"minimum":1.0,"title":"Max Chunks Per Document","description":"MMR-style diversity: max N chunks dello stesso documento nei top_k finali. Default 2 evita gli ammassi 2-3 chunk consecutivi (chunk_index 9+11+12 stesso doc). Set a 10 per disabilitare.","default":2},"include_score_breakdown":{"type":"boolean","title":"Include Score Breakdown","description":"Se true, espone in ogni chunk 'score_breakdown' con i componenti (vector, fts, reference, article, title_boost, source_boost, vigenza_penalty) usati per il relevance_score finale.","default":false},"exclude_authorities":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Exclude Authorities","description":"Hard filter strutturato: lista di doc_type da escludere (es. ['sentenza_penale', 'circolare']). Alternative a exclude_topics che opera su keyword nel testo."},"exclude_date_range":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Exclude Date Range","description":"Filtro temporale: {'from': 'YYYY-MM-DD', 'to': 'YYYY-MM-DD'}. Esclude chunks pubblicati FUORI dal range."}},"type":"object","required":["query"],"title":"DoczoomRetrieveLegalRequest","description":"v25.77 — Legal-aware retrieve per chat legale strutturata.\n\nEstende /retrieve con:\n- practice_area + sub_area: routing pre-config (norme + giurisprudenza + prassi mirate)\n- must_include: anchor deterministici (articoli che DEVONO uscire)\n- authority_mix: distribuzione richiesta {norms, case_law, practice}\n- exclude_topics: hard filter rumore (penalizza chunks con keyword)\n- explain: ritorna metadata debug sul retrieval"},"DoczoomRetrieveLegalResponse":{"properties":{"query":{"type":"string","title":"Query"},"chunks":{"items":{"$ref":"#/components/schemas/DoczoomChunk"},"type":"array","title":"Chunks"},"sources":{"items":{"$ref":"#/components/schemas/DoczoomSource"},"type":"array","title":"Sources"},"total_results":{"type":"integer","title":"Total Results"},"confidence_level":{"type":"string","title":"Confidence Level"},"top_score":{"type":"number","title":"Top Score"},"needs_verification":{"type":"boolean","title":"Needs Verification"},"no_results_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"No Results Message"},"context":{"type":"string","title":"Context"},"timing_ms":{"additionalProperties":true,"type":"object","title":"Timing Ms"},"detected_issues":{"items":{"type":"string"},"type":"array","title":"Detected Issues"},"rewritten_queries":{"items":{"type":"string"},"type":"array","title":"Rewritten Queries"},"authority_breakdown":{"additionalProperties":{"type":"integer"},"type":"object","title":"Authority Breakdown"},"explain":{"anyOf":[{"$ref":"#/components/schemas/LegalRetrieveExplain"},{"type":"null"}]}},"type":"object","required":["query","chunks","sources","total_results","confidence_level","top_score","needs_verification","context"],"title":"DoczoomRetrieveLegalResponse"},"DoczoomRetrieveRequest":{"properties":{"query":{"type":"string","maxLength":2000,"title":"Query"},"top_k":{"type":"integer","maximum":30.0,"minimum":1.0,"title":"Top K","description":"Default 15 (più alto di /query default 8) per LLM consumption.","default":15},"sources":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Sources"},"collections":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Collections"},"min_relevance":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Min Relevance","description":"Default 0.4 (più severo di /query default 0.0) — filtra chunks deboli prima del LLM downstream.","default":0.4},"context_format":{"type":"string","title":"Context Format","description":"Default 'llm_xml' ottimizzato per Anthropic/OpenAI tool use. Alternatives: 'compact' | 'markdown'.","default":"llm_xml"},"group_by_document":{"type":"boolean","title":"Group By Document","description":"Default true — riduce context size unendo chunks consecutivi.","default":true},"use_hybrid":{"type":"boolean","title":"Use Hybrid","default":true}},"type":"object","required":["query"],"title":"DoczoomRetrieveRequest","description":"v25.75 — Endpoint dedicato BYOL (Bring Your Own LLM).\n\nWrapper di /api/doczoom/query con default ottimizzati per LLM consumption\ne output formati specifici."},"DoczoomSource":{"properties":{"title":{"type":"string","title":"Title"},"url":{"type":"string","title":"Url"},"source":{"type":"string","title":"Source"},"doc_type":{"type":"string","title":"Doc Type"},"relevance_score":{"type":"number","title":"Relevance Score"},"doc_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Doc Id","description":"ID interno del documento parent. Usato per costruire viewer_url."},"viewer_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Viewer Url","description":"Link al lettore interno DocZoom (/doc/{id}) che apre il testo completo del documento direttamente. Risolve il problema delle fonti con link non-deep-linkabili (es. SPA ItalGiure #id=... che costringe l'utente a reinserire il codice della sentenza). `url` resta la fonte ufficiale; usa `viewer_url` come target del bottone 'Apri fonte'."}},"type":"object","required":["title","url","source","doc_type","relevance_score"],"title":"DoczoomSource"},"DoczoomToolRequest":{"properties":{"tool":{"type":"string","title":"Tool"},"params":{"additionalProperties":true,"type":"object","title":"Params"}},"type":"object","required":["tool"],"title":"DoczoomToolRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HarvestRequest":{"properties":{"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"}},"type":"object","title":"HarvestRequest"},"LegalRetrieveExplain":{"properties":{"practice_area":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Practice Area"},"sub_area":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sub Area"},"routing_slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Routing Slug"},"detected_issues":{"items":{"type":"string"},"type":"array","title":"Detected Issues"},"rewritten_queries":{"items":{"type":"string"},"type":"array","title":"Rewritten Queries"},"sources_whitelist":{"items":{"type":"string"},"type":"array","title":"Sources Whitelist"},"normattiva_codici_filter":{"items":{"type":"string"},"type":"array","title":"Normattiva Codici Filter"},"cassazione_macro_aree_filter":{"items":{"type":"string"},"type":"array","title":"Cassazione Macro Aree Filter"},"must_include_requested":{"items":{"type":"string"},"type":"array","title":"Must Include Requested"},"must_include_resolved":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Must Include Resolved","description":"Per ogni must_include: {citation, found, title, url}."},"authority_mix_target":{"additionalProperties":{"type":"integer"},"type":"object","title":"Authority Mix Target"},"authority_mix_actual":{"additionalProperties":{"type":"integer"},"type":"object","title":"Authority Mix Actual"},"exclude_topics_applied":{"items":{"type":"string"},"type":"array","title":"Exclude Topics Applied"},"chunks_excluded_by_topic":{"type":"integer","title":"Chunks Excluded By Topic","default":0},"ranking_strategy":{"type":"string","title":"Ranking Strategy","default":"default"}},"type":"object","title":"LegalRetrieveExplain"},"ReferenceSearchRequest":{"properties":{"reference":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reference","description":"Stringa libera es. 'DPR 633/72', 'Circolare 25/E del 2023', 'L. 392/1978 art. 27', 'art. 1384 c.c.'"},"ref_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ref Type","description":"dpr, dlgs, dl, legge, circolare, sentenza, codice"},"number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Number"},"year":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year"},"article":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Article","description":"Numero articolo opzionale (es. '27', '1384', '2112-bis')"},"codice":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Codice","description":"Codice (cc/cp/cpc/cpp) o nome esteso"},"authority":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authority","description":"ade, inps, cassazione"}},"type":"object","title":"ReferenceSearchRequest"},"SearchRequest":{"properties":{"query":{"type":"string","maxLength":2000,"title":"Query"},"top_k":{"type":"integer","maximum":50.0,"minimum":1.0,"title":"Top K","default":10},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"}},"type":"object","required":["query"],"title":"SearchRequest"},"ToolRequest":{"properties":{"params":{"additionalProperties":true,"type":"object","title":"Params"}},"type":"object","title":"ToolRequest"},"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"}},"securitySchemes":{"APIKeyHeader":{"type":"apiKey","in":"header","name":"X-API-Key"}}},"tags":[{"name":"DocZoom","description":"Endpoint principali per integrazione DocZoom (RAG end-to-end con LLM synthesis)."},{"name":"Search","description":"Ricerca e browsing documenti."},{"name":"Sources","description":"Lista fonti, categorie, documenti per source."},{"name":"Knowledge Graph","description":"Grafo concetti ontologici e relazioni."},{"name":"Reference","description":"Lookup di riferimenti normativi specifici."},{"name":"System","description":"Health check, statistiche, info sistema."}]}