Conversazioni e messaggi

Avvia chat, invia messaggi di follow-up, ricevi le risposte in streaming su WebSocket, confronta modelli, approva le chiamate agli strumenti MCP e condividi conversazioni tramite l'API REST.

23 min di lettura · Aggiornato il 28 settembre 2026

Una conversazione è un thread di chat: la stessa cosa che il tuo team vede in Chat History nell’app. La avvii con un messaggio, il modello risponde e tu continui con altri messaggi di follow-up. Ogni richiesta descritta qui viene eseguita come l’utente proprietario della chiave API, con gli stessi accessi, le stesse quote e le stesse regole del workspace che quell’utente ha nel browser.

Tutti i percorsi sono relativi a https://api.stickyprompts.com/api/v1. I corpi delle richieste sono in JSON. I file si caricano prima tramite gli endpoint dei file e poi si richiamano tramite il loro ID.

Come funziona la chat

  1. Avvia la conversazione con POST /conversation (attende la risposta completa) o con POST /conversation/stream (risponde subito; la risposta del modello arriva sul WebSocket). Entrambi creano la conversazione e inviano il primo messaggio in un’unica chiamata.
  2. Continua con POST /conversation/:id o POST /conversation/:id/stream.
  3. Cambia modello, stile o livello di ragionamento per i turni successivi con PATCH /conversation/:id/model. I corpi dei messaggi di follow-up non accettano un modello: la conversazione ricorda le impostazioni del “prossimo messaggio”.
  4. Leggi la cronologia con GET /conversation/:id (paginata, dal più recente) ed elenca le conversazioni con GET /conversation.
  5. Segui l’avanzamento in tempo reale con il WebSocket GET /conversation/:id/ws, oppure interroga periodicamente GET /conversation/:id e leggi lo status del messaggio dell’IA.

Identificatori. La maggior parte degli endpoint accetta come :id sia l’ID numerico della conversazione sia lo slug di 8 caratteri. Gli endpoint di condivisione, l’eliminazione, l’approvazione e gli stream WebSocket e SSE accettano solo l’ID numerico; ognuno di questi casi è segnalato più avanti.

L’oggetto Message

Viene restituito dagli endpoint di invio senza streaming, all’interno di GET /conversation/:id e come data degli eventi WebSocket message.

{
  "ID": 90412,
  "CreatedAt": "2026-09-28T09:14:03.512Z",
  "UpdatedAt": "2026-09-28T09:14:09.871Z",
  "DeletedAt": null,
  "conversation_context_id": 5120,
  "ai_model_id": 12,
  "ai_model": { "ID": 12, "name": "Example Model", "stream_support": true, "...": "..." },
  "agent_id": null,
  "is_auto_model": false,
  "text": "Here are three subject lines for the Northwind Q4 newsletter: ...",
  "raw_text": "Here are three subject lines for the Northwind Q4 newsletter: ...",
  "parts": [
    { "id": "p1", "type": "text", "static": true, "text_part": { "text": "Here are three ...", "raw_text": "Here are three ..." } }
  ],
  "thinking": null,
  "isUser": false,
  "error_text": null,
  "error_id": null,
  "error_code": null,
  "mcp_approval_requests": null,
  "ai_style": "prompty",
  "status": "finished",
  "citations": null,
  "response_duration_ms": 6210,
  "type": "normal",
  "attachments": []
}
CampoTipoSignificato
conversation_context_iduint o nullLa conversazione a cui appartiene il messaggio.
ai_model_id, ai_modeluint, oggettoIl modello che ha prodotto il messaggio (IA) o che era selezionato per il messaggio (utente).
agent_id, agentuint, oggettoL’agente che ha risposto, se presente.
is_auto_modelbooltrue quando il modello è stato scelto dalla selezione automatica.
prompt_iduint o nullIl prompt salvato usato per questo messaggio dell’utente.
textstringIl testo del messaggio. Nei messaggi dell’IA i marcatori delle citazioni sono già renderizzati.
raw_textstringIl testo dell’IA non elaborato (solo nei percorsi di lettura e di streaming).
partsarrayBlocchi strutturati. type vale text, thinking, web_search, web_fetch, code_execution, image_generation o mcp_call, con l’oggetto *_part corrispondente. static è true quando non c’è nulla da espandere.
thinkingstring o nullIl testo del ragionamento, per i modelli di ragionamento.
isUserbooltrue per i messaggi dell’utente, false per quelli dell’IA.
statusstringthinking, generating o finished.
error_text, error_id, error_codestring o nullValorizzati quando la generazione non è riuscita. error_id è un riferimento di 10 caratteri da comunicare all’assistenza; error_code è un codice leggibile dalle macchine per alcune classi di errore, come i blocchi per dati sensibili e gli errori MCP.
mcp_approval_requestsarrayApprovazioni di strumenti MCP in attesa o già decise. Vedi Approvazione degli strumenti MCP.
toolsarrayGli strumenti attivati per questo turno.
citationsarrayCitazioni dal web e dalle knowledge base: url, file_path, title, info (kb_id, file_id, intervalli di pagine e di righe), citation_index e altro.
knowledge_base_ids, world_knowledgeuint[], boolLe knowledge base consultate in questo turno e se il modello poteva andare oltre il loro contenuto.
response_duration_msint o nullIl tempo di generazione.
typestringnormal o sub_conversation (il contenitore di un confronto tra modelli).
attachmentsarrayFile allegati e trascrizioni, ciascuno con una chiave iniziale "type": "file" o "type": "transcription". È sempre un array.

Stato. thinking significa che la riga del messaggio dell’IA esiste ma non è ancora stato generato nulla; generating significa che l’output parziale sta arrivando in streaming; finished significa che la generazione è terminata. Anche un messaggio dell’IA non riuscito è finished, con error_text ed error_id valorizzati: controlla quindi questi campi, non solo status. I messaggi dell’utente sono sempre finished.

Crediti. Ogni campo credits_used è un decimale serializzato come stringa JSON, per esempio "0.4213".

Inviare messaggi

Corpo della richiesta

POST /conversation e POST /conversation/stream accettano tutti i campi seguenti. Gli endpoint di follow-up accettano solo i campi contrassegnati con «follow-up»; gli altri vengono ignorati senza alcun avviso. Il binder non rende obbligatorio nessun campo, ma invia sempre un message e un modello.

Corpo
message string facoltativo
Il messaggio dell’utente. Menziona @<agent-tag> per indirizzare il turno a uno dei tuoi agenti. Avvio e follow-up.
attachments array facoltativo
Elementi { "id": uint, "type": "file" | "transcription", "temp": bool }. File caricati o trascrizioni completate. Con temp: true l’allegato vale solo per questo turno e non viene registrato sul messaggio. Avvio e follow-up.
knowledge_base_ids uint[] facoltativo
Le knowledge base da cui recuperare il contesto per questo turno. Avvio e follow-up.
knowledge_base_limit_node_ids uint[] facoltativo
Limita il recupero a queste cartelle o a questi file della knowledge base. Avvio e follow-up.
world_knowledge bool facoltativo predefinito false
false: risponde solo in base alla knowledge base. true: la knowledge base resta la fonte principale, ma il modello può usare anche la ricerca web e le proprie conoscenze. Avvio e follow-up.
flash_mode bool facoltativo
Richiede il livello di servizio prioritario del provider. Ha effetto solo sui modelli che lo supportano. Avvio e follow-up.
tools array facoltativo
Elementi { "name", "use", "mcp_tool_id"? }. Vedi Strumenti. Avvio e follow-up.
ai_model_id uint facoltativo
Il modello da usare, preso da GET /ai_model. Viene ignorato se è impostato auto_model_type. Solo all’avvio.
auto_model_type string facoltativo
best, balanced o cheapest. La selezione automatica sceglie il modello migliore per ogni messaggio e ha la precedenza su ai_model_id. Solo all’avvio.
ai_style string facoltativo predefinito prompty
Lo stile di risposta: il valore tone di uno stile dell’IA. Solo all’avvio.
reasoning_effort string facoltativo
Il livello di ragionamento per i modelli di ragionamento. Un valore non supportato ricade sul valore predefinito del modello; per i modelli senza ragionamento viene salvato null. Solo all’avvio.
agent_id uint facoltativo
Avvia una chat con un agente: a ogni turno vengono usati il modello, il prompt di sistema, gli strumenti, i server MCP, le knowledge base e i file dell’agente. L’agente deve essere tuo. Solo all’avvio.
invited_agent_ids uint[] facoltativo
I tuoi agenti che si possono menzionare in questa conversazione (@mentioned). Viene ignorato se è impostato agent_id. Solo all’avvio.
team_id uint facoltativo
Crea una conversazione di team (il team riceve l’accesso chat_contribute). Vedi Quando risponde l’IA. Solo all’avvio.
X-Project-ID integer in header facoltativo
Solo all’avvio. Archivia la conversazione in questo progetto. Se il corpo non contiene un modello, viene usato il modello predefinito del progetto. A ogni turno i file del progetto vengono aggiunti al contesto. Il campo project_id del corpo esiste ma non viene letto: usa questo header.

Selezione del modello. Vince auto_model_type, poi ai_model_id, poi il modello predefinito del progetto (con X-Project-ID). Se nessuno dei tre si applica, la conversazione viene comunque creata, ma l’invio non riesce con 503 {"error":"no AI model is selected, try reselecting model"}: all’avvio invia quindi sempre un modello o un tipo di selezione automatica. Non esiste un ID di modello letterale "auto". Le chat con un agente e le menzioni @agent usano il modello dell’agente (stile prompty, nessun livello di ragionamento). Il modello deve essere consentito dalle regole di Accesso a modelli e provider e di Residenza dei dati del tuo workspace, altrimenti l’invio non riesce con 503.

Strumenti

Ogni voce ha la forma { "name": ..., "use": true | false }. Nomi disponibili: web_search, image_generation, video_generation, speech_generation, music_generation, remote_mcp.

  • Uno strumento arriva al modello solo se il modello lo elenca tra le proprie capacità tools. In caso contrario viene ignorato senza alcun avviso.
  • remote_mcp richiede anche "mcp_tool_id": <id>, l’ID di uno dei server MCP remoti che hai configurato. Aggiungi una voce per ogni server.
  • Selezione automatica degli strumenti. Sui modelli che supportano gli strumenti, un piccolo classificatore può attivare web_search e image_generation (e l’esecuzione di codice) in base al messaggio, anche se non li hai richiesti. Se invii una voce per uno strumento, anche {"name":"web_search","use":false}, il classificatore non lo aggiunge più. L’esecuzione di codice dipende solo dal classificatore.

POST /conversation

POST /api/v1/conversation
Autorizzazione conversations_manage Strumento MCP send_message

Crea una conversazione, invia il primo messaggio e attende la risposta completa dell’IA, comprese eventuali chiamate agli strumenti. La risposta è il Message dell’IA ormai completato; l’ID della nuova conversazione è il suo conversation_context_id. Dopo il primo scambio la conversazione riceve un nome automatico. Accetta il corpo della richiesta descritto sopra, più l’header facoltativo X-Project-ID.

curl -X POST https://api.stickyprompts.com/api/v1/conversation \
  -H "Authorization: Bearer $STICKY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Draft three subject lines for the Northwind Q4 newsletter.",
    "ai_model_id": 12,
    "ai_style": "prompty",
    "tools": [{ "name": "web_search", "use": false }]
  }'
import os, requests

r = requests.post(
    "https://api.stickyprompts.com/api/v1/conversation",
    headers={"Authorization": f"Bearer {os.environ['STICKY_API_KEY']}"},
    json={
        "message": "Draft three subject lines for the Northwind Q4 newsletter.",
        "ai_model_id": 12,
        "ai_style": "prompty",
        "tools": [{"name": "web_search", "use": False}],
    },
    timeout=300,
)
r.raise_for_status()
reply = r.json()
if reply.get("error_id"):
    print("Failed:", reply["error_text"], reply["error_id"])
else:
    print(reply["conversation_context_id"], reply["text"])
const res = await fetch("https://api.stickyprompts.com/api/v1/conversation", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.STICKY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    message: "Draft three subject lines for the Northwind Q4 newsletter.",
    ai_model_id: 12,
    ai_style: "prompty",
    tools: [{ name: "web_search", use: false }],
  }),
});
const reply = await res.json();
console.log(reply.conversation_context_id, reply.error_id ?? reply.text);

Risposta. 200 con il Message dell’IA (vedi l’oggetto Message). Quando l’IA non deve rispondere (una conversazione di team con più membri e nessuna menzione di @ai o di un agente), il corpo è invece 200 {"conversation_context_id": 5120}.

Errori

StatoCorpoQuando
400{"error": "..."}JSON non valido.
400{"error": "...", "error_code": "..."}Bloccato dalle regole sui dati sensibili del tuo workspace, oppure errore di un server MCP. Quando l’IA non risponde, un blocco per dati sensibili usa la chiave code al posto di error_code.
400{"error": "Knowledge base not found"}Knowledge base sconosciuta.
429{"error": "Quota exceeded", "reason": "..."}Verificato prima di creare qualsiasi cosa. reason vale credit_count, user_count, subscription, trial_expired o unknown.
503{"error": "could not create conversation"}Per esempio un agent_id che non è tuo o un team_id sconosciuto (un errore nella ricerca del team può emergere anche come 500).
503{"error": "<message>"}Qualsiasi altro errore: nessun modello selezionato, modello soggetto a restrizioni, errore del provider.

Se il provider fallisce a generazione in corso, il messaggio dell’IA viene comunque salvato con error_text ed error_id, e POST /conversation/:id/retry può eseguirlo di nuovo.

POST /conversation/stream

POST /api/v1/conversation/stream
Autorizzazione conversations_manage

Stessi header e stesso corpo di POST /conversation, ma risponde non appena il messaggio dell’utente è stato salvato. La generazione prosegue in background e la risposta arriva sul WebSocket oppure tramite polling.

{ "conversation_context_id": 5120 }

Gli errori rilevati prima che il messaggio dell’utente venga salvato (quota 429, agente non valido, nessun modello selezionato, modello soggetto a restrizioni, blocco per dati sensibili) vengono comunque restituiti in modo sincrono, con le stesse forme viste sopra. Gli errori successivi al 200 (selezione del modello o degli strumenti, recupero dalla knowledge base, errore del provider) vengono segnalati solo come evento WebSocket error_message e salvati su un messaggio dell’IA con error_text ed error_id.

Ricevere la risposta

  • WebSocket (consigliato). Connettiti a GET /conversation/{conversation_context_id}/ws. Gli eventi emessi prima della connessione vengono messi in buffer e riprodotti al primo client, quindi se ti connetti subito dopo il 200 non perdi nulla.
  • Polling. Chiama GET /conversation/:id?page=1&size=2 e leggi il messaggio dell’IA più recente (isUser: false, il primo dell’elenco, perché l’ordine va dal più recente). Il messaggio dell’IA viene creato con status: "thinking" e salvato a ogni chunk, quindi vedrai generating con un text sempre più lungo e poi finished. Fermati a finished e controlla error_id. Subito dopo il 200 il messaggio dell’IA potrebbe non esistere ancora: se il messaggio più recente è ancora il tuo, la generazione non è iniziata.

Lo streaming avviene in tempo reale solo per i modelli con stream_support: true. Con gli altri modelli la risposta compare tutta insieme quando è completa.

POST /conversation/:id

POST /api/v1/conversation/:id
Autorizzazione conversations_manage Strumento MCP send_message

Invia un messaggio di follow-up e attende la risposta dell’IA. Modello, stile e livello di ragionamento provengono dalle impostazioni next_message_* della conversazione; per cambiarli, usa prima PATCH /conversation/:id/model.

Parametri di percorso
id string in path obbligatorio
ID o slug della conversazione.
Corpo
message string facoltativo
Il testo del messaggio di follow-up.
attachments array facoltativo
Come nel corpo della richiesta.
knowledge_base_ids uint[] facoltativo
Le knowledge base per questo turno.
knowledge_base_limit_node_ids uint[] facoltativo
Limita il recupero a questi nodi.
world_knowledge bool facoltativo
Consente conoscenze che vanno oltre la knowledge base.
flash_mode bool facoltativo
Livello di servizio prioritario, dove supportato.
tools array facoltativo
Gli strumenti per questo turno.
curl -X POST https://api.stickyprompts.com/api/v1/conversation/5120 \
  -H "Authorization: Bearer $STICKY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "message": "Make the second one shorter." }'

Risposta. Come per POST /conversation: 200 con il Message dell’IA, oppure 200 {"conversation_context_id": ...} quando l’IA non risponde. Se la conversazione ha un confronto tra modelli attivo, il messaggio va in parallelo a ogni thread e la risposta è un array JSON di Message dell’IA, uno per thread.

Errori. Come per POST /conversation, più 404 {"error":"Conversation not found"}. Possono inviare messaggi solo il proprietario e gli utenti o i team con chat_contribute; chiunque altro riceve 503 {"error":"you are not allowed to send messages in this conversation"}.

POST /conversation/:id/stream

POST /api/v1/conversation/:id/stream
Autorizzazione conversations_manage

Come POST /conversation/:id, ma restituisce subito 200 {"conversation_context_id": <id>}. Segui l’avanzamento sul WebSocket o tramite polling, come per POST /conversation/stream. Con un confronto tra modelli attivo, ogni thread trasmette in streaming sul WebSocket della conversazione padre.

Parametri di percorso
id string in path obbligatorio
ID o slug della conversazione.

Quando risponde l’IA

L’IA risponde a un messaggio quando vale almeno una di queste condizioni:

  • un solo utente ha accesso in scrittura alla conversazione (il caso normale, che comprende ogni conversazione creata con una chiave API e senza team_id);
  • il testo contiene @ai (con distinzione tra maiuscole e minuscole);
  • è una chat con un agente;
  • il testo menziona (@mentions) il tag di un agente invitato nella conversazione.

In tutti gli altri casi il messaggio viene salvato come semplice messaggio dell’utente, visibile agli altri partecipanti, e l’endpoint restituisce 200 {"conversation_context_id": <id>} senza chiamare alcun modello, anche negli endpoint senza streaming.

Agenti in una conversazione

  • Un agent_id indicato alla creazione rende la conversazione una chat con un agente. Il suo modello non può essere cambiato in seguito.
  • In una conversazione con un solo utente, @tag indirizza quel turno al tuo agente con quel tag, oppure a un agente condiviso con te (se il tag coincide, prevalgono i tuoi agenti). In una conversazione con più utenti si possono menzionare solo gli agenti invitati con invited_agent_ids o con POST /conversation/:id/agents.
  • L’agente che risponde viene registrato sul messaggio dell’IA (agent_id, agent).

Streaming

GET /conversation/:id/ws

GET /api/v1/conversation/:id/ws
Autorizzazione conversations_get

Un WebSocket che invia ogni aggiornamento della conversazione. Connettiti a wss://api.stickyprompts.com/api/v1/conversation/5120/ws e invia l’header Authorization: Bearer STICKY-API-... nella richiesta di upgrade. Gli oggetti WebSocket del browser non possono impostare header, quindi questo endpoint è pensato per i client lato server. Il server ignora tutto ciò che invii: legge solo per accorgersi della chiusura.

Parametri di percorso
id integer in path obbligatorio
L’ID numerico della conversazione. Con uno slug ti connetti a uno stream diverso e vuoto.

Ogni frame è un frame di testo JSON {"type": <string>, "data": <payload>}:

typedataQuando
messageMessage (snapshot completo)Un nuovo messaggio di un altro partecipante, e ogni aggiornamento di un messaggio dell’IA durante la generazione. text, parts e thinking contengono il contenuto accumulato finora, non un delta: sostituisci la tua copia in base a data.ID. status passa da thinking a generating e infine a finished.
ai_message_endnessuno (chiave assente)La generazione è terminata, con o senza successo. Chiude il turno.
error_messageMessage con error_text, error_id, error_code facoltativo, status: "finished"La generazione o la sua preparazione non è riuscita. Consideralo un evento finale: dopo gli errori di preparazione di solito segue un ai_message_end, ma non è garantito dopo gli errori del provider a generazione in corso.
ai_model_change{ "ai_model_id", "ai_style", "auto_model_type", "reasoning_effort" }Qualcuno ha chiamato PATCH /conversation/:id/model.
run_dataOggetto RunHistorySolo per i turni avviati dall’esecuzione di un prompt salvato, insieme agli eventi message. Vedi Prompt ed esecuzioni.
{"type":"message","data":{"ID":90412,"isUser":false,"status":"generating","text":"Here are","...":"..."}}
{"type":"message","data":{"ID":90412,"isUser":false,"status":"generating","text":"Here are three options","...":"..."}}
{"type":"message","data":{"ID":90412,"isUser":false,"status":"finished","text":"Here are three options for ...","response_duration_ms":6210,"...":"..."}}
{"type":"ai_message_end"}
  • I messaggi che invii tu non vengono rimandati sulla tua connessione. La chiave agisce come il suo proprietario, quindi un client che la usa vede solo i messaggi dell’IA e quelli delle altre persone.
  • Finché nessun client è connesso, gli eventi vengono messi in buffer, riprodotti alla connessione successiva e poi cancellati. Un client che si connette molto tempo dopo un turno può ricevere per primi gli eventi ormai superati di quel turno: ignora i messaggi il cui ID hai già in stato finale.
  • Gli stream risiedono nella memoria del server API. Se ti connetti e non ricevi nulla per un turno che sai essere in corso, ripiega sul polling di GET /conversation/:id.
# 1. Start the turn without waiting
curl -X POST https://api.stickyprompts.com/api/v1/conversation/stream \
  -H "Authorization: Bearer $STICKY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "message": "Summarise last week for the Northwind team.", "auto_model_type": "balanced" }'
# -> {"conversation_context_id":5120}

# 2. cURL cannot hold a WebSocket open; poll the newest messages instead
curl "https://api.stickyprompts.com/api/v1/conversation/5120?page=1&size=2" \
  -H "Authorization: Bearer $STICKY_API_KEY"
import asyncio, json, os, requests, websockets

KEY = os.environ["STICKY_API_KEY"]
BASE = "https://api.stickyprompts.com/api/v1"

start = requests.post(
    f"{BASE}/conversation/stream",
    headers={"Authorization": f"Bearer {KEY}"},
    json={"message": "Summarise last week for the Northwind team.", "auto_model_type": "balanced"},
).json()
cid = start["conversation_context_id"]

async def listen():
    url = f"wss://api.stickyprompts.com/api/v1/conversation/{cid}/ws"
    async with websockets.connect(url, additional_headers={"Authorization": f"Bearer {KEY}"}) as ws:
        async for frame in ws:
            event = json.loads(frame)
            if event["type"] == "message" and not event["data"]["isUser"]:
                print(event["data"]["status"], len(event["data"]["text"]))
            elif event["type"] == "error_message":
                print("Failed:", event["data"]["error_text"], event["data"]["error_id"])
                break
            elif event["type"] == "ai_message_end":
                break

asyncio.run(listen())
import WebSocket from "ws"; // Node: browsers cannot send the Authorization header

const KEY = process.env.STICKY_API_KEY;
const BASE = "https://api.stickyprompts.com/api/v1";

const { conversation_context_id: cid } = await fetch(`${BASE}/conversation/stream`, {
  method: "POST",
  headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({ message: "Summarise last week for the Northwind team.", auto_model_type: "balanced" }),
}).then((r) => r.json());

const ws = new WebSocket(`wss://api.stickyprompts.com/api/v1/conversation/${cid}/ws`, {
  headers: { Authorization: `Bearer ${KEY}` },
});
ws.on("message", (raw) => {
  const { type, data } = JSON.parse(raw);
  if (type === "message" && !data.isUser) console.log(data.status, data.text.length);
  if (type === "error_message") { console.error(data.error_text, data.error_id); ws.close(); }
  if (type === "ai_message_end") ws.close();
});

GET /conversation/:id/stream

GET /api/v1/conversation/:id/stream
Autorizzazione conversations_get

Un endpoint Server-Sent Events legacy. Usa invece il WebSocket o il polling. Nel codice attuale il percorso di invio delle conversazioni scrive solo sul WebSocket, quindi questo stream fa emergere solo eventi error_message e run_data per gli errori delle esecuzioni dei prompt; per il resto rimane aperto senza inviare il contenuto dei messaggi.

Parametri di percorso
id integer in path obbligatorio
ID numerico della conversazione.

Restituisce 404 {"error":"Stream not found"} quando il server non ha in memoria alcuno stream per la conversazione. Altrimenti risponde con Content-Type: text/event-stream e frame event:<name>\ndata:<json>\n\n, dove <name> è message, error_message (array di {run_id, error_id, error_text, model_name}), run_data (array di RunHistory) o close (data: Stream closed).

Leggere le conversazioni

GET /conversation/:id

GET /api/v1/conversation/:id
Autorizzazione conversations_get Strumento MCP get_conversation

Restituisce una conversazione con una pagina dei suoi messaggi, dal più recente. I metadati di paginazione si trovano negli header di risposta X-Total-Count (numero totale di messaggi), X-Page e X-Page-Size.

Parametri di percorso
id string in path obbligatorio
ID o slug della conversazione, oppure il suo slug di condivisione pubblica.
Parametri di query
page integer in query facoltativo predefinito 1
Numero di pagina.
size integer in query facoltativo predefinito 10
Messaggi per pagina.

Accesso. Il proprietario; gli utenti con chat_view o chat_contribute ottenuti tramite condivisione; oppure, quando :id è lo slug pubblico di una conversazione condivisa, chiunque sia ammesso dal tipo di condivisione (public: chiunque, internal: qualsiasi utente che ha effettuato l’accesso, workspace: i membri del workspace del proprietario).

{
  "credits_used": "0.4213",
  "conversation_context_id": 5120,
  "conversation_context_name": "Q4 newsletter subject lines",
  "conversation_context_slug": "aB3dE5gH",
  "expensive_warning_threshold": 1000,
  "public_slug": null,
  "share_type": "",
  "is_public": false,
  "user_name": "John Smith",
  "messages": [
    { "ID": 90412, "isUser": false, "status": "finished", "text": "Here are three options ...", "...": "..." },
    { "ID": 90411, "isUser": true, "status": "finished", "text": "Draft three subject lines ...", "...": "..." }
  ],
  "next_message_ai_model_id": 12,
  "next_message_ai_model_style": "prompty",
  "next_message_auto_model_type": null,
  "next_message_reasoning_effort": null,
  "has_sub_conversations": false,
  "unsupported_files": null,
  "current_user_permission": "chat_contribute",
  "owner_id": 77,
  "user_count": 1,
  "team_shares": null,
  "agent_id": null,
  "invited_agents": [],
  "is_agent_chat": false
}
CampoSignificato
credits_usedI crediti dell’intera conversazione, compresi i thread di confronto (stringa decimale).
messagesQuesta pagina di messaggi, dal più recente. Nei messaggi dell’IA le citazioni sono renderizzate in text e l’originale si trova in raw_text.
next_message_ai_model_id, next_message_ai_modelIl modello che userà il prossimo turno. In sua assenza vale il modello dell’ultimo messaggio dell’IA.
next_message_ai_model_styleLo stile del prossimo turno. Nota che la chiave è diversa da next_message_ai_style dell’oggetto conversazione.
next_message_auto_model_typebest, balanced o cheapest quando è attiva la selezione automatica.
forked_from_conversation_id, _slug, _nameValorizzati nei fork.
has_sub_conversationsEsiste un confronto tra modelli.
unsupported_filesFile allegati in precedenza che il prossimo modello non è in grado di leggere.
current_user_permissionchat_contribute per il proprietario, altrimenti il tuo livello di accesso più alto: chat_view, chat_contribute o none.
team_shares{team_id, team, access_type} per ogni team con cui la conversazione è condivisa.

Errori. 403 {"error":"You are not allowed to access this conversation"}, 404 {"error":"Conversation not found"}.

GET /conversation

GET /api/v1/conversation
Autorizzazione conversations_get Strumento MCP list_conversations

Elenca le tue conversazioni di chat, a partire da quelle con l’attività più recente. Compaiono solo le conversazioni di chat interattive con almeno un messaggio.

Parametri di query
page integer in query facoltativo predefinito 1
Numero di pagina. I totali si trovano negli header X-Total-Count, X-Page e X-Page-Size.
size integer in query facoltativo predefinito 10
Elementi per pagina.
search string in query facoltativo
Corrispondenza parziale sul testo di qualsiasi messaggio o sul nome della conversazione.
agent_id uint in query facoltativo
Elenca le chat di questo agente. Senza questo parametro, le chat con gli agenti sono escluse.
team_id uint in query facoltativo
Elenca le conversazioni condivise con questo team con chat_contribute. Senza questo parametro ottieni le conversazioni di tua proprietà che non sono conversazioni di team.
X-Project-ID integer in header facoltativo
Solo le conversazioni di questo progetto.

Risposta. 200 con un array di oggetti conversazione (se è vuoto: []). Ognuno include user e project precaricati, message_count, credits_used e, in messages, solo l’ultimo messaggio (con attachments svuotato). Nota le chiavi in camelCase publicSlug, isPublic, userId, projectId, runId, isInteractive e forkedFromConversationId accanto a quelle in snake_case.

[
  {
    "ID": 5120, "CreatedAt": "2026-09-28T09:13:58Z", "UpdatedAt": "2026-09-28T09:14:10Z", "DeletedAt": null,
    "slug": "aB3dE5gH", "publicSlug": null, "isPublic": false, "share_type": "",
    "userId": 77, "user": { "ID": 77, "first_name": "John", "last_name": "Smith", "...": "..." },
    "next_message_ai_model_id": 12, "next_message_ai_style": "prompty",
    "next_message_auto_model_type": null, "next_message_reasoning_effort": null,
    "projectId": null, "project": null, "agent_id": null,
    "name": "Q4 newsletter subject lines", "source": "chat",
    "messages": [ { "ID": 90412, "isUser": false, "text": "Here are three options ...", "...": "..." } ],
    "isInteractive": true, "expensive_warning_threshold": 1000,
    "forkedFromConversationId": null,
    "message_count": 2, "credits_used": "0.4213"
  }
]

GET /conversation/:id/used_prompts

GET /api/v1/conversation/:id/used_prompts
Autorizzazione conversations_get

Restituisce i prompt salvati distinti eseguiti in questa conversazione, compresi i thread di confronto: un array di { "id", "name", "slug", "ai_model_id", "user_id" }, oppure []. L’ordine non è definito.

Parametri di percorso
id string in path obbligatorio
ID o slug della conversazione.

Gestire le conversazioni

PATCH /conversation/:id

PATCH /api/v1/conversation/:id
Autorizzazione conversations_manage

Rinomina una conversazione e la sposta in cima all’elenco. Solo per il proprietario.

Parametri di percorso
id string in path obbligatorio
ID o slug della conversazione.
Corpo
name string obbligatorio
Il nuovo nome. Se è vuoto o manca, restituisce 400.

Risposta. 200 con l’oggetto conversazione. Errori. 403 {"error":"You are not allowed to edit this conversation"}.

PATCH /conversation/:id/model

PATCH /api/v1/conversation/:id/model
Autorizzazione conversations_manage

Imposta modello, stile e livello di ragionamento per i turni successivi. È consentito al proprietario e agli utenti con chat_contribute. Trasmette anche un evento WebSocket ai_model_change.

Parametri di percorso
id string in path obbligatorio
ID o slug della conversazione.
Corpo
ai_model_id uint facoltativo
Il modello per i turni successivi. 400 {"error":"AI model not found"} se è sconosciuto.
ai_style string facoltativo
Lo stile per i turni successivi.
auto_model_type string facoltativo
best, balanced, cheapest o null. Viene sempre scritto: se lo ometti o invii null, la selezione automatica si disattiva. Invialo a ogni chiamata per mantenerla attiva.
reasoning_effort string facoltativo
Normalizzato in base ai livelli supportati dal nuovo modello o da quello attuale.

Risposta. 200 {"message":"Model changed successfully"}. Errori. 400 {"error":"Cannot change model for agent conversations"}, 403 se non hai accesso.

POST /conversation/:id/fork

POST /api/v1/conversation/:id/fork
Autorizzazione conversations_manage

Crea una nuova conversazione chiamata "Forked from <name>" con le copie di tutti i messaggi fino a message_id incluso. Vengono copiati allegati, strumenti e registrazioni delle approvazioni MCP, e restano invariate le impostazioni di modello, stile e ragionamento. Devi essere il proprietario della conversazione di origine, oppure questa deve essere condivisa; altrimenti ottieni 403.

Parametri di percorso
id string in path obbligatorio
ID o slug della conversazione di origine.
Corpo
message_id uint obbligatorio
L’ultimo messaggio da copiare. Deve essere un messaggio normal di questa conversazione o di uno dei suoi thread di confronto (altrimenti 404 o 400).

Risposta. 200 con una versione ridotta della risposta della conversazione: conversation_context_id, conversation_context_name, conversation_context_slug, public_slug, is_public, user_name, messages (qui dal più vecchio) e credits_used: "0". Le altre chiavi contengono il valore zero del rispettivo tipo.

DELETE /conversation/:id

DELETE /api/v1/conversation/:id

Esegue un’eliminazione logica (soft delete) della conversazione e dei suoi messaggi e nasconde le relative esecuzioni dei prompt dalla cronologia delle esecuzioni. Solo per il proprietario.

Parametri di percorso
id integer in path obbligatorio
ID numerico della conversazione.
curl -X DELETE https://api.stickyprompts.com/api/v1/conversation/5120 \
  -H "Authorization: Bearer $STICKY_API_KEY"

Risposta. 200 {"message":"Conversation deleted successfully"}. Errori. 404 {"error":"Conversation not found or you do not have permission to delete it"}.

POST /conversation/:id/retry

POST /api/v1/conversation/:id/retry
Autorizzazione conversations_manage

Esegue di nuovo l’ultimo turno quando l’ultimo messaggio dell’IA si è concluso con un errore. Il messaggio non riuscito viene rigenerato sul posto (stesso ID) con il modello e lo stile attuali della conversazione, oppure con quelli dell’agente. Solo per il proprietario. Nessun corpo.

Parametri di percorso
id string in path obbligatorio
ID o slug della conversazione.

Risposta. 200 con:

  • per un normale turno di chat, un Message. Sui modelli con stream_support: true la chiamata risponde subito con il messaggio in status: "thinking" e il contenuto arriva sul WebSocket o tramite polling; sugli altri modelli restituisce il messaggio completato;
  • per un turno nato da un prompt salvato, un array di oggetti RunHistory.

Errori. 400 {"error":"Last AI message does not have an error"}, 400 {"error":"No user message found in this conversation"}, 403, 500 {"error":"..."}.

POST /conversation/:id/snooze-expensive-warning

POST /api/v1/conversation/:id/snooze-expensive-warning
Autorizzazione conversations_manage

Quando i credits_used di una conversazione superano il suo expensive_warning_threshold (predefinito: 1000), l’app web mostra un avviso sull’utilizzo. Questo endpoint raddoppia la soglia finché non supera l’utilizzo attuale. Solo per il proprietario.

Parametri di percorso
id string in path obbligatorio
ID o slug della conversazione.

Risposta. 200 con l’oggetto conversazione e il nuovo expensive_warning_threshold. Errori. 400 se l’utilizzo è ancora sotto la soglia.

POST /conversation/:id/agents

POST /api/v1/conversation/:id/agents
Autorizzazione conversations_manage

Invita uno dei tuoi agenti nella conversazione, così da poterlo menzionare (@mentioned). Solo per il proprietario; non è consentito nelle chat con un agente (400).

Parametri di percorso
id string in path obbligatorio
ID o slug della conversazione.
Corpo
agent_id uint obbligatorio
Un agente di tua proprietà.

Risposta. 200 {"message":"Agent invited successfully"}. Errori. 404 {"error":"Agent not found or not owned by user"}, 403.

DELETE /conversation/:id/agents/:agentId

DELETE /api/v1/conversation/:id/agents/:agentId
Autorizzazione conversations_manage

Rimuove un agente invitato. Solo per il proprietario.

Parametri di percorso
id string in path obbligatorio
ID o slug della conversazione.
agentId integer in path obbligatorio
ID numerico dell’agente.

Risposta. 200 {"message":"Agent removed successfully"}. Errori. 400 {"error":"Invalid agent ID"}, 404.

Approvazione degli strumenti MCP

Questa sezione vale quando un turno usa un server MCP remoto (uno strumento remote_mcp o un agente con server MCP) la cui configurazione ha require_approval: true e il modello gira sul protocollo OpenAI Responses, l’unico percorso che genera richieste di approvazione. Le esecuzioni dei workflow approvano sempre in automatico.

  1. Il modello chiede di chiamare uno strumento MCP. La generazione si ferma e il messaggio dell’IA riceve in mcp_approval_requests una voce per ogni chiamata richiesta. Nel percorso in streaming, al testo del messaggio viene aggiunto anche il testo Approval required for action: <name>, con l’etichetta del server e gli argomenti.
  2. Ogni voce usa come chiavi i nomi dei campi Go (senza tag JSON). Approved: null indica una richiesta in attesa; true o false indicano che la decisione è stata presa.
{
  "ID": 311, "CreatedAt": "2026-09-28T09:20:01Z", "UpdatedAt": "2026-09-28T09:20:01Z", "DeletedAt": null,
  "ApprovalRequestID": "mcpr_abc123",
  "Approved": null,
  "ServerLabel": "crm",
  "Name": "create_contact",
  "Arguments": "{\"email\":\"jane@northwind.example\"}",
  "MCPToolID": 0,
  "MessageID": 90415
}
  1. Decidi con POST /conversation/:id/approve, usando l’ID numerico della voce, non ApprovalRequestID.
  2. Il server registra la decisione ed esegue un nuovo turno dell’IA che la ritrasmette al modello. La continuazione è un nuovo messaggio dell’IA.

POST /conversation/:id/approve

POST /api/v1/conversation/:id/approve
Autorizzazione conversations_manage

Approva o rifiuta una chiamata in attesa a uno strumento MCP.

Parametri di percorso
id integer in path obbligatorio
ID numerico della conversazione.
Corpo
approval_request_id uint obbligatorio
L’ID della voce in mcp_approval_requests.
is_approved bool obbligatorio
true per approvare, false per rifiutare.
use_stream bool facoltativo
true: risponde subito e la continuazione arriva sul WebSocket o tramite polling. false: attende che la continuazione sia completata.

Risposta. 200 con la stringa JSON "Successfully handled approval", non un oggetto. Il messaggio di continuazione non viene restituito: leggilo con GET /conversation/:id o dal WebSocket.

Errori. 404 {"error":"Conversation not found"}, 404 {"error":"MCP Approval request not found"}, 400 {"error":"Approval request already processed"}, 400 {"error":"No messages in conversation"}, 500. La continuazione usa il modello fisso impostato per il prossimo messaggio della conversazione; nelle conversazioni con selezione automatica del modello questa operazione non è supportata.

Confronto tra modelli

Una sub-conversation esegue lo stesso messaggio su due o più modelli fianco a fianco, con un “thread” per modello. Finché è attiva, i follow-up inviati alla conversazione padre vanno a tutti i thread. Quando la chiudi, scegli un thread vincitore, la cui cronologia prosegue nella conversazione padre. È il lato API di Confrontare i modelli.

POST /conversation/:id/sub-conversation

POST /api/v1/conversation/:id/sub-conversation
Autorizzazione conversations_manage

Avvia un confronto e attende tutti i thread.

Parametri di percorso
id string in path obbligatorio
ID o slug della tua conversazione (solo per il proprietario). I valori letterali null o undefined creano prima una nuova conversazione vuota.
Corpo
ai_model_ids uint[] obbligatorio
Almeno 2 ID di modello.
message string facoltativo
Il messaggio inviato a ogni modello.
prompt_instance_id uint facoltativo
In alternativa a message, esegue in ogni thread un’istanza di prompt salvata.
attachments array facoltativo
Usato solo con prompt_instance_id.
flash_mode bool facoltativo
Usato solo con prompt_instance_id.
reasoning_effort string facoltativo
Usato solo con prompt_instance_id.

Lo stile è quello della conversazione, altrimenti il tuo stile preferito, altrimenti prompty.

Risposta. 200 con il Message contenitore (type: "sub_conversation"). Il suo sub_conversation contiene parent_conversation_context_id, threads, selected_thread_id e is_active: true. Ogni thread ha ai_model_id, ai_model, thread_conversation_context_id, thread_conversation_context (con messages), unsupported_files e credits_used.

Errori. 400 con meno di 2 modelli, 400 {"error":"AI model not found: <id>"}, 400 {"error":"An active sub-conversation already exists. Close it before creating a new one."}.

POST /conversation/:id/sub-conversation/stream

POST /api/v1/conversation/:id/sub-conversation/stream
Autorizzazione conversations_manage

La variante in streaming: stessi parametri di percorso e stesso corpo, ma risponde non appena esistono i messaggi dell’utente. Le risposte dei thread arrivano poi in streaming sul WebSocket della conversazione padre, e nella risposta i messages di ogni thread contengono solo il messaggio dell’utente.

POST /sub-conversation/:id/close

POST /api/v1/sub-conversation/:id/close
Autorizzazione conversations_manage

Chiude un confronto attivo in una conversazione di tua proprietà.

Parametri di percorso
id integer in path obbligatorio
ID numerico della sub-conversation (sub_conversation_id o sub_conversation.ID).
Corpo
ai_model_id uint obbligatorio
Diventa il modello del prossimo messaggio della conversazione padre. Deve essere maggiore di 0.
ai_style string facoltativo
Diventa lo stile della conversazione padre.
thread_id uint facoltativo
Il thread vincitore. 0 significa nessuno.

Risposta. 204 No Content. Errori. 404 se non esiste o non è attivo.

POST /sub-conversation/:id/select-thread

POST /api/v1/sub-conversation/:id/select-thread
Autorizzazione conversations_manage

Cambia il thread i cui messaggi valgono come cronologia della conversazione. La sub-conversation deve essere chiusa; solo per il proprietario.

Parametri di percorso
id integer in path obbligatorio
ID numerico della sub-conversation.
Corpo
thread_id uint obbligatorio
Maggiore di 0.

Risposta. 204 No Content.

Condivisione

Tutti e quattro gli endpoint di condivisione accettano l’ID numerico della conversazione e sono riservati al proprietario (altrimenti 404 {"error":"Conversation not found"}).

POST /conversation/:id/share

POST /api/v1/conversation/:id/share
Autorizzazione conversations_manage

Condivide la conversazione tramite link. Se non esiste ancora, genera uno slug pubblico di 32 caratteri e imposta isPublic: true. Leggi la conversazione condivisa con GET /conversation/{public_slug}.

Parametri di percorso
id integer in path obbligatorio
ID numerico della conversazione.
Corpo
share_type string obbligatorio
workspace (i membri del tuo workspace), internal (qualsiasi utente di StickyPrompts che ha effettuato l’accesso e ha il link) o public (chiunque abbia il link).

Risposta. 200 {"message":"Conversation shared successfully","publicSlug":"<32 chars>"}. Errori. 400 {"error":"Invalid share type"}.

POST /conversation/:id/revoke-share

POST /api/v1/conversation/:id/revoke-share
Autorizzazione conversations_manage

Imposta isPublic: false. Lo slug viene mantenuto. Nessun corpo.

Parametri di percorso
id integer in path obbligatorio
ID numerico della conversazione.

Risposta. 200 {"message":"Conversation share successfully revoked"}.

POST /conversation/:id/regenrate-share-slug

POST /api/v1/conversation/:id/regenrate-share-slug
Autorizzazione conversations_manage

Sostituisce lo slug pubblico, così i vecchi link smettono di funzionare. Nel percorso si scrive regenrate, esattamente come mostrato. Nessun corpo.

Parametri di percorso
id integer in path obbligatorio
ID numerico della conversazione.

Risposta. 200 {"slug":"<new 32 chars>"}.

POST /conversation/:id/share-with-teams

POST /api/v1/conversation/:id/share-with-teams
Autorizzazione conversations_manage

Condivide la conversazione in sola lettura con i tuoi team. L’elenco rappresenta lo stato completo desiderato per i tuoi team: ogni team di cui fai parte che non compare nell’elenco perde la condivisione, tranne i team che hanno già chat_contribute (come il team per cui è stata creata una conversazione di team). Garantisce inoltre che esista uno slug pubblico.

Parametri di percorso
id integer in path obbligatorio
ID numerico della conversazione.
Corpo
shares array obbligatorio
Elementi { "team_id": uint, "access_type": "chat_view" | "none" }. Qui si può concedere solo chat_view; chat_contribute viene rifiutato con 400.

Risposta. 200 {"message":"Conversation shared successfully with teams","publicSlug":"<32 chars>"}. Errori. 403 {"error":"You are not a member of team <name>"}, 404 per i team sconosciuti.

Messaggi

POST /message/:id/export

POST /api/v1/message/:id/export
Autorizzazione conversations_get

Esporta un singolo messaggio in un file DOCX o PDF, come link di download oppure direttamente in un’integrazione di archiviazione collegata, per esempio Google Drive. Devi essere il proprietario della conversazione o avere almeno chat_view su di essa.

Parametri di percorso
id integer in path obbligatorio
ID numerico del messaggio.
Corpo
format string obbligatorio
docx o pdf.
target string facoltativo
Vuoto o local: genera un file scaricabile. In alternativa, l’identificatore di un provider di archiviazione collegato, per esempio google_drive.
folder_id string facoltativo
La cartella di destinazione nel provider. Obbligatorio per le destinazioni non locali.
name string facoltativo
Il nome del file per le destinazioni su un provider (l’estensione viene aggiunta automaticamente). Per impostazione predefinita è il titolo della conversazione.

Risposta. 200 con esattamente una delle due forme seguenti:

{ "path": "https://<storage host>/<bucket>/<userId>/<random>.pdf" }
{ "entry": { "id": "...", "name": "Q4 subject lines.docx", "kind": "file", "mime_type": "...", "web_url": "...", "parent_id": "...", "...": "..." } }

Errori. 400 {"error":"invalid format"}, 400 {"error":"invalid folder_id"}, 400 {"error":"unknown export target"}, 403 {"error":"you are not allowed to export this message"} oppure una cartella di destinazione non scrivibile, 404 messaggio non trovato o integrazione non collegata, 409 {"error":"reconnect_required"}, 501 il provider non supporta il caricamento, 500.