Passa al contenuto principale

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.

Classificazione dei testi

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:

CampoTipoDescrizione
namestring (max 255)Nome del job, mostrato anche nella cronologia della console.
template_idintTemplate di analisi (sezione 7) da cui ereditare la configurazione.
optionsobjectOpzioni del job (vedere sotto).
Campo di optionsTipoPredefinitoDescrizione
modelstring—Modello da usare al posto di quello predefinito dell'organizzazione. Per l'analisi PDF vedere anche la scelta del modello.
temperaturefloat (0.0–2.0)—Temperatura di campionamento.
max_tokensint (1–128000)—Limite di token della risposta del modello.
languagestring—Lingua preferita per il risultato (it, en, ...).
output_formatstringjsonjson, html, markdown.
webhook_idint—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à.

CampoInTipoObbligatorioPredefinitoDescrizione
textbodystring (1–200000)Sì—Testo da analizzare.
custom_instructionsbodystring (max 4000)No—Istruzioni aggiuntive per l'analisi.
max_charsbodyint (100–200000)No20000Lunghezza 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.

CampoInTipoObbligatorioPredefinitoDescrizione
filenamebodystring (max 512)Sì*—Nome del file. *Obbligatorio per un singolo documento; con files è solo un'etichetta.
file_contentbodystring base64Sì*—Contenuto del PDF. *Uno solo tra file_content e file_path.
file_pathbodystringSì*—Percorso di un file già caricato nell'archivio dell'organizzazione.
filesbodyarrayNo—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_promptbodystring (max 8000)No—Domanda o istruzioni per l'analisi.
agent_idbodyintNo—Agente le cui istruzioni si usano quando custom_prompt è vuoto.
create_kb_agentbodyboolNofalseSe true, a fine analisi crea una base di conoscenza e un agente temporanei con il documento, per conversare con esso.
temp_agent_namebodystring (max 512)No—Nome dell'agente temporaneo.
skip_analysisbodyboolNofalseCrea 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

CodiceSignificato
422Né 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.

CampoInTipoObbligatorioDescrizione
file_contentbodystring base64SìCV (PDF o DOCX) in base64.
filenamebodystring (max 512)SìNome del file con estensione, che determina il formato.
job_position_idbodyintNoPosizione rispetto a cui valutare il CV. Se omessa, il CV viene valutato rispetto a tutte le posizioni.
cv_prompt_idbodyintNoPrompt 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.

CampoInTipoObbligatorioDescrizione
databodyobject[] (1–10000)SìRighe nella forma { "colonna": valore }.
columnsbodystring[]NoColonne da analizzare (predefinito: tutte).
custom_promptbodystring (max 4000)NoDomanda o istruzioni per l'analisi.
agent_idbodyintNoAgente 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.

CampoInTipoPredefinitoDescrizione
offsetqueryint0Paginazione.
limitqueryint (1–200)50Dimensione della pagina.
statusquerystring—pending, processing, completed, failed.
typequerystring—content, pdf, cv, data, ocr, document_verify, anonymize, catalog_build.
searchquerystring—Parte del nome del job.
sort_by, orderquerystringcreated_at, descOrdinamento.

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.

CampoInTipoObbligatorioPredefinitoDescrizione
fileform-datafileSì*—Immagine o PDF (massimo 50 MB). *Uno solo tra file e file_path.
file_pathform-datastringSì*—Percorso di un file già caricato nell'archivio dell'organizzazione.
languageform-datastringNoitaLingua del documento: ita, eng, fra, deu, spa.
template_idform-dataintNo—Template di analisi.
webhook_idform-dataintNo—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

CodiceSignificato
400Nessuno o entrambi tra file e file_path, tipo di file non gestito oppure file vuoto.
413File 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.

CampoInTipoObbligatorioPredefinitoDescrizione
filenamebodystring (max 512)Sì—Nome del file.
content_typebodystringSì—Tipo MIME: image/png, image/jpeg, image/tiff, image/webp, application/pdf.
file_contentbodystringSì (in alternativa)—Contenuto del documento in base64, in alternativa a file_path.
file_pathbodystringSì (in alternativa)—Percorso di un file già caricato, in alternativa a file_content.
document_typebodystringNogenericreceipt, form o generic: seleziona i controlli strutturali e i campi previsti.
expectedbodyobjectSì (in alternativa)—Forma compatta {codice: valore}: tipo, tolleranze e ancore derivano dal profilo.
expected_fieldsbodyarray (max 50)Sì (in alternativa)—Campi descritti per esteso; a parità di codice prevalgono su expected.
languagebodystringNoitaLingua del documento.
tolerance_levelbodystringNolivello configuratostrict, 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.

LivelloComportamento
strictConferma solo una lettura esatta. Qualunque differenza è uno scarto, e ciò che il documento non riporta trattiene l'esito.
balancedUn carattere fuori posto porta a una verifica manuale, non a una conferma.
relaxedIl 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

CampoValoriSignificato
verdictpassIl documento conferma quanto atteso.
failIl documento riporta un valore diverso, oppure manca un elemento che il livello considera vincolante.
inconclusiveLa lettura non basta a decidere: il caso va a revisione manuale.
fields[].statusmatch, mismatch, uncertain, not_foundEsito del singolo campo, con evidence che cita il testo da cui è stato letto.
fields[].sourceocr, agentIndica se il campo è stato deciso dalle regole deterministiche o dall'agente di validazione.
reasonselenco di codiciMotivi di scarto e segnalazioni, da ricondurre alle proprie casistiche.

Errori specifici

CodiceSignificato
400Tipo MIME non gestito.
422Nessun 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.

CampoInTipoObbligatorioDescrizione
fileform-datafileSì*Documento PDF, DOCX o XLSX, oppure un archivio ZIP che li contiene. *Uno solo tra file e file_path.
file_pathform-datastringSì*Percorso di un documento già caricato nell'archivio dell'organizzazione.
exclude_labelsform-datastringNoTipi di dato da lasciare invariati, separati da virgola (i valori disponibili si leggono con GET /analysis/anonymize/labels).
webhook_idform-dataintNoWebhook 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

CodiceSignificato
400Nessuno o entrambi tra file e file_path, formato non gestito oppure file vuoto.
413File 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.

CampoInTipoObbligatorioPredefinitoDescrizione
namebodystring (1–100)Sì—Nome del catalogo.
modebodystringSì—supervised (richiede label_path su almeno un esempio) oppure unsupervised.
thresholdbodyfloat (0.0–1.0)No0.6Somiglianza minima per accettare un candidato.
pairsbodyobject[]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

CodiceSignificato
400pairs 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.

CampoInTipoObbligatorioPredefinitoDescrizione
catalog_idbodyintSì—Catalogo.
textsbodystring[]Sì—Testi da classificare.
top_nbodyint (1–10)No3Numero di candidati per testo.
threshold_overridebodyfloat (0.0–1.0)No—Soglia da usare al posto di quella del catalogo, solo per questa richiesta.
external_idsbody(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.
recordbodyboolNofalseRegistra la classificazione anche senza external_ids.
headersbodyobject[]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_templatesbodyboolNofalseCon 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-IdheaderstringNo—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

CodiceSignificato
422external_ids o headers con un numero di elementi diverso da texts (messaggio con EXTERNAL_IDS_LENGTH o HEADERS_LENGTH), oppure un identificativo vuoto.
429Esecuzioni agentic esaurite: {"detail": {"code": "AGENTIC_QUOTA_EXCEEDED", "quota_code": "...", "used": ..., "limit": ...}}. La richiesta è respinta per intero e nessun testo viene classificato.
502Con 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.

CampoInTipoObbligatorioDescrizione
textbodystring (1–20000)SìIl testo classificato, identico a quello inviato alla classificazione.
classification_idbodyintNoIl riferimento restituito dalla classificazione.
external_idbodystring (1–200)NoIdentificativo del sistema chiamante; se manca, vale quello indicato alla classificazione. Un nuovo feedback con lo stesso identificativo aggiorna il precedente.
outcomebodystringNoconfirmed (proposta accettata), corrected (predefinito, etichetta diversa) oppure none_valid (nessuna etichetta valida).
correct_label_pathbodystring[]Per correctedL'etichetta scelta dall'operatore. Senza, l'esito corrected è rifiutato con CORRECT_LABEL_REQUIRED.
predicted_label_pathbodystring[]NoL'etichetta proposta.
actorbodystring (max 200)NoCodice 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.

ParametroInTipoDescrizione
external_idquerystring (1–255)Identificativo da cercare. Per le organizzazioni in modalità tramite trova anche gli elementi conservati come hash.
catalog_idqueryintCatalogo.
operationquerystringTipo di operazione, per esempio classification, feedback, attribute_extraction.
from, toquerydatetimePeriodo: dall'istante from incluso all'istante to escluso.
limit, offsetqueryintPaginazione: 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​

EndpointMetodoDescrizione
/analysis/catalogsGETElenco paginato; filtri status (queued, building, ready, failed, cancelled) e mode.
/analysis/catalogs/{catalog_id}GETDettaglio del catalogo, con gli esempi rappresentativi (templates).
/analysis/catalogs/{catalog_id}PATCHModifica nome e soglia: { "name": "...", "threshold": 0.65 }.
/analysis/catalogs/{catalog_id}DELETEElimina il catalogo; su un catalogo in costruzione interrompe la costruzione.
/analysis/catalogs/db/test-connectionPOSTVerifica la connessione a un database da cui costruire il catalogo.
/analysis/catalogs/db/previewPOSTAnteprima delle righe che verrebbero usate come esempi.
/analysis/catalogs/db/build-from-dbPOSTCostruisce il catalogo leggendo gli esempi dal database.
/analysis/catalogs/queue-statusGETStato della coda di costruzione.
/analysis/catalogs/{catalog_id}/requeuePOSTRilancia un catalogo in stato failed o cancelled.
/analysis/catalogs/{catalog_id}/refine-labelsPOSTMigliora i nomi dei gruppi di un catalogo non supervisionato.
/analysis/catalogs/{catalog_id}/refine-templatesPOSTSeleziona gli esempi più rappresentativi di ciascun gruppo.
/analysis/catalogs/{catalog_id}/templates/{template_id}PATCH, DELETEModifica testo o label_path di un esempio, oppure lo rimuove.
/analysis/catalogs/{catalog_id}/feedback, /feedback/batchPOSTFeedback dell'operatore su una classificazione, singolo o a lotti (vedere sopra).
/analysis/catalogs/{catalog_id}/feedback/summaryGETRiepilogo delle correzioni per esito ed etichetta.

5. Editor PDF​

EndpointMetodoDescrizione
/analysis/pdf/fieldsPOSTEstrae i campi compilabili e le aree di firma di un modulo PDF.
/analysis/pdf/editPOSTModifica un PDF secondo le istruzioni fornite.
/analysis/pdf/smart-editPOSTModifica guidata dal modello di sezioni specifiche del PDF.

6. Posizioni lavorative e prompt dei CV​

EndpointMetodoDescrizione
/analysis/job-positionsGET, POSTElenco e creazione delle posizioni lavorative usate per valutare i CV.
/analysis/job-positions/{position_id}GET, PUT, DELETEDettaglio, modifica, eliminazione di una posizione.
/analysis/cv-promptGET, PUTPrompt di valutazione usato da POST /analysis/cv per l'organizzazione.

7. Template di analisi​

EndpointMetodoDescrizione
/templatesGET, POSTElenco e creazione dei template di analisi, richiamabili con template_id nei corpi di POST /analysis/*.
/templates/{template_id}GET, PUT, DELETEDettaglio, modifica, eliminazione.

8. Report​

EndpointMetodoDescrizione
/reportsGET, POSTElenco e generazione di report a partire dai job di analisi.
/reports/{report_id}GETDettaglio del report.
/reports/{report_id}/downloadGETDownload del report generato.

9. Integrazione con Askme Desk​

Per chi integra 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.

EndpointMetodoDescrizione
/desk/suggestionsPOSTSuggerimenti di risposta per un ticket.
/desk/workflow/generatePOSTProposta di workflow a partire da una descrizione.
/desk/extract-attributesPOSTEstrazione di attributi strutturati da un ticket.
/desk/classify-triplePOSTClassificazione su categoria, sottocategoria e motivazione.
/desk/classifications/{classification_id}GETDettaglio di una classificazione eseguita.

Pagine correlate​