Endpoint: Agenti, Chat e Conversazioni
Riferimento degli endpoint per la gestione degli agenti conversazionali, l'invio di messaggi (con risposta completa o in streaming), le conversazioni, la traduzione di testi, i connettori MCP associabili agli agenti, le valutazioni e i template di prompt riutilizzabili.
Tutte le richieste usano l'URL base https://ai.askme.it/api/v2 e le intestazioni descritte in Autenticazione (X-Api-Key, e facoltativamente X-Source-App e X-External-Id). Gli elenchi seguono la paginazione offset/limit descritta nella Panoramica. Accanto a ogni endpoint è indicato, dove rilevante, il permesso che gli scope della chiave devono coprire.
1. Agenti
POST /agents
Crea un nuovo agente nell'organizzazione. Permesso: agents.create.
Attributi della richiesta
| Campo | In | Tipo | Obbligatorio | Predefinito | Descrizione |
|---|---|---|---|---|---|
name | body | string (1–255) | Sì | — | Nome dell'agente. |
description | body | string | No | — | Descrizione libera. |
model_provider | body | string | No | openai | Fornitore del modello: openai, anthropic, google, mistral, aruba, ollama, local. |
model_name | body | string (max 100) | No | gpt-4o-mini | Modello da usare, tra quelli abilitati per l'organizzazione (l'elenco si legge con GET /ai/models). |
system_prompt | body | string | No | — | Istruzioni di sistema dell'agente. Può contenere variabili {{nome_variabile}}, valorizzate con template_vars in POST /chat. |
temperature | body | float (0.0–2.0) | No | 0.7 | Temperatura di campionamento. |
max_tokens | body | int (1–128000) | No | 2048 | Numero massimo di token della risposta. |
reasoning_effort | body | string | No | — | Solo per i modelli con ragionamento: minimal, low, medium, high. |
rag_config | body | object | No | — | Basi di conoscenza consultate dall'agente (vedere sotto). |
handoff_config | body | object | No | — | Regole di passaggio all'operatore: enabled e l'elenco conditions. Si consiglia di configurarle dalla console e di riusare l'oggetto restituito da GET /agents/{agent_id}. |
tools_config | body | object | No | — | Impostazioni degli strumenti, per esempio require_tool_approval: true per chiedere un'approvazione prima di ogni chiamata a uno strumento. |
output_schema | body | object (JSON Schema) | No | — | Schema JSON a cui la risposta del modello deve attenersi (output strutturato). |
mcp_server_ids | body | int[] | No | — | Connettori MCP da associare. Omesso o []: nessuna associazione. Ogni id deve appartenere all'organizzazione. |
parent_id | body | int | No | — | Agente genitore: le istruzioni effettive sono la concatenazione lungo la catena (massimo 10 livelli). |
icon | body | object | No | — | Icona dell'agente: {"kind": "material", "value": "support_agent"} (nome di un'icona Material in minuscolo) oppure {"kind": "image", "value": "https://..."} (indirizzo http(s) o percorso che inizia con /). Omesso: icona predefinita. |
is_temporary | body | bool | No | false | Se true, l'agente non compare negli elenchi standard. |
Il campo rag_config
| Campo | Tipo | Descrizione |
|---|---|---|
enabled | bool | Attiva la consultazione delle basi di conoscenza. |
knowledge_base_ids | int[] | Identificativi delle basi di conoscenza consultate dall'agente. |
rewrite_instructions | string (max 1000) | Indicazioni facoltative per riformulare le domande nel dominio dell'agente prima della ricerca (per esempio la terminologia di settore). |
Il flusso di recupero (riformulazione della domanda, ricerca ibrida, riordino e filtro di pertinenza) è lo stesso per tutti gli agenti ed è regolato dalla piattaforma. Chiavi diverse da quelle elencate, inviate da versioni precedenti o da client esterni, vengono ignorate senza errore.
Esempio di richiesta
curl -X POST "https://ai.askme.it/api/v2/agents" \
-H "X-Api-Key: amai_xxxxxxxxxxxxxxxx" \
-H "X-Source-App: api_direct" \
-H "Content-Type: application/json" \
-d '{
"name": "Assistente sito",
"description": "Risponde sui servizi dell'\''azienda usando il sito pubblico e le FAQ.",
"model_provider": "openai",
"model_name": "gpt-5-mini",
"system_prompt": "Sei l'\''assistente virtuale del sito. Rispondi in italiano, con registro formale, solo sulla base delle informazioni disponibili.",
"temperature": 0.35,
"max_tokens": 1024,
"reasoning_effort": "minimal",
"rag_config": { "enabled": true, "knowledge_base_ids": [10, 11, 12] },
"icon": { "kind": "material", "value": "support_agent" }
}'
Esempio di risposta (201 Created)
{
"id": 54,
"tenant_id": "5b0e2f7a-2c41-4d0e-9a6b-1c9e8f3d4a21",
"name": "Assistente sito",
"description": "Risponde sui servizi dell'azienda usando il sito pubblico e le FAQ.",
"model_provider": "openai",
"model_name": "gpt-5-mini",
"system_prompt": "Sei l'assistente virtuale del sito. Rispondi in italiano, con registro formale, solo sulla base delle informazioni disponibili.",
"temperature": 0.35,
"max_tokens": 1024,
"tools_config": null,
"rag_config": { "enabled": true, "knowledge_base_ids": [10, 11, 12] },
"handoff_config": null,
"reasoning_effort": "minimal",
"mcp_server_ids": [],
"parent_id": null,
"is_temporary": false,
"output_schema": null,
"icon": { "kind": "material", "value": "support_agent" },
"status": "draft",
"version": 1,
"created_by": "apikey:28",
"created_at": "2026-09-24T17:09:03.035266Z",
"updated_at": "2026-09-24T17:09:03.035266Z"
}
Un agente creato tramite API è in stato draft: risponde già alle richieste POST /chat che lo indicano con agent_id, ma compare in GET /chat/agents e nella chat della console solo dopo l'attivazione, che si esegue impostando "status": "active" con PUT /agents/{agent_id}. Il campo created_by vale apikey:<id> per le risorse create con una chiave.
Esempio di handoff_config restituito per un agente con passaggio all'operatore attivo:
{
"enabled": true,
"conditions": [
{ "id": "scope", "type": "scope", "enabled": true, "timing": "immediate", "description": "Domanda fuori ambito" },
{ "id": "human_handoff", "type": "intent", "enabled": true, "timing": "immediate", "min_confidence": 0.5, "description": "Richiesta esplicita di un operatore umano" }
]
}
Errori specifici
| Codice | Significato |
|---|---|
400 | mcp_server_ids contiene connettori che non appartengono all'organizzazione, oppure la configurazione non è valida. |
422 | Validazione non superata (per esempio model_provider fuori elenco, icon non valida, temperature fuori intervallo). |
GET /agents
Elenca gli agenti dell'organizzazione. Permesso: agents.read.
Attributi della richiesta
| Campo | In | Tipo | Obbligatorio | Predefinito | Descrizione |
|---|---|---|---|---|---|
offset | query | int (≥0) | No | 0 | Offset di paginazione. |
limit | query | int (1–100) | No | 50 | Numero massimo di elementi. |
search | query | string | No | — | Ricerca testuale su nome e descrizione. |
status | query | string | No | — | Stato: draft, active, paused, archived. |
model_name | query | string | No | — | Modello esatto. |
parent_id | query | int | No | — | Solo gli agenti figli dell'agente indicato. |
mcp_server_id | query | int | No | — | Solo gli agenti che usano il connettore MCP indicato. |
knowledge_base_id | query | int | No | — | Solo gli agenti che consultano la base di conoscenza indicata. |
include_temporary | query | bool | No | false | Include gli agenti temporanei (per esempio quelli creati dall'analisi PDF per conversare con un documento). |
sort_by | query | string | No | — | Campo di ordinamento (name, created_at, updated_at). |
order | query | string | No | — | asc o desc. |
Esempio di richiesta
curl -G "https://ai.askme.it/api/v2/agents" \
-H "X-Api-Key: amai_xxxxxxxxxxxxxxxx" \
--data-urlencode "limit=20" \
--data-urlencode "status=active"
La risposta (200 OK) ha la forma { "items": [...], "total": 3, "offset": 0, "limit": 20 }, con ogni elemento nello stesso formato di POST /agents.
GET /agents/{agent_id}
Restituisce un agente. Permesso: agents.read. Risposta nello stesso formato di POST /agents; 404 se l'agente non esiste o appartiene a un'altra organizzazione.
PUT /agents/{agent_id}
Aggiorna l'agente. Permesso: agents.update. Tutti i campi sono facoltativi e vengono modificati solo quelli inviati; ogni modifica incrementa version e salva un'istantanea della configurazione. Oltre ai campi di POST /agents:
| Campo | In | Tipo | Descrizione |
|---|---|---|---|
status | body | string | draft, active, paused, archived. |
mcp_server_ids | body | int[] | null | null o omesso: associazioni invariate; []: rimuove tutte le associazioni; [n, m]: sostituisce le associazioni con quelle indicate. |
curl -X PUT "https://ai.askme.it/api/v2/agents/54" \
-H "X-Api-Key: amai_xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "status": "active", "temperature": 0.3 }'
DELETE /agents/{agent_id}
Elimina l'agente e libera la quota agents. Permesso: agents.delete. Risposta 204 No Content.
POST /agents/{agent_id}/duplicate
Crea una copia indipendente dell'agente, in stato draft. Permesso: agents.create. Risponde 201 con il nuovo agente; la copia consuma la quota agents come una nuova creazione.
GET /agents/{agent_id}/versions
Elenca le versioni salvate della configurazione (version_number, config_snapshot, changelog, created_at, created_by). Paginazione offset/limit (massimo 200).
POST /agents/{agent_id}/rollback/{version_number}
Riporta la configurazione a una versione precedente e registra una nuova versione con changelog "Rollback to version N". Permesso: agents.update. Risponde 404 se la versione non esiste.
GET /agents/{agent_id}/usage-sources
Indica chi usa l'agente: le chiavi API e le applicazioni che hanno avviato conversazioni con esso.
{
"items": [
{ "kind": "product", "id": 1, "label": "api_direct", "detail": "4 conversazioni" }
],
"total": 1
}
kind vale api_key oppure product.
Connettori MCP di un agente
Oltre al campo mcp_server_ids di POST e PUT /agents, sono disponibili endpoint dedicati (permesso agents.update per le modifiche):
POST /agents/{agent_id}/mcp-servers: corpo{ "mcp_server_id": 12 }. Risponde201, oppure409se il connettore è già associato.GET /agents/{agent_id}/mcp-servers: connettori associati, con il dettaglio.DELETE /agents/{agent_id}/mcp-servers/{mcp_server_id}: rimuove l'associazione (204, oppure404).
2. Chat
POST /chat
Invia un messaggio a un agente e restituisce la risposta, completa oppure in streaming. È l'endpoint principale per le integrazioni conversazionali.
- Modalità agente: si indica
agent_id. Modello, istruzioni, basi di conoscenza e connettori derivano dalla configurazione dell'agente. - Modalità diretta: si omette
agent_ide si fornisconomodelesystem_prompt. La chiamata va direttamente al modello, senza basi di conoscenza.
Attributi della richiesta
| Campo | In | Tipo | Obbligatorio | Predefinito | Descrizione |
|---|---|---|---|---|---|
message | body | string (1–32000) | Sì | — | Testo del messaggio dell'utente. |
agent_id | body | int | No* | — | Agente. *Se omesso sono obbligatori model e system_prompt. |
conversation_id | body | int | No | — | Conversazione esistente da proseguire. Se assente (e senza external_conversation_id) viene creata una nuova conversazione. |
external_conversation_id | body | string | No | — | Identificativo della conversazione nel sistema chiamante. La prima richiesta crea la conversazione con questo identificativo; le richieste successive con lo stesso valore e lo stesso agente la proseguono. Consente di non memorizzare il conversation_id. |
stream | body | bool | No | false | Se true la risposta è uno stream SSE. |
include_sources | body | bool | No | true | Include le fonti usate (rag_sources). |
supports_citations | body | bool | No | false | Chiede al modello di citare le fonti nella risposta con riferimenti numerati [N]; ogni fonte citata riporta il numero in citation_index. Richiede include_sources. |
include_thinking | body | bool | No | false | Per i modelli con ragionamento, restituisce anche il ragionamento (thinking_content e, in streaming, eventi thinking). |
template_vars | body | object (string→string) | No | — | Valori delle variabili {{nome}} presenti nelle istruzioni dell'agente. |
files | body | array | No | — | Allegati: [{ "filename", "content_type", "data" }], con data in base64. |
temperature | body | float | No | (dell'agente) | Temperatura per questo messaggio. |
max_tokens | body | int | No | (dell'agente) | Limite di token per questo messaggio. |
model | body | string | No* | — | Solo in modalità diretta. |
system_prompt | body | string | No* | — | Solo in modalità diretta. |
response_format | body | string | No | markdown | Formato del testo della risposta: markdown o html. |
llm_response_format | body | object | No | — | Schema JSON per un output strutturato valido solo per questo messaggio. |
classification_intents | body | array | No | — | Categorie di intento personalizzate per la classificazione del messaggio: [{ "name", "description" }]. |
force_handoff | body | bool | No | false | Richiede il passaggio all'operatore. |
skip_handoff | body | bool | No | false | Non valuta il passaggio all'operatore per questo messaggio. |
skip_scope_check | body | bool | No | false | Non valuta se la domanda è fuori ambito per questo messaggio. |
metadata | body | object | No | — | Metadati liberi, riportati nel record di consumo. |
mcp_auth | body | object (string→string) | No | — | Credenziali dell'utente per i connettori MCP che le richiedono, indicizzate per connettore. |
tool_approval | body | object | No | — | Risposta a una richiesta di approvazione di uno strumento: { "approved": true, "pending_message_id": 5932 } (vedere Approvazione degli strumenti). |
L'attribuzione dei consumi si imposta con le intestazioni X-Source-App e X-External-Id (si veda Autenticazione); i campi source_app ed external_id del corpo non sostituiscono le intestazioni. Con una chiave API le fonti nascoste delle basi di conoscenza non vengono mai restituite.
Esempio di richiesta (cURL)
curl -X POST "https://ai.askme.it/api/v2/chat" \
-H "X-Api-Key: amai_xxxxxxxxxxxxxxxx" \
-H "X-Source-App: api_direct" \
-H "X-External-Id: ticket-9281" \
-H "Content-Type: application/json" \
-d '{
"agent_id": 54,
"message": "Di cosa si occupa Lascaux?",
"external_conversation_id": "ticket-9281",
"stream": false
}'
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": os.environ.get("ASKMEAI_SOURCE_APP", "api_direct"),
},
timeout=60.0,
)
resp = client.post("/chat", json={
"agent_id": 54,
"message": "Di cosa si occupa Lascaux?",
"external_conversation_id": "ticket-9281",
"stream": False,
})
resp.raise_for_status()
data = resp.json()
print(data["content"])
for source in data.get("rag_sources") or []:
print("-", source["metadata"].get("document_title") or source["title"])
Esempio di risposta (200 OK)
{
"message_id": 5921,
"conversation_id": 2758,
"external_conversation_id": "ticket-9281",
"content": "Lascaux è un'impresa che offre servizi informatici orientati alla trasformazione digitale delle aziende. [...]",
"thinking_content": null,
"content_format": "markdown",
"role": "assistant",
"model_used": "gpt-5-mini-2025-08-07",
"tokens_input": 2415,
"tokens_output": 243,
"latency_ms": 7333,
"rag_sources": [
{
"document_id": "100",
"knowledge_base_id": 10,
"chunk_id": "15",
"title": null,
"content_preview": "Mettiti in contatto: troveremo insieme le risposte che cerchi [...]",
"relevance_score": 0.029828,
"metadata": {
"source_url": "https://www.lascaux.it/contatti",
"source_type": "text",
"document_title": "Contatti - Lascaux",
"source_priority": 2
},
"is_visible": true,
"passages": [
{ "chunk_id": "15", "content_preview": "Mettiti in contatto: [...]", "relevance_score": 0.029828 },
{ "chunk_id": "14", "content_preview": "Contatti. Telefono, la sede, scrivici [...]", "relevance_score": 0.029762 }
]
}
],
"confidence_score": 0.63,
"needs_review": false,
"handoff_requested": false,
"handoff_reason": null,
"handoff_trigger": null,
"intent": null,
"handoff_intent": null,
"sentiment": null,
"is_in_scope": null,
"classified_intent": null,
"intent_confidence": null,
"handoff_criterion": null,
"tool_calls_count": 0,
"tools_used": [],
"pending_tool_approval": false,
"pending_tool_calls": null,
"pending_message_id": null,
"created_at": "2026-09-24T17:10:58.072295Z"
}
Il testo della risposta è nel campo content, al primo livello dell'oggetto.
Campi delle fonti (rag_sources[])
| Campo | Descrizione |
|---|---|
document_id | Identificativo del documento (stringa). |
knowledge_base_id | Base di conoscenza del documento. |
chunk_id | Frammento più pertinente del documento. |
title | Titolo del documento, quando disponibile. Per le pagine web il titolo è in metadata.document_title. |
content_preview | Estratto del frammento più pertinente. |
relevance_score | Punteggio di pertinenza del frammento, utile a ordinare le fonti tra loro; la scala dipende dalla configurazione del recupero e non va interpretata come una percentuale. |
metadata | Metadati del documento, per esempio source_url, document_title, source_type. |
is_visible | Sempre true per le richieste con chiave API. |
citation_index | Presente solo con supports_citations: il numero [N] con cui la risposta cita la fonte. |
passages[] | Presente quando la risposta si basa su più frammenti dello stesso documento: chunk_id, content_preview, relevance_score di ciascun frammento, il più pertinente per primo. |
Altri campi della risposta
| Campo | Descrizione |
|---|---|
confidence_score, needs_review | Confidenza della risposta (0–1) e indicazione che la risposta merita una revisione. |
handoff_requested, handoff_reason, handoff_trigger, handoff_criterion, handoff_intent | Esito della valutazione del passaggio all'operatore: se è richiesto, per quale motivo, quale condizione lo ha attivato e quale segnale di escalation è stato riconosciuto. |
intent, classified_intent, intent_confidence | Intento del messaggio e relativa confidenza, valorizzati quando la classificazione è attiva (per esempio con classification_intents). |
sentiment, is_in_scope | Sentimento del messaggio e valutazione dell'ambito, quando calcolati. |
tool_calls_count, tools_used | Chiamate a strumenti eseguite nel turno. |
pending_tool_approval, pending_tool_calls, pending_message_id | Presenti quando il turno è sospeso in attesa di approvazione di uno strumento. |
Risposta in streaming (SSE)
Con "stream": true il servizio risponde con Content-Type: text/event-stream. Ogni evento è una riga data: seguita da un oggetto JSON con il campo type:
data: {"type": "...", ...}
Gli eventi riportano in genere tutti i campi del formato, valorizzati a null quando non pertinenti: il client distingue gli eventi dal campo type e legge solo i campi indicati nella tabella. Il testo arriva in porzioni brevi, anche di poche lettere, da concatenare.
| Tipo | Quando | Campi principali |
|---|---|---|
rag_thinking | Subito dopo l'avvio in modalità agente, e di nuovo quando inizia la ricerca nelle basi di conoscenza (può quindi arrivare più volte). È un segnale senza contenuto, utile a mostrare un indicatore di attesa | — |
sources | Fonti usate. Con supports_citations arriva a fine risposta e contiene solo le fonti citate | sources[] (stesso formato di rag_sources) |
thinking | Porzione del ragionamento del modello, solo con include_thinking: true | content |
content | Porzione del testo della risposta, da concatenare nell'ordine di arrivo | content |
tool_call | Il modello invoca uno strumento | tool_name, tool_args |
tool_approval_required | Lo strumento richiede un'approvazione; il turno si riprende con tool_approval | pending_tool_calls[], pending_message_id |
input_required | Lo strumento chiede dati all'utente tramite un modulo | tool_name, input_requests, pending_message_id |
confidence | Punteggio di confidenza della risposta | confidence_score, confidence_signals, needs_review |
handoff_decision | Esito della valutazione del passaggio all'operatore, solo per gli agenti con passaggio all'operatore attivo | handoff_requested, handoff_reason, handoff_trigger, handoff_intent, handoff_criterion |
usage | Riepilogo del turno | usage.message_id, usage.conversation_id, usage.model_used, usage.tokens_input, usage.tokens_output, usage.latency_ms, e gli stessi indicatori di confidenza e passaggio all'operatore della risposta completa |
done | Fine dello stream | — |
error | Errore verificatosi dopo l'avvio dello stream | error (messaggio), code |
Codici dell'evento error:
code | Significato |
|---|---|
quota_exceeded | Quota delle conversazioni del piano esaurita. |
context_window_exceeded | Il messaggio, con la cronologia, supera la finestra di contesto del modello. |
invalid_request | Parametri non validi (per esempio un agente o una conversazione inesistenti). |
missing_provider_api_key | Il fornitore del modello non è configurato per l'organizzazione; error riporta il nome del fornitore. |
internal_error | Errore interno. |
| (assente) | Errore del fornitore del modello durante la generazione; il messaggio è in error. |
Dopo un evento error può seguire l'evento done. Poiché lo stato HTTP 200 è già stato inviato quando lo stream inizia, gli errori successivi arrivano solo come eventi.
Esempio di stream senza supports_citations (estratto, campi a null omessi per leggibilità; senza citazioni le fonti arrivano prima del testo):
data: {"type":"rag_thinking"}
data: {"type":"rag_thinking"}
data: {"type":"sources","sources":[{"document_id":"100","knowledge_base_id":10,"chunk_id":"15","title":null,"content_preview":"Mettiti in contatto: [...]","relevance_score":0.029828,"metadata":{"source_url":"https://www.lascaux.it/contatti"},"is_visible":true}]}
data: {"type":"content","content":"L"}
data: {"type":"content","content":"asc"}
data: {"type":"content","content":"aux è un'impresa"}
data: {"type":"confidence","confidence_score":0.63,"confidence_signals":{"length":1.0,"overlap":0.8,"coverage":1.0,"uncertainty":1.0},"needs_review":false}
data: {"type":"handoff_decision","handoff_requested":false}
data: {"type":"usage","usage":{"message_id":5921,"conversation_id":2758,"model_used":"gpt-5-mini-2025-08-07","tokens_input":2415,"tokens_output":243,"latency_ms":7333,"needs_review":false,"handoff_requested":false,"tool_calls_count":0,"tools_used":[]}}
data: {"type":"done"}
Esempio Python (httpx, lettura dello stream)
import json
import os
import httpx
with httpx.stream(
"POST",
os.environ["ASKMEAI_HOST"] + "/api/v2/chat",
headers={
"X-Api-Key": os.environ["ASKMEAI_API_KEY"],
"X-Source-App": "api_direct",
"Accept": "text/event-stream",
},
json={"agent_id": 54, "message": "Come posso contattare Lascaux?", "stream": True},
timeout=None,
) as resp:
resp.raise_for_status()
for raw in resp.iter_lines():
if not raw.startswith("data: "):
continue
event = json.loads(raw[len("data: "):])
kind = event["type"]
if kind == "content":
print(event["content"], end="", flush=True)
elif kind == "error":
print(f"\n[errore {event.get('code')}] {event.get('error')}")
elif kind == "usage":
print(f"\n[conversazione {event['usage']['conversation_id']}]")
elif kind == "done":
print()
break
Errori specifici
| Codice | Significato |
|---|---|
400 | Parametri non validi, per esempio un agent_id o un conversation_id inesistenti o di un'altra organizzazione (detail: Invalid request parameters). |
413 | Il messaggio supera la finestra di contesto del modello: detail.code vale context_window_exceeded e detail riporta model, context_window, estimated_input_tokens, max_output_tokens, total_needed. |
422 | Validazione non superata, per esempio message vuoto o oltre 32000 caratteri, oppure modalità diretta senza model o system_prompt. |
429 | Quota delle conversazioni del piano esaurita (all'avvio di una nuova conversazione), crediti AI esauriti oppure limite di frequenza della chiave (con Retry-After). |
503 | Troppi stream contemporanei: ripetere la richiesta più tardi. |
Approvazione degli strumenti
Se l'agente è configurato per chiedere un'approvazione prima di usare uno strumento (tools_config.require_tool_approval, oppure un'impostazione del singolo strumento del connettore), il turno si sospende: la risposta completa riporta pending_tool_approval: true, pending_tool_calls[] (tool_call_id, tool_name, tool_args) e pending_message_id, mentre lo stream emette l'evento tool_approval_required. Per riprendere il turno si invia un nuovo POST /chat con lo stesso agente e la stessa conversazione e il campo tool_approval:
{
"agent_id": 55,
"conversation_id": 2790,
"message": "Approvato",
"tool_approval": { "approved": true, "pending_message_id": 5932 }
}
Con "approved": false lo strumento non viene eseguito e il modello risponde senza il suo risultato.
GET /chat/agents
Elenca gli agenti attivi con cui si può avviare una conversazione, con le sole informazioni utili a sceglierli (mai istruzioni, modello o connettori). Permesso: chat.agents.list oppure agents.read.
{
"items": [
{
"id": 54,
"name": "Assistente sito",
"description": "Risponde sui servizi dell'azienda usando il sito pubblico e le FAQ.",
"icon": null,
"has_prompt_placeholders": false,
"prompt_placeholders": []
},
{
"id": 55,
"name": "Supporto interno",
"description": "Supporto ai reparti, con il contesto del reparto richiedente.",
"icon": { "kind": "material", "value": "support_agent" },
"has_prompt_placeholders": true,
"prompt_placeholders": ["reparto"]
}
]
}
prompt_placeholders elenca le variabili {{nome}} delle istruzioni, da valorizzare con template_vars in POST /chat.
POST /classify
Classifica un messaggio (intento, sentimento, ambito) e valuta il passaggio all'operatore secondo la configurazione dell'agente, senza generare una risposta. Risponde 404 se l'agente non appartiene all'organizzazione.
| Campo | In | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|---|
agent_id | body | int | Sì | Agente di cui usare le regole. |
message | body | string (1–32000) | Sì | Messaggio da classificare. |
conversation_id | body | int | No | Conversazione di contesto. |
external_conversation_id | body | string | No | Conversazione di contesto, tramite identificativo esterno. |
classification_intents | body | array | No | Categorie di intento personalizzate: [{ "name", "description" }]. |
external_id | body | string (max 255) | No | Identificativo del sistema chiamante registrato sui costi della classificazione. L'intestazione X-External-Id ha la precedenza; se mancano entrambi vale external_conversation_id. |
La risposta contiene intent (null se nessuna delle categorie personalizzate corrisponde), confidence, should_handoff, handoff_reason, handoff_trigger, handoff_intent, sentiment, sentiment_score, is_in_scope, complexity, complexity_score.
Le condizioni di passaggio all'operatore impostate su Dopo i tentativi contano le risposte del bot su tutta la conversazione indicata (vedere Handoff). Ogni classificazione conta come un'esecuzione agentic.
POST /decisions
Risponde a domande tipizzate (sì o no, scelta fra opzioni, punteggio) su un testo, una conversazione o un oggetto, e restituisce per ogni domanda la probabilità delle opzioni. Usa il modello decisionale dell'organizzazione, cioè un modello del catalogo con la capacità Decisionale (vedere Modelli LLM). Disponibile dalla versione 2.3.17.
| Campo | In | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|---|
state | body | string | object[] | object | Sì | Lo stato da giudicare: un testo, una conversazione ([{ "role": "user" | "assistant", "content" }]) oppure un oggetto libero. |
questions | body | object | Sì | Da 1 a 8 domande, indicizzate per nome. Ogni domanda ha type e instructions, più criteria secondo il tipo (vedere sotto). |
model | body | string | No | Modello decisionale da usare al posto di quello dell'organizzazione. |
external_id | body | string (max 255) | No | Identificativo del sistema chiamante registrato sui costi. L'intestazione X-External-Id ha la precedenza. |
type | criteria | Risposta |
|---|---|---|
noul (sì o no) | Facoltativo: { "true": "...", "false": "..." } | noul: probabilità del sì (da 0 a 1) |
choice (scelta) | Obbligatorio: { "etichetta": "descrizione" | null }, almeno 2 voci | choice, confidence e probabilities per etichetta |
score (punteggio) | Obbligatorio: elenco delle descrizioni dei livelli, dal più basso, almeno 2 | score (indice del livello), confidence e probabilities per livello |
Lo stato, le istruzioni e le descrizioni dei criteri passano sempre dalla protezione dei dati personali dell'organizzazione prima di arrivare al modello; le etichette delle domande choice restano invariate, perché le risposte sono indicizzate su di esse.
Esempio di richiesta
curl -X POST "https://ai.askme.it/api/v2/decisions" \
-H "X-Api-Key: amai_xxxxxxxxxxxxxxxx" \
-H "X-External-Id: ticket-4711" \
-H "Content-Type: application/json" \
-d '{
"state": [
{ "role": "user", "content": "Buongiorno, non riesco ad accedere al mio account." },
{ "role": "assistant", "content": "Può descrivere l'\''errore che vede?" },
{ "role": "user", "content": "Dice che la password è errata, ma è urgente." }
],
"questions": {
"urgent": { "type": "noul", "instructions": "Il messaggio è urgente?" },
"topic": {
"type": "choice",
"instructions": "Qual è l'\''argomento della richiesta?",
"criteria": { "accesso": "Problemi di login", "fatturazione": null }
},
"tone": {
"type": "score",
"instructions": "Valuta il tono dell'\''utente.",
"criteria": ["sereno", "neutro", "irritato"]
}
}
}'
Esempio di risposta
{
"model": "gpt-6-luna",
"answers": {
"urgent": { "type": "noul", "noul": 0.93 },
"topic": {
"type": "choice",
"choice": "accesso",
"confidence": 0.91,
"probabilities": { "accesso": 0.91, "fatturazione": 0.09 }
},
"tone": {
"type": "score",
"score": 1,
"confidence": 0.64,
"probabilities": { "sereno": 0.1, "neutro": 0.64, "irritato": 0.26 }
}
},
"latency_ms": 480,
"usage": { "input_tokens": 212 }
}
Ogni risposta riuscita conta come un'esecuzione agentic; le risposte con errore non consumano esecuzioni.
Errori specifici
| Codice | Significato |
|---|---|
422 | Corpo non valido, più di 8 domande, oppure una domanda con più opzioni del massimo consentito (predefinito 20). |
503 | Decisione non disponibile: {"code": "DECISION_UNAVAILABLE", "reason": "..."}. Fra i motivi: timeout, rate_limited, pii_unavailable (protezione dei dati personali non pronta), model_not_decision (il modello indicato non è decisionale), not_configured. |
Classificazione con il modello decisionale
Per un'organizzazione, l'intento e il segnale di passaggio all'operatore calcolati da POST /classify possono essere affidati al modello decisionale. L'impostazione, a cura del team Askme, prevede tre modalità: spenta (predefinita); in prova, in cui il modello decisionale viene interrogato e il confronto registrato nei costi, senza cambiare la risposta; automatica, in cui decide il modello decisionale quando la sua confidenza supera la soglia stabilita, altrimenti resta la classificazione attuale. In caso di errore del modello decisionale la classificazione torna sempre a quella attuale.
3. Conversazioni
Una conversazione appartiene a chi l'ha avviata: le conversazioni avviate con una chiave API appartengono a quella chiave (user_id vale apikey:<id>). Per leggere le conversazioni di altri utenti o di altre chiavi serve il permesso conversations.read_all; per rinominarle, chiuderle o eliminarle serve conversations.delete. Senza il permesso la risposta è 404, come per una conversazione inesistente.
GET /conversations
Elenca le conversazioni della chiave che effettua la richiesta.
| Campo | In | Tipo | Predefinito | Descrizione |
|---|---|---|---|---|
offset | query | int | 0 | Paginazione. |
limit | query | int (1–100) | 50 | Dimensione della pagina. |
exclude_source_app | query | string | — | Esclude le conversazioni attribuite all'applicazione indicata. |
Esempio di risposta (200 OK)
{
"items": [
{
"id": 2758,
"tenant_id": "5b0e2f7a-2c41-4d0e-9a6b-1c9e8f3d4a21",
"agent_id": 54,
"agent_name": "Assistente sito",
"user_id": "apikey:28",
"title": "Di cosa si occupa Lascaux?",
"status": "active",
"message_count": 2,
"metadata": null,
"external_id": "ticket-9281",
"source_app": "api_direct",
"agent_deleted": false,
"last_message_preview": "Di cosa si occupa Lascaux?",
"needs_review": false,
"handoff": false,
"insights": null,
"created_at": "2026-09-24T17:10:50.687393Z",
"updated_at": "2026-09-24T17:10:58.072295Z"
}
],
"total": 1,
"offset": 0,
"limit": 50
}
GET /conversations/monitoring
Tutte le conversazioni dell'organizzazione. Permesso: conversations.read_all.
| Campo | In | Tipo | Descrizione |
|---|---|---|---|
offset, limit | query | int | Paginazione (limit massimo 100). |
status | query | string | active, closed, archived. |
agent_id | query | int | Agente. |
date_from, date_to | query | datetime ISO 8601 | Intervallo di date. |
source_app | query | string | Solo le conversazioni attribuite all'applicazione indicata. |
exclude_source_app | query | string | Esclude l'applicazione indicata. |
needs_review | query | bool | Solo le conversazioni con risposte da rivedere. |
handoff | query | bool | Solo le conversazioni con passaggio all'operatore. |
GET /conversations/{conversation_id}
Dettaglio della conversazione, senza messaggi, nello stesso formato degli elementi di GET /conversations.
GET /conversations/by-external-id/{external_id}
Recupera la conversazione tramite l'identificativo inviato in external_conversation_id nel corpo di POST /chat. L'intestazione X-External-Id non entra in questa ricerca: serve solo all'attribuzione dei consumi.
| Campo | In | Tipo | Descrizione |
|---|---|---|---|
external_id | path | string | Identificativo esterno della conversazione. |
agent_id | query | int | Facoltativo: limita la ricerca a un agente, utile se lo stesso identificativo è stato usato con più agenti. |
curl "https://ai.askme.it/api/v2/conversations/by-external-id/ticket-9281" \
-H "X-Api-Key: amai_xxxxxxxxxxxxxxxx"
Per proseguire la conversazione è sufficiente inviare un nuovo POST /chat con lo stesso external_conversation_id (oppure con il conversation_id restituito). Risponde 404 se nessuna conversazione visibile alla chiave corrisponde all'identificativo.
GET /conversations/{conversation_id}/messages
Messaggi della conversazione, dal più vecchio.
| Campo | In | Tipo | Predefinito | Descrizione |
|---|---|---|---|---|
offset | query | int | 0 | Paginazione. |
limit | query | int (1–500) | 100 | Dimensione della pagina. |
Esempio di risposta (200 OK, estratto)
{
"items": [
{
"id": 5920,
"conversation_id": 2758,
"role": "user",
"content": "Di cosa si occupa Lascaux?",
"tokens_input": null,
"tokens_output": null,
"model_used": null,
"latency_ms": null,
"metadata": null,
"created_at": "2026-09-24T17:10:50.723217Z"
},
{
"id": 5921,
"conversation_id": 2758,
"role": "assistant",
"content": "Lascaux è un'impresa che offre servizi informatici [...]",
"tokens_input": 2415,
"tokens_output": 243,
"model_used": "gpt-5-mini-2025-08-07",
"latency_ms": 7333,
"metadata": {
"rag_used": true,
"confidence": 0.631,
"needs_review": false,
"handoff_requested": false,
"tool_calls_count": 0,
"tools_used": [],
"sources": [
{
"document_id": "100",
"knowledge_base_id": 10,
"chunk_id": "15",
"title": "Contatti - Lascaux",
"relevance_score": 0.029828,
"is_visible": true,
"source_type": "text",
"source_url": "https://www.lascaux.it/contatti",
"snippet": "Mettiti in contatto: troveremo insieme le risposte che cerchi [...]",
"passages": [
{ "chunk_id": "15", "snippet": "Mettiti in contatto: [...]", "relevance_score": 0.029828 },
{ "chunk_id": "14", "snippet": "Contatti. Telefono, la sede, scrivici [...]", "relevance_score": 0.029762 }
]
}
]
},
"created_at": "2026-09-24T17:10:58.072295Z"
}
],
"total": 2,
"offset": 0,
"limit": 100
}
Nei messaggi salvati le fonti sono in metadata.sources, con title, source_type, source_url e un estratto in snippet; citation_index è presente se la risposta è stata generata con supports_citations. Con una chiave API le fonti nascoste non compaiono.
PATCH /conversations/{conversation_id}/messages/{message_id}
Imposta o toglie il contrassegno di revisione su un messaggio. Corpo: { "needs_review": true }. Restituisce il messaggio aggiornato.
POST /conversations/{conversation_id}/messages/{message_id}/feedback
Registra una valutazione su una risposta dell'assistente.
| Campo | In | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|---|
rating | body | string | Sì | positive o negative (altri valori: 400). |
comment | body | string | No | Commento. |
{ "message_id": 5921, "rating": "positive", "comment": "Risposta chiara." }
POST /conversations/{conversation_id}/export
Scarica la conversazione come allegato. Parametro format: json (predefinito) oppure csv.
- JSON:
{ "conversation": {...}, "messages": [{ "id", "role", "content", "model_used", "confidence", "needs_review", "sources", "created_at" }], "exported_at": "..." }. - CSV: una riga per messaggio, con le colonne
timestamp,role,content,model_used,confidence,needs_review.
PUT /conversations/{conversation_id}
Aggiorna titolo o stato della conversazione (è accettato anche PATCH).
| Campo | In | Tipo | Descrizione |
|---|---|---|---|
title | body | string (max 500) | Nuovo titolo. |
status | body | string | active, closed o archived. |
DELETE /conversations/{conversation_id}
Elimina la conversazione e i suoi messaggi. Risposta 204 No Content.
4. Traduzione
Traduzione di testi con il servizio di traduzione configurato per l'installazione. Gli endpoint richiedono che la funzionalità translation sia attiva per l'organizzazione (altrimenti 403 con FEATURE_FLAG_DISABLED) e il permesso translation.use. Le traduzioni consumano i crediti AI dell'organizzazione; i testi già tradotti vengono riutilizzati da una cache.
POST /translation/translate
| Campo | In | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|---|
text | body | string (1–32000) | Sì | Testo da tradurre. |
target_lang | body | string (2–10) | Sì | Lingua di destinazione, codice ISO 639-1 (per esempio en, it, de). |
source_lang | body | string (2–10) | No | Lingua di origine. Se omessa viene rilevata automaticamente. |
curl -X POST "https://ai.askme.it/api/v2/translation/translate" \
-H "X-Api-Key: amai_xxxxxxxxxxxxxxxx" \
-H "X-Source-App: api_direct" \
-H "Content-Type: application/json" \
-d '{
"text": "Le spedizioni standard partono entro 24 ore lavorative.",
"target_lang": "en"
}'
Esempio di risposta (200 OK)
{
"translated_text": "Standard shipments start within 24 working hours.",
"detected_source_lang": "IT",
"provider": "openai"
}
POST /translation/translate-batch
Traduce più testi in una sola richiesta, nella stessa lingua di destinazione. Ogni elemento ha un id scelto dal chiamante, restituito insieme alla traduzione.
| Campo | In | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|---|
items | body | array | Sì | [{ "id": string (1–200), "text": string (1–32000) }], almeno un elemento. |
target_lang | body | string | Sì | Lingua di destinazione. |
source_lang | body | string | No | Lingua di origine. |
Una richiesta può contenere al massimo 200 elementi e 100.000 caratteri complessivi (valori predefiniti dell'installazione); oltre questi limiti la risposta è 422.
{
"items": [
{ "id": "titolo", "text": "Politica di reso" },
{ "id": "testo", "text": "Il reso è gratuito entro 30 giorni dalla consegna." }
],
"source_lang": "it",
"target_lang": "en"
}
Esempio di risposta (200 OK)
{
"items": [
{ "id": "titolo", "translated_text": "Return policy" },
{ "id": "testo", "translated_text": "Returns are free within 30 days of delivery." }
],
"provider": "openai"
}
Errori specifici
| Codice | Significato |
|---|---|
400 | Lingua non gestita dal servizio di traduzione. |
403 | Funzionalità translation non attiva (FEATURE_FLAG_DISABLED) oppure permesso translation.use non coperto dalla chiave (PERMISSION_DENIED). |
422 | Validazione non superata oppure troppi elementi o caratteri nella richiesta batch. |
429 | Quota di traduzione esaurita. |
502 | Errore del servizio di traduzione. |
5. Connettori MCP
Connettori MCP (Model Context Protocol) registrati nell'organizzazione e associabili agli agenti. Permessi: admin.integrations.read per la lettura, admin.integrations.manage per le modifiche.
POST /mcp-servers
Registra un connettore.
| Campo | In | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|---|
name | body | string (1–255) | Sì | Nome del connettore. |
description | body | string | No | Descrizione. |
transport | body | string | Sì | streamable-http, sse oppure stdio. |
url | body | string (max 500) | Per sse e streamable-http | Indirizzo del server. |
command, args | body | string, string[] | Per stdio | Comando e argomenti. |
env | body | object (string→string) | No | Intestazioni (per esempio Authorization) o variabili d'ambiente. I valori non vengono mai restituiti: le letture riportano "***". |
protocol_mode | body | string | No | auto (predefinito), stateless oppure session: modalità del protocollo; con auto viene rilevata al primo collegamento. |
curl -X POST "https://ai.askme.it/api/v2/mcp-servers" \
-H "X-Api-Key: amai_xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"name": "Calendario",
"description": "Connettore per la gestione del calendario",
"transport": "streamable-http",
"url": "https://mcp.example.com/calendar",
"env": { "Authorization": "Bearer <token>" }
}'
Esempio di risposta (201 Created)
{
"id": 12,
"tenant_id": "5b0e2f7a-2c41-4d0e-9a6b-1c9e8f3d4a21",
"name": "Calendario",
"description": "Connettore per la gestione del calendario",
"transport": "streamable-http",
"command": null,
"args": null,
"url": "https://mcp.example.com/calendar",
"env": { "Authorization": "***" },
"status": "unknown",
"protocol_mode": "auto",
"detected_protocol": null,
"tools_count": 0,
"tools_cache": null,
"linked_agents_count": null,
"last_health_check": null,
"created_at": "2026-09-24T10:50:00Z",
"updated_at": "2026-09-24T10:50:00Z"
}
Altri endpoint dei connettori
| Endpoint | Metodo | Descrizione |
|---|---|---|
/mcp-servers | GET | Elenco dei connettori (search, status, transport, offset, limit), con linked_agents_count. |
/mcp-servers/{server_id} | GET, PATCH, DELETE | Dettaglio, modifica (campi facoltativi; inviando di nuovo "***" in env il valore salvato resta invariato), eliminazione (204). |
/mcp-servers/{server_id}/tools | GET | Strumenti esposti dal connettore, con input_schema. |
/mcp-servers/{server_id}/tools/{tool_name} | PATCH | Abilita o disabilita uno strumento e ne imposta l'approvazione: { "enabled": false }, { "requires_approval": true }. I campi omessi restano invariati. |
/mcp-servers/{server_id}/refresh-tools | POST | Rilegge l'elenco degli strumenti dal connettore. |
/mcp-servers/{server_id}/test | POST | Verifica il collegamento: { "success": bool, "message": string }. |
/mcp-servers/{server_id}/calls | GET | Registro delle chiamate agli strumenti: tool_name, outcome, agent_id, conversation_id, duration_ms, error_message. Filtri outcome, tool_name, ordinamento sort_by/order, paginazione. |
/mcp-servers/health | GET | Ultimo stato noto di tutti i connettori, senza nuove verifiche. |
/mcp-servers/health/refresh | POST | Avvia in background la verifica di tutti i connettori (202, { "scheduled": n, "status": "scheduled" }); lo stato aggiornato si legge poi con GET /mcp-servers/health. |
/mcp-servers/{server_id}/health/refresh | POST | Verifica immediata di un singolo connettore. |
6. Valutazioni e test A/B
Endpoint per misurare la qualità degli agenti su insiemi di domande e confrontare varianti di configurazione. Richiedono le funzionalità corrispondenti del piano.
| Endpoint | Metodo | Descrizione |
|---|---|---|
/evaluations/datasets | GET, POST | Elenco e creazione dei dataset di valutazione. |
/evaluations/datasets/{dataset_id} | GET, PATCH, DELETE | Dettaglio, modifica, eliminazione di un dataset. |
/evaluations/run | POST | Avvia una valutazione di un agente su un dataset. |
/evaluations/runs | GET | Elenco delle esecuzioni. |
/evaluations/runs/{run_id} | GET, DELETE | Risultato di un'esecuzione ed eliminazione. |
/ab-experiments | GET, POST | Elenco e creazione degli esperimenti A/B tra varianti di un agente. |
/ab-experiments/{experiment_id} | GET, DELETE | Dettaglio ed eliminazione. |
/ab-experiments/{experiment_id}/run | POST | Avvia l'esperimento. |
/ab-experiments/{experiment_id}/cancel | POST | Interrompe l'esperimento. |
/ab-experiments/{experiment_id}/results | GET | Risultati aggregati. |
/ab-experiments/{experiment_id}/promote/{variant} | POST | Applica all'agente la configurazione della variante scelta. |
7. Template di prompt
Template di prompt riutilizzabili e versionati.
POST /prompt-templates
| Campo | In | Tipo | Obbligatorio | Predefinito | Descrizione |
|---|---|---|---|---|---|
name | body | string (1–255) | Sì | — | Nome del template. |
content | body | string | Sì | — | Testo del prompt; può contenere variabili {{variabile}}. |
role | body | string | No | system | Ruolo del messaggio: system, user, assistant. |
description | body | string | No | — | Descrizione. |
variables | body | array | No | [] | [{ "name", "description", "default_value" }]. |
parent_id | body | int | No | — | Template genitore. |
tags | body | string[] | No | [] | Etichette libere. |
status | body | string | No | draft | draft, published, archived. |
Esempio di risposta (201 Created)
{
"id": 55,
"tenant_id": "5b0e2f7a-2c41-4d0e-9a6b-1c9e8f3d4a21",
"name": "Saluto cliente",
"description": "Apertura standard delle conversazioni di supporto",
"content": "Buongiorno {{nome_cliente}}, sono l'assistente di {{azienda}}.",
"role": "system",
"variables": [
{ "name": "nome_cliente", "description": "Nome del cliente", "default_value": "" },
{ "name": "azienda", "description": "Nome dell'azienda", "default_value": "Askme" }
],
"parent_id": null,
"tags": ["benvenuto", "supporto"],
"status": "draft",
"version": 1,
"created_by": "apikey:28",
"created_at": "2026-09-24T12:00:00Z",
"updated_at": "2026-09-24T12:00:00Z"
}
Altri endpoint dei template
| Endpoint | Metodo | Descrizione |
|---|---|---|
/prompt-templates | GET | Elenco, con filtri parent_id, status, search, root_only e paginazione (limit massimo 200). |
/prompt-templates/{template_id} | GET, PUT, DELETE | Dettaglio, modifica (incrementa version), eliminazione (204). |
/prompt-templates/{template_id}/versions | GET | Versioni salvate. |
/prompt-templates/{template_id}/rollback/{version_number} | POST | Ripristina una versione precedente. |
/prompt-templates/{template_id}/children | GET | Template figli. |
/prompt-templates/{template_id}/render | POST | Restituisce il testo con le variabili sostituite dai valori inviati. |