Endpoint: Analisi e Classificazione
Riferimento degli endpoint per l'analisi di contenuti, documenti PDF, CV e dati tabellari, l'OCR, la verifica documentale, l'anonimizzazione dei documenti, la classificazione tramite catalogo, l'editor PDF, i template di analisi e l'integrazione con Askme Desk.
Tutte le richieste usano l'URL base https://ai.askme.it/api/v2 e le intestazioni descritte in Autenticazione. Gli endpoint di analisi richiedono che la funzionalità analysis sia attiva per l'organizzazione (cv_analyzer per l'analisi dei CV) e i permessi analysis.create, analysis.read e analysis.delete per creare, leggere ed eliminare i job.
La classificazione dei testi (smistamento, instradamento delle richieste) si esegue con i cataloghi di classificazione (sezione 4). Per la classificazione dell'intento di un messaggio rispetto a un agente si usa POST /classify, descritto in Endpoint: Agenti, Chat e Conversazioni.
1. Analisi
Ogni analisi è un job. POST /analysis/<tipo> risponde 202 Accepted con il job in stato pending e avvia l'elaborazione; il risultato si legge con GET /analysis/{analysis_id} quando lo stato diventa completed. Gli stati possibili sono pending, processing, completed e failed (con il motivo in error_message). Per i contenuti, i PDF e i CV sono disponibili anche le varianti /sync, che attendono il completamento e rispondono 200 OK con il risultato.
L'intestazione facoltativa Idempotency-Key evita di creare due volte lo stesso job: ripetendo la richiesta con la stessa chiave si ottiene il job già creato.
Campi comuni
Tutti i corpi JSON di POST /analysis/<tipo> accettano:
| Campo | Tipo | Descrizione |
|---|---|---|
name | string (max 255) | Nome del job, mostrato anche nella cronologia della console. |
template_id | int | Template di analisi (sezione 7) da cui ereditare la configurazione. |
options | object | Opzioni del job (vedere sotto). |
Campo di options | Tipo | Predefinito | Descrizione |
|---|---|---|---|
model | string | — | Modello da usare al posto di quello predefinito dell'organizzazione. Per l'analisi PDF vedere anche la scelta del modello. |
temperature | float (0.0–2.0) | — | Temperatura di campionamento. |
max_tokens | int (1–128000) | — | Limite di token della risposta del modello. |
language | string | — | Lingua preferita per il risultato (it, en, ...). |
output_format | string | json | json, html, markdown. |
webhook_id | int | — | Webhook da notificare a fine job (si veda Webhook). |
I campi non previsti nel corpo sono rifiutati con 422; all'interno di options sono invece ammessi campi aggiuntivi.
POST /analysis/content
Analisi di un testo: riassunto, sentimento, argomenti, parole chiave, entità, tono e leggibilità.
| Campo | In | Tipo | Obbligatorio | Predefinito | Descrizione |
|---|---|---|---|---|---|
text | body | string (1–200000) | Sì | — | Testo da analizzare. |
custom_instructions | body | string (max 4000) | No | — | Istruzioni aggiuntive per l'analisi. |
max_chars | body | int (100–200000) | No | 20000 | Lunghezza massima del testo analizzato; l'eccedenza viene troncata. |
Esempio di richiesta
curl -X POST "https://ai.askme.it/api/v2/analysis/content" \
-H "X-Api-Key: amai_xxxxxxxxxxxxxxxx" \
-H "X-Source-App: api_direct" \
-H "Idempotency-Key: 2c9e5c0e-6b0a-4c8e-9a51-7d0f3e1b2a44" \
-H "Content-Type: application/json" \
-d '{
"name": "Analisi recensione",
"text": "Askme AI ci ha permesso di rispondere in modo più rapido alle domande frequenti dei nostri utenti. Il team di supporto è sempre disponibile, anche se a volte la configurazione iniziale della knowledge base richiede attenzione. Nel complesso siamo molto soddisfatti del servizio.",
"options": { "language": "it" }
}'
Esempio di risposta (202 Accepted)
{
"id": 1977,
"tenant_id": "5b0e2f7a-2c41-4d0e-9a6b-1c9e8f3d4a21",
"type": "content",
"status": "pending",
"input_config": {
"name": "Analisi recensione",
"text": "Askme AI ci ha permesso di rispondere in modo più rapido [...]",
"max_chars": 20000,
"source_app": "api_direct"
},
"result": null,
"error_message": null,
"created_by": "apikey:28",
"started_at": null,
"completed_at": null,
"created_at": "2026-09-24T17:07:54.490588Z"
}
input_config riporta i parametri del job così come sono stati ricevuti, compreso l'eventuale contenuto dei file in base64. POST /analysis/content/sync accetta lo stesso corpo e risponde 200 OK con result già valorizzato.
POST /analysis/pdf
Analisi di uno o più documenti PDF. Facoltativamente crea una base di conoscenza e un agente temporanei per conversare con il documento. Il corpo è JSON, con il file in base64.
| Campo | In | Tipo | Obbligatorio | Predefinito | Descrizione |
|---|---|---|---|---|---|
filename | body | string (max 512) | Sì* | — | Nome del file. *Obbligatorio per un singolo documento; con files è solo un'etichetta. |
file_content | body | string base64 | Sì* | — | Contenuto del PDF. *Uno solo tra file_content e file_path. |
file_path | body | string | Sì* | — | Percorso di un file già caricato nell'archivio dell'organizzazione. |
files | body | array | No | — | Più PDF analizzati insieme: [{ "filename", "file_content" }] (oppure file_path al posto di file_content). Se presente, file_content e file_path del primo livello sono ignorati. |
custom_prompt | body | string (max 8000) | No | — | Domanda o istruzioni per l'analisi. |
agent_id | body | int | No | — | Agente le cui istruzioni si usano quando custom_prompt è vuoto. |
create_kb_agent | body | bool | No | false | Se true, a fine analisi crea una base di conoscenza e un agente temporanei con il documento, per conversare con esso. |
temp_agent_name | body | string (max 512) | No | — | Nome dell'agente temporaneo. |
skip_analysis | body | bool | No | false | Crea solo la base di conoscenza e l'agente temporanei, senza analisi. Richiede create_kb_agent: true. |
Esempio di richiesta
PDF_B64=$(base64 -w0 contratto.pdf)
curl -X POST "https://ai.askme.it/api/v2/analysis/pdf" \
-H "X-Api-Key: amai_xxxxxxxxxxxxxxxx" \
-H "X-Source-App: api_direct" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"Analisi contratto\",
\"filename\": \"contratto.pdf\",
\"file_content\": \"$PDF_B64\",
\"custom_prompt\": \"Elenca le clausole principali del contratto.\"
}"
La risposta è 202 Accepted con il job (type: "pdf", status: "pending"). A job completato, result contiene:
{
"summary": "Il documento è un esempio di contratto di servizio per l'erogazione di un assistente conversazionale basato su intelligenza artificiale [...]",
"response": "Le clausole principali del contratto includono: le parti coinvolte, l'oggetto del contratto, il corrispettivo annuale e la durata di 12 mesi con rinnovo tacito salvo disdetta.",
"key_findings": [
"Oggetto: servizio di assistente conversazionale basato su IA",
"Corrispettivo: EUR 1.200,00 annui",
"Durata: 12 mesi",
"Rinnovo tacito salvo disdetta"
],
"topics": ["Contratti di servizio", "Intelligenza artificiale", "Termini di pagamento"],
"sentiment": "neutral",
"language": "it",
"word_count": 239,
"page_estimate": 1
}
response contiene la risposta alla domanda di custom_prompt.
Errori specifici
| Codice | Significato |
|---|---|
422 | Né file_content né file_path (o entrambi), filename mancante per un singolo documento, oppure skip_analysis senza create_kb_agent. |
POST /analysis/pdf/sync esegue la stessa analisi in modo sincrono.
Scelta del modello dell'analisi PDF
Il modello che analizza il documento è, nell'ordine: quello indicato in options.model; altrimenti quello dell'agente indicato con agent_id; altrimenti il modello impostato per l'organizzazione dal team Askme; altrimenti quello predefinito dell'installazione. Con create_kb_agent, il modello indicato in options.model è usato anche dall'agente temporaneo. Disponibile dalla versione 2.3.15.
Quando custom_prompt fissa una lunghezza massima in caratteri per il riassunto (per esempio "riassunto di massimo 200 caratteri"), il campo summary la rispetta e un riassunto più lungo si chiude all'ultima frase completa entro il limite.
POST /analysis/cv
Valuta un CV rispetto alle posizioni lavorative configurate (sezione 6). Richiede la funzionalità cv_analyzer.
| Campo | In | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|---|
file_content | body | string base64 | Sì | CV (PDF o DOCX) in base64. |
filename | body | string (max 512) | Sì | Nome del file con estensione, che determina il formato. |
job_position_id | body | int | No | Posizione rispetto a cui valutare il CV. Se omessa, il CV viene valutato rispetto a tutte le posizioni. |
cv_prompt_id | body | int | No | Prompt di valutazione alternativo a quello configurato. |
POST /analysis/cv/sync esegue la valutazione in modo sincrono.
POST /analysis/data
Analisi di dati tabellari, inviati come elenco di righe.
| Campo | In | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|---|
data | body | object[] (1–10000) | Sì | Righe nella forma { "colonna": valore }. |
columns | body | string[] | No | Colonne da analizzare (predefinito: tutte). |
custom_prompt | body | string (max 4000) | No | Domanda o istruzioni per l'analisi. |
agent_id | body | int | No | Agente le cui istruzioni si usano quando custom_prompt è vuoto. |
{
"name": "Conversazioni mensili",
"data": [
{ "mese": "Gennaio", "conversazioni": 320, "passaggi_operatore": 12 },
{ "mese": "Febbraio", "conversazioni": 410, "passaggi_operatore": 18 },
{ "mese": "Marzo", "conversazioni": 505, "passaggi_operatore": 9 }
],
"custom_prompt": "Individua il trend dei passaggi a operatore rispetto al volume di conversazioni."
}
Il result contiene, tra gli altri, columns, insights e trends.
GET /analysis
Elenca i job di analisi dell'organizzazione.
| Campo | In | Tipo | Predefinito | Descrizione |
|---|---|---|---|---|
offset | query | int | 0 | Paginazione. |
limit | query | int (1–200) | 50 | Dimensione della pagina. |
status | query | string | — | pending, processing, completed, failed. |
type | query | string | — | content, pdf, cv, data, ocr, document_verify, anonymize, catalog_build. |
search | query | string | — | Parte del nome del job. |
sort_by, order | query | string | created_at, desc | Ordinamento. |
Gli elementi hanno lo stesso formato del job, senza result.
GET /analysis/{analysis_id}
Dettaglio del job, con result (struttura specifica per tipo). Esempio per un'analisi di contenuto completata:
{
"id": 1977,
"tenant_id": "5b0e2f7a-2c41-4d0e-9a6b-1c9e8f3d4a21",
"type": "content",
"status": "completed",
"input_config": { "name": "Analisi recensione", "text": "[...]", "source_app": "api_direct" },
"result": {
"summary": "Askme AI ha reso più rapide le risposte alle domande frequenti; il supporto è sempre disponibile, anche se la configurazione iniziale richiede attenzione.",
"sentiment": { "label": "positive", "score": 0.8 },
"tone": "formal",
"topics": ["AI technology", "customer support", "knowledge management"],
"keywords": ["Askme AI", "domande frequenti", "team di supporto", "knowledge base"],
"entities": [{ "text": "Askme AI", "type": "org" }],
"language": "it",
"word_count": 56,
"readability": { "level": "intermediate", "estimated_reading_time_minutes": 1 },
"metadata": { "truncated": false, "analyzed_length": 278, "original_length": 278 }
},
"error_message": null,
"created_by": "apikey:28",
"started_at": "2026-09-24T17:07:54.499694Z",
"completed_at": "2026-09-24T17:07:58.065700Z",
"created_at": "2026-09-24T17:07:54.490588Z"
}
GET /analysis/{analysis_id}/status
Solo lo stato del job, senza result: adatto a un'interrogazione frequente.
{
"id": 1977,
"status": "processing",
"type": "content",
"started_at": "2026-09-24T17:07:54.499694Z",
"completed_at": null,
"error_message": null
}
DELETE /analysis/{analysis_id}
Elimina il job. Risposta 204.
2. OCR e verifica documentale
POST /analysis/ocr
Estrazione del testo da immagini o PDF scansionati. Il corpo è multipart/form-data.
| Campo | In | Tipo | Obbligatorio | Predefinito | Descrizione |
|---|---|---|---|---|---|
file | form-data | file | Sì* | — | Immagine o PDF (massimo 50 MB). *Uno solo tra file e file_path. |
file_path | form-data | string | Sì* | — | Percorso di un file già caricato nell'archivio dell'organizzazione. |
language | form-data | string | No | ita | Lingua del documento: ita, eng, fra, deu, spa. |
template_id | form-data | int | No | — | Template di analisi. |
webhook_id | form-data | int | No | — | Webhook da notificare a fine job. |
curl -X POST "https://ai.askme.it/api/v2/analysis/ocr" \
-H "X-Api-Key: amai_xxxxxxxxxxxxxxxx" \
-H "X-Source-App: api_direct" \
-F "[email protected]" \
-F "language=ita"
A job completato, result contiene:
{
"text": "RICEVUTA DI PAGAMENTO (ESEMPIO) [...] Importo totale: EUR 100,00",
"pages": 1,
"method": "tesseract",
"language": "ita",
"confidence": 0.9467,
"processing_time_seconds": 0.606
}
Errori specifici
| Codice | Significato |
|---|---|
400 | Nessuno o entrambi tra file e file_path, tipo di file non gestito oppure file vuoto. |
413 | File oltre 50 MB. |
Un file che non si riesce a leggere (per esempio un PDF danneggiato) produce un job in stato failed con il motivo in error_message.
POST /analysis/document-verify
Verifica di un documento giustificativo: il documento viene letto e i valori che il chiamante già conosce (importo, partita IVA, codice fiscale, date, nomi, testo libero) vengono confrontati con quanto il documento riporta. Risponde 202 Accepted con un job asincrono; l'esito si legge con GET /analysis/{analysis_id}.
La lettura e il giudizio sono passaggi distinti. Il motore OCR trasforma il documento in testo senza conoscere i valori attesi. Il testo viene poi confrontato con i valori attesi da regole deterministiche: normalizzazione per tipo, carattere di controllo di codice fiscale e partita IVA, distanza tra i codici, somiglianza dei nomi. I campi che le regole non riescono a decidere passano a un agente di validazione, che dichiara se il documento li conferma citando la porzione di testo da cui ha letto il valore.
| Campo | In | Tipo | Obbligatorio | Predefinito | Descrizione |
|---|---|---|---|---|---|
filename | body | string (max 512) | Sì | — | Nome del file. |
content_type | body | string | Sì | — | Tipo MIME: image/png, image/jpeg, image/tiff, image/webp, application/pdf. |
file_content | body | string | Sì (in alternativa) | — | Contenuto del documento in base64, in alternativa a file_path. |
file_path | body | string | Sì (in alternativa) | — | Percorso di un file già caricato, in alternativa a file_content. |
document_type | body | string | No | generic | receipt, form o generic: seleziona i controlli strutturali e i campi previsti. |
expected | body | object | Sì (in alternativa) | — | Forma compatta {codice: valore}: tipo, tolleranze e ancore derivano dal profilo. |
expected_fields | body | array (max 50) | Sì (in alternativa) | — | Campi descritti per esteso; a parità di codice prevalgono su expected. |
language | body | string | No | ita | Lingua del documento. |
tolerance_level | body | string | No | livello configurato | strict, balanced o relaxed. |
Livelli di tolleranza
Lo scostamento accettabile tra documento e valore atteso dipende dall'uso dell'esito: chi alimenta un pagamento automatico vuole una soglia stretta, chi smista un lotto prima della revisione manuale può preferirne una più ampia.
| Livello | Comportamento |
|---|---|
strict | Conferma solo una lettura esatta. Qualunque differenza è uno scarto, e ciò che il documento non riporta trattiene l'esito. |
balanced | Un carattere fuori posto porta a una verifica manuale, non a una conferma. |
relaxed | Il rumore dell'OCR su fotografie e moduli compilati a mano viene assorbito; un campo obbligatorio assente o un elemento strutturale mancante restano segnalati nei reasons senza scartare il documento. |
Omettendo tolerance_level vale il livello configurato per l'organizzazione. Un valore che il documento contraddice resta uno scarto a qualunque livello: la tolleranza stabilisce quanto una lettura può discostarsi prima di essere considerata in contrasto, non se il contrasto conti.
Esempio di richiesta
curl -X POST "https://ai.askme.it/api/v2/analysis/document-verify" \
-H "X-Api-Key: amai_xxxxxxxxxxxxxxxx" \
-H "X-Source-App: api_direct" \
-H "Content-Type: application/json" \
-d '{
"name": "Verifica ricevuta",
"filename": "ricevuta.png",
"content_type": "image/png",
"file_content": "<base64>",
"document_type": "receipt",
"expected": {
"amount": "100,00",
"vat_number": "IT00000000000",
"doc_date": "15/09/2026"
},
"tolerance_level": "relaxed"
}'
Esempio di risultato (campo result di GET /analysis/{analysis_id}, estratto)
{
"verdict": "pass",
"ocr": { "engine": "auto", "method": "tesseract", "pages": 1, "chars": 251, "confidence": 0.9467 },
"document": {
"profile": "receipt",
"valid": false,
"checks": [
{ "name": "commercial_doc_header", "status": "missing", "severity": "blocking", "found": null },
{ "name": "vat_present", "status": "ok", "severity": "blocking", "found": "00000000000" },
{ "name": "progressive_number", "status": "missing", "severity": "blocking", "found": null },
{ "name": "rt_serial", "status": "missing", "severity": "warning", "found": null }
],
"detected_type": null,
"type_mismatch": false
},
"fields": [
{
"code": "amount",
"status": "match",
"expected_normalized": "100.00",
"extracted_value": "100,00",
"extracted_normalized": "100.00",
"confidence": 1.0,
"evidence": "IMPORTO TOTALE: EUR 100,00",
"reason": null,
"expected_checksum_valid": null,
"source": "ocr"
},
{
"code": "vat_number",
"status": "match",
"expected_normalized": "00000000000",
"extracted_value": "00000000000",
"extracted_normalized": "00000000000",
"confidence": 1.0,
"evidence": "P.IVA IT00000000000",
"reason": null,
"expected_checksum_valid": true,
"source": "ocr"
}
],
"reasons": ["receipt_incomplete"],
"extracted_text": "RICEVUTA DI PAGAMENTO (ESEMPIO) [...]"
}
Nell'esempio i valori attesi sono confermati, mentre la ricevuta non riporta tutti gli elementi di un documento commerciale: con il livello relaxed la mancanza è segnalata in reasons (receipt_incomplete) senza scartare il documento.
Esiti
| Campo | Valori | Significato |
|---|---|---|
verdict | pass | Il documento conferma quanto atteso. |
fail | Il documento riporta un valore diverso, oppure manca un elemento che il livello considera vincolante. | |
inconclusive | La lettura non basta a decidere: il caso va a revisione manuale. | |
fields[].status | match, mismatch, uncertain, not_found | Esito del singolo campo, con evidence che cita il testo da cui è stato letto. |
fields[].source | ocr, agent | Indica se il campo è stato deciso dalle regole deterministiche o dall'agente di validazione. |
reasons | elenco di codici | Motivi di scarto e segnalazioni, da ricondurre alle proprie casistiche. |
Errori specifici
| Codice | Significato |
|---|---|
400 | Tipo MIME non gestito. |
422 | Nessun valore da confermare, codice di campo sconosciuto per il profilo, oppure file_content e file_path entrambi presenti o entrambi assenti. |
GET /analysis/document-verify/profiles
Elenca i profili disponibili, con i controlli strutturali di ciascuno e i codici di campo accettati dalla forma compatta expected. Consente di leggere il contratto dalla piattaforma invece di replicarlo nel proprio codice.
{
"profiles": [
{
"document_type": "receipt",
"checks": [
{ "name": "commercial_doc_header", "severity": "blocking" },
{ "name": "vat_present", "severity": "blocking" },
{ "name": "progressive_number", "severity": "blocking" },
{ "name": "rt_serial", "severity": "warning" }
],
"fields": [
{ "code": "amount", "type": "amount", "tolerance": "0.01", "required": true, "label": "Totale documento" },
{ "code": "vat_number", "type": "vat_it", "required": true, "label": "P.IVA esercente" }
]
}
]
}
3. Anonimizzazione dei documenti
Restituisce una copia del documento in cui i dati personali (nomi, recapiti, codici fiscali, IBAN, indirizzi, ...) sono sostituiti da segnaposto nella forma [TIPO_N], per esempio [PERSON_1]. Lo stesso valore riceve lo stesso segnaposto in tutto il job, anche tra file diversi di un archivio. Il documento mantiene formato e struttura originali. Il motore di riconoscimento dei dati personali è quello configurato per l'installazione, e i nomi dei tipi di dato dipendono dal motore.
POST /analysis/anonymize
Il corpo è multipart/form-data. Risponde 202 Accepted con un job di tipo anonymize.
| Campo | In | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|---|
file | form-data | file | Sì* | Documento PDF, DOCX o XLSX, oppure un archivio ZIP che li contiene. *Uno solo tra file e file_path. |
file_path | form-data | string | Sì* | Percorso di un documento già caricato nell'archivio dell'organizzazione. |
exclude_labels | form-data | string | No | Tipi di dato da lasciare invariati, separati da virgola (i valori disponibili si leggono con GET /analysis/anonymize/labels). |
webhook_id | form-data | int | No | Webhook da notificare a fine job. |
curl -X POST "https://ai.askme.it/api/v2/analysis/anonymize" \
-H "X-Api-Key: amai_xxxxxxxxxxxxxxxx" \
-H "X-Source-App: api_direct" \
-F "[email protected]"
A job completato, result contiene il documento anonimizzato in base64 e il conteggio delle sostituzioni per tipo di dato (esempio illustrativo):
{
"engine": "pii_shield",
"filename": "contratto.docx",
"content": "UEsDBBQABgAIAAAAIQ[...]",
"replacements": 7,
"replacements_by_label": { "PERSON": 2, "EMAIL": 1, "PHONE": 1, "IBAN": 1, "FISCAL_CODE": 1, "ADDRESS": 1 },
"entries": [],
"excluded_labels": []
}
Per un archivio ZIP, entries riporta l'esito di ciascun file contenuto (name, status, reason in caso di errore). Se il motore di riconoscimento non è disponibile il job termina in stato failed: la piattaforma non restituisce mai un documento che non è stato esaminato.
Esempio di salvataggio del risultato in Python:
import base64
import os
import time
import httpx
client = httpx.Client(
base_url=os.environ["ASKMEAI_HOST"] + "/api/v2",
headers={"X-Api-Key": os.environ["ASKMEAI_API_KEY"], "X-Source-App": "api_direct"},
timeout=120.0,
)
with open("contratto.docx", "rb") as fh:
job = client.post("/analysis/anonymize", files={"file": ("contratto.docx", fh)}).json()
while True:
job = client.get(f"/analysis/{job['id']}").json()
if job["status"] in ("completed", "failed"):
break
time.sleep(2)
if job["status"] == "completed":
with open("contratto-anonimizzato.docx", "wb") as out:
out.write(base64.b64decode(job["result"]["content"]))
else:
print("Anonimizzazione non riuscita:", job["error_message"])
Errori specifici
| Codice | Significato |
|---|---|
400 | Nessuno o entrambi tra file e file_path, formato non gestito oppure file vuoto. |
413 | File oltre il limite di dimensione consentito. |
GET /analysis/anonymize/labels
Tipi di dato personale che il motore configurato riconosce, da usare in exclude_labels: { "engine": "...", "labels": [...] }. Un elenco vuoto indica che il motore non espone i propri tipi: in questo caso tutti i dati riconosciuti vengono sostituiti.
4. Cataloghi di classificazione
La classificazione dei testi si basa su un catalogo: si costruisce un indice a partire da esempi (con o senza etichette) e si classificano i nuovi testi rispetto a quell'indice. Tutti gli endpoint sono sotto /api/v2/analysis/catalogs.
POST /analysis/catalogs/build
Avvia la costruzione di un catalogo. Risponde 202 Accepted: la costruzione è asincrona e il suo stato si legge sul catalogo.
| Campo | In | Tipo | Obbligatorio | Predefinito | Descrizione |
|---|---|---|---|---|---|
name | body | string (1–100) | Sì | — | Nome del catalogo. |
mode | body | string | Sì | — | supervised (richiede label_path su almeno un esempio) oppure unsupervised. |
threshold | body | float (0.0–1.0) | No | 0.6 | Somiglianza minima per accettare un candidato. |
pairs | body | object[] | Sì | — | Esempi: ticket (testo, obbligatorio), response (facoltativo), label_path (etichetta, come elenco di livelli). |
Esempio di richiesta (cURL)
curl -X POST "https://ai.askme.it/api/v2/analysis/catalogs/build" \
-H "X-Api-Key: amai_xxxxxxxxxxxxxxxx" \
-H "X-Source-App: api_direct" \
-H "Content-Type: application/json" \
-d '{
"name": "Richieste assistenza",
"mode": "supervised",
"threshold": 0.6,
"pairs": [
{ "ticket": "Non riesco ad accedere al portale, password dimenticata", "label_path": ["Accesso"] },
{ "ticket": "Il mio abbonamento risulta scaduto ma ho pagato", "label_path": ["Fatturazione"] },
{ "ticket": "Come posso cambiare la lingua dell'\''assistente virtuale?", "label_path": ["Configurazione"] }
]
}'
Esempio di richiesta (Python, httpx)
import os
import httpx
client = httpx.Client(
base_url=os.environ["ASKMEAI_HOST"] + "/api/v2",
headers={"X-Api-Key": os.environ["ASKMEAI_API_KEY"], "X-Source-App": "api_direct"},
timeout=30.0,
)
resp = client.post("/analysis/catalogs/build", json={
"name": "Richieste assistenza",
"mode": "supervised",
"threshold": 0.6,
"pairs": [
{"ticket": "Non riesco ad accedere al portale, password dimenticata", "label_path": ["Accesso"]},
{"ticket": "Il mio abbonamento risulta scaduto ma ho pagato", "label_path": ["Fatturazione"]},
{"ticket": "Come posso cambiare la lingua dell'assistente virtuale?", "label_path": ["Configurazione"]},
],
})
resp.raise_for_status()
catalog_id = resp.json()["id"]
Esempio di risposta (202 Accepted)
{
"id": 9,
"tenant_id": "5b0e2f7a-2c41-4d0e-9a6b-1c9e8f3d4a21",
"name": "Richieste assistenza",
"mode": "supervised",
"status": "queued",
"refinement_status": "templates_refined",
"embedding_model": "paraphrase-multilingual-MiniLM-L12-v2",
"threshold": 0.6,
"template_count": 0,
"created_by": "apikey:28",
"created_at": "2026-09-24T17:32:15.926836Z",
"started_at": null,
"completed_at": null,
"error_message": null,
"build_warnings": null
}
Lo stato si legge con GET /analysis/catalogs/{catalog_id}: il catalogo è utilizzabile quando status vale ready.
Errori specifici
| Codice | Significato |
|---|---|
400 | pairs vuoto, oppure mode=supervised senza alcun esempio con label_path. |
POST /analysis/catalogs/classify
Classifica uno o più testi rispetto a un catalogo in stato ready e restituisce i candidati migliori con il punteggio di somiglianza. La risposta è sincrona.
| Campo | In | Tipo | Obbligatorio | Predefinito | Descrizione |
|---|---|---|---|---|---|
catalog_id | body | int | Sì | — | Catalogo. |
texts | body | string[] | Sì | — | Testi da classificare. |
top_n | body | int (1–10) | No | 3 | Numero di candidati per testo. |
threshold_override | body | float (0.0–1.0) | No | — | Soglia da usare al posto di quella del catalogo, solo per questa richiesta. |
external_ids | body | (string | null)[] | No | — | Un identificativo del sistema chiamante per ogni testo, nello stesso ordine di texts (da 1 a 200 caratteri, null per un testo senza identificativo), per esempio il numero del ticket. La classificazione viene registrata e ogni risultato riporta classification_id. |
record | body | bool | No | false | Registra la classificazione anche senza external_ids. |
headers | body | object[] | No | — | Un elemento per ogni testo, nello stesso ordine di texts, con subject (max 1000) e sender (max 320) facoltativi, oppure null: oggetto e mittente usati dalle regole del catalogo. |
include_templates | body | bool | No | false | Con una versione addestrata del classificatore, aggiunge a ogni candidato template_id e template_text del template della sua etichetta; senza, i due campi valgono null. I cataloghi di similarità restituiscono sempre il template. |
X-External-Id | header | string | No | — | Con un solo testo e senza external_ids, vale come identificativo di quel testo: la classificazione viene registrata (identificativo troncato a 200 caratteri) e il risultato riporta classification_id. |
resp = client.post("/analysis/catalogs/classify", json={
"catalog_id": catalog_id,
"texts": [
"Ho perso la password e non entro più",
"La fattura di settembre riporta un importo errato",
],
"top_n": 3,
})
resp.raise_for_status()
for result in resp.json()["results"]:
print(result["text"], "->", result["best_label_path"], result["verdict"])
Esempio di risposta
{
"catalog_id": 9,
"catalog_name": "Richieste assistenza",
"catalog_mode": "supervised",
"refinement_status": "templates_refined",
"total_texts": 2,
"processing_time_seconds": 0.42,
"threshold_used": 0.6,
"top_n": 3,
"results": [
{
"text": "Ho perso la password e non entro più",
"candidates": [
{ "label_path": ["Accesso"], "similarity": 0.81, "template_id": 22, "template_text": "Non riesco ad accedere al portale, password dimenticata" }
],
"best_label_path": ["Accesso"],
"best_similarity": 0.81,
"verdict": "strong",
"decision": "manual",
"classification_id": null,
"method": null,
"rule_id": null,
"rule_name": null
}
],
"distribution": { "Accesso": 1, "Fatturazione": 1 },
"version": null,
"method": null
}
verdict vale strong, medium, weak o none ed è calcolato sul punteggio del primo candidato e sulla distanza dal secondo. decision indica come trattare la proposta: auto (applicabile in automatico), review (da rivedere) o manual (lasciata all'operatore); i cataloghi di similarità e le versioni prive di soglie per etichetta non decidono mai auto. classification_id è valorizzato quando la classificazione viene registrata ed è il riferimento da indicare nel feedback. Un testo deciso da una regola del catalogo riporta method pari a rule, con rule_id e rule_name. A livello di risposta, version e method indicano la versione del classificatore che ha risposto, quando il catalogo ne ha una promossa.
Ogni testo classificato conta come un'esecuzione agentic (vedere Quote e crediti), anche quando decide una regola; i consumi di una richiesta sono addebitati insieme.
Errori specifici
| Codice | Significato |
|---|---|
422 | external_ids o headers con un numero di elementi diverso da texts (messaggio con EXTERNAL_IDS_LENGTH o HEADERS_LENGTH), oppure un identificativo vuoto. |
429 | Esecuzioni agentic esaurite: {"detail": {"code": "AGENTIC_QUOTA_EXCEEDED", "quota_code": "...", "used": ..., "limit": ...}}. La richiesta è respinta per intero e nessun testo viene classificato. |
502 | Con record: true o external_ids, il servizio di classificazione ha restituito un numero di risultati diverso dai testi (ML_RESULT_MISMATCH). |
Feedback sulle classificazioni
La decisione dell'operatore su una proposta si invia con POST /analysis/catalogs/{catalog_id}/feedback, sullo stesso catalogo usato per classificare. Le correzioni alimentano l'addestramento delle versioni successive del classificatore.
| Campo | In | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|---|
text | body | string (1–20000) | Sì | Il testo classificato, identico a quello inviato alla classificazione. |
classification_id | body | int | No | Il riferimento restituito dalla classificazione. |
external_id | body | string (1–200) | No | Identificativo del sistema chiamante; se manca, vale quello indicato alla classificazione. Un nuovo feedback con lo stesso identificativo aggiorna il precedente. |
outcome | body | string | No | confirmed (proposta accettata), corrected (predefinito, etichetta diversa) oppure none_valid (nessuna etichetta valida). |
correct_label_path | body | string[] | Per corrected | L'etichetta scelta dall'operatore. Senza, l'esito corrected è rifiutato con CORRECT_LABEL_REQUIRED. |
predicted_label_path | body | string[] | No | L'etichetta proposta. |
actor | body | string (max 200) | No | Codice dell'operatore nel sistema chiamante, conservato solo come hash non reversibile. |
POST /analysis/catalogs/{catalog_id}/feedback/batch accetta in items da 1 a 500 feedback con gli stessi campi e li applica per intero oppure per niente; GET /analysis/catalogs/{catalog_id}/feedback/summary restituisce i conteggi delle correzioni per esito ed etichetta.
GET /monitoring/classification-items
Elenca, dal più recente, gli elementi classificati per cui il sistema chiamante ha indicato un identificativo esterno: classificazioni e feedback dei cataloghi, classificazioni delle terne ed estrazioni degli attributi di Askme Desk. Riservato agli amministratori dell'organizzazione; gli altri utenti ricevono 403. Disponibile dalla versione 2.3.17.
| Parametro | In | Tipo | Descrizione |
|---|---|---|---|
external_id | query | string (1–255) | Identificativo da cercare. Per le organizzazioni in modalità tramite trova anche gli elementi conservati come hash. |
catalog_id | query | int | Catalogo. |
operation | query | string | Tipo di operazione, per esempio classification, feedback, attribute_extraction. |
from, to | query | datetime | Periodo: dall'istante from incluso all'istante to escluso. |
limit, offset | query | int | Paginazione: limit da 1 a 500 (predefinito 100). |
{
"items": [
{
"event_id": "b1e6c2d4-...",
"position": 0,
"catalog_id": 9,
"catalog_name": "Richieste assistenza",
"operation": "classification",
"model_version": 2,
"method": "linear_head",
"source_app": "api_direct",
"external_id": "26/00231",
"label": "Accesso | Password",
"confidence": 0.81,
"decision": "auto",
"outcome": "ok",
"correct_label": null,
"attributes": null,
"occurred_at": "2026-10-21T08:15:00Z"
}
],
"total": 1,
"limit": 100,
"offset": 0
}
label riporta l'etichetta proposta con i livelli separati da |. Per un feedback, outcome vale confirmed, corrected o none_valid e correct_label riporta l'etichetta scelta; per un'estrazione di attributi, attributes è il numero di attributi estratti. Gli elementi non contengono i testi classificati.
Altri endpoint dei cataloghi
| Endpoint | Metodo | Descrizione |
|---|---|---|
/analysis/catalogs | GET | Elenco paginato; filtri status (queued, building, ready, failed, cancelled) e mode. |
/analysis/catalogs/{catalog_id} | GET | Dettaglio del catalogo, con gli esempi rappresentativi (templates). |
/analysis/catalogs/{catalog_id} | PATCH | Modifica nome e soglia: { "name": "...", "threshold": 0.65 }. |
/analysis/catalogs/{catalog_id} | DELETE | Elimina il catalogo; su un catalogo in costruzione interrompe la costruzione. |
/analysis/catalogs/db/test-connection | POST | Verifica la connessione a un database da cui costruire il catalogo. |
/analysis/catalogs/db/preview | POST | Anteprima delle righe che verrebbero usate come esempi. |
/analysis/catalogs/db/build-from-db | POST | Costruisce il catalogo leggendo gli esempi dal database. |
/analysis/catalogs/queue-status | GET | Stato della coda di costruzione. |
/analysis/catalogs/{catalog_id}/requeue | POST | Rilancia un catalogo in stato failed o cancelled. |
/analysis/catalogs/{catalog_id}/refine-labels | POST | Migliora i nomi dei gruppi di un catalogo non supervisionato. |
/analysis/catalogs/{catalog_id}/refine-templates | POST | Seleziona gli esempi più rappresentativi di ciascun gruppo. |
/analysis/catalogs/{catalog_id}/templates/{template_id} | PATCH, DELETE | Modifica testo o label_path di un esempio, oppure lo rimuove. |
/analysis/catalogs/{catalog_id}/feedback, /feedback/batch | POST | Feedback dell'operatore su una classificazione, singolo o a lotti (vedere sopra). |
/analysis/catalogs/{catalog_id}/feedback/summary | GET | Riepilogo delle correzioni per esito ed etichetta. |
5. Editor PDF
| Endpoint | Metodo | Descrizione |
|---|---|---|
/analysis/pdf/fields | POST | Estrae i campi compilabili e le aree di firma di un modulo PDF. |
/analysis/pdf/edit | POST | Modifica un PDF secondo le istruzioni fornite. |
/analysis/pdf/smart-edit | POST | Modifica guidata dal modello di sezioni specifiche del PDF. |
6. Posizioni lavorative e prompt dei CV
| Endpoint | Metodo | Descrizione |
|---|---|---|
/analysis/job-positions | GET, POST | Elenco e creazione delle posizioni lavorative usate per valutare i CV. |
/analysis/job-positions/{position_id} | GET, PUT, DELETE | Dettaglio, modifica, eliminazione di una posizione. |
/analysis/cv-prompt | GET, PUT | Prompt di valutazione usato da POST /analysis/cv per l'organizzazione. |
7. Template di analisi
| Endpoint | Metodo | Descrizione |
|---|---|---|
/templates | GET, POST | Elenco e creazione dei template di analisi, richiamabili con template_id nei corpi di POST /analysis/*. |
/templates/{template_id} | GET, PUT, DELETE | Dettaglio, modifica, eliminazione. |
8. Report
| Endpoint | Metodo | Descrizione |
|---|---|---|
/reports | GET, POST | Elenco e generazione di report a partire dai job di analisi. |
/reports/{report_id} | GET | Dettaglio del report. |
/reports/{report_id}/download | GET | Download del report generato. |
9. Integrazione con Askme Desk
Gli endpoint seguenti supportano l'integrazione tra Askme AI e Askme Desk (suggerimenti di risposta, classificazione dei ticket, generazione di workflow) e sono usati di norma dall'integrazione tra i due prodotti, non da integrazioni dirette.
| Endpoint | Metodo | Descrizione |
|---|---|---|
/desk/suggestions | POST | Suggerimenti di risposta per un ticket. |
/desk/workflow/generate | POST | Proposta di workflow a partire da una descrizione. |
/desk/extract-attributes | POST | Estrazione di attributi strutturati da un ticket. |
/desk/classify-triple | POST | Classificazione su categoria, sottocategoria e motivazione. |
/desk/classifications/{classification_id} | GET | Dettaglio di una classificazione eseguita. |