Conversaciones y mensajes

Inicia chats, envía mensajes de seguimiento, recibe las respuestas en streaming por WebSocket, compara modelos, aprueba llamadas a herramientas MCP y comparte conversaciones con la API REST.

24 min de lectura · Actualizado el 28 de septiembre de 2026

Una conversación es un hilo de chat: lo mismo que tu equipo ve en Chat History dentro de la aplicación. La inicias con un mensaje, el modelo responde y tú sigues enviando mensajes de seguimiento. Todas las solicitudes de esta página se ejecutan como el usuario propietario de la clave de API, con los mismos accesos, cuotas y reglas del espacio de trabajo que tendría ese usuario en el navegador.

Todas las rutas son relativas a https://api.stickyprompts.com/api/v1. Los cuerpos de las solicitudes son JSON. Los archivos se suben primero mediante los endpoints de archivos y después se referencian por su ID.

Cómo funciona el chat

  1. Inicia con POST /conversation (espera la respuesta completa) o con POST /conversation/stream (devuelve el control al instante y la respuesta llega por el WebSocket). Ambos crean la conversación y envían el primer mensaje en una sola llamada.
  2. Continúa con POST /conversation/:id o POST /conversation/:id/stream.
  3. Cambia el modelo, el estilo o el nivel de razonamiento de los turnos siguientes con PATCH /conversation/:id/model. Los cuerpos de seguimiento no aceptan un modelo: la conversación recuerda su configuración para el «siguiente mensaje».
  4. Lee el historial con GET /conversation/:id (paginado, del más reciente al más antiguo) y lista las conversaciones con GET /conversation.
  5. Sigue el progreso en directo con el WebSocket GET /conversation/:id/ws, o consulta periódicamente GET /conversation/:id y lee el status del mensaje de la IA.

Identificadores. La mayoría de los endpoints aceptan como :id tanto el ID numérico de la conversación como su slug de 8 caracteres. Los endpoints de compartir, eliminar y aprobar, así como los streams de WebSocket y SSE, solo aceptan el ID numérico; cada caso se indica más abajo.

El objeto Message

Lo devuelven los endpoints de envío sin streaming, aparece dentro de GET /conversation/:id y es el data de los eventos message del WebSocket.

{
  "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": []
}
CampoTipoSignificado
conversation_context_iduint o nullLa conversación a la que pertenece este mensaje.
ai_model_id, ai_modeluint, objetoEl modelo que generó este mensaje (IA) o que se seleccionó para él (usuario).
agent_id, agentuint, objetoEl agente que respondió, si lo hubo.
is_auto_modelbooltrue cuando el enrutamiento automático eligió el modelo.
prompt_iduint o nullEl prompt guardado que se usó en este mensaje del usuario.
textstringTexto del mensaje. En los mensajes de la IA, los marcadores de cita ya vienen renderizados.
raw_textstringEl texto de la IA sin procesar (solo en las rutas de lectura y de streaming).
partsarrayBloques estructurados. type es text, thinking, web_search, web_fetch, code_execution, image_generation o mcp_call, con el objeto *_part correspondiente. static es true cuando no hay nada que desplegar.
thinkingstring o nullTexto de razonamiento de los modelos de razonamiento.
isUserbooltrue en los mensajes del usuario, false en los de la IA.
statusstringthinking, generating o finished.
error_text, error_id, error_codestring o nullSe rellenan cuando la generación falla. error_id es una referencia de soporte de 10 caracteres; error_code es un código legible por máquina para algunos tipos de fallo, como los bloqueos por datos sensibles y los errores de MCP.
mcp_approval_requestsarrayAprobaciones de herramientas MCP pendientes o ya resueltas. Consulta Aprobaciones de herramientas MCP.
toolsarrayHerramientas habilitadas en este turno.
citationsarrayCitas de la web y de bases de conocimiento: url, file_path, title, info (kb_id, file_id, rangos de páginas y de filas), citation_index y más.
knowledge_base_ids, world_knowledgeuint[], boolLas bases de conocimiento consultadas en este turno y si el modelo podía ir más allá de ellas.
response_duration_msint o nullTiempo de generación.
typestringnormal o sub_conversation (el contenedor de una comparación de modelos).
attachmentsarrayArchivos y transcripciones adjuntos, cada uno con una clave inicial "type": "file" o "type": "transcription". Siempre es un array.

Estado. thinking significa que la fila del mensaje de la IA ya existe, pero todavía no se ha generado nada; generating, que está llegando salida parcial; finished, que ha terminado. Un mensaje de la IA que ha fallado también queda en finished, con error_text y error_id rellenos, así que comprueba esos campos y no solo status. Los mensajes del usuario siempre están en finished.

Créditos. Todos los campos credits_used son decimales serializados como cadena JSON, por ejemplo "0.4213".

Envío de mensajes

Cuerpo de la solicitud

POST /conversation y POST /conversation/stream aceptan todos los campos siguientes. Los endpoints de seguimiento solo aceptan los marcados con «seguimiento»; el resto se ignora sin avisar. La validación de la solicitud no exige técnicamente ningún campo, pero envía siempre un message y un modelo.

Cuerpo
message string opcional
El mensaje del usuario. Menciona @<agent-tag> para dirigir el turno a uno de tus agentes. Inicio y seguimiento.
attachments array opcional
Elementos { "id": uint, "type": "file" | "transcription", "temp": bool }. Archivos subidos o transcripciones terminadas. Con temp: true, el adjunto se usa solo en este turno y no queda registrado en el mensaje. Inicio y seguimiento.
knowledge_base_ids uint[] opcional
Bases de conocimiento de las que se obtiene contexto en este turno. Inicio y seguimiento.
knowledge_base_limit_node_ids uint[] opcional
Limita la recuperación a estas carpetas o archivos de la base de conocimiento. Inicio y seguimiento.
world_knowledge bool opcional por defecto false
false: responde solo a partir de la base de conocimiento. true: la base de conocimiento es la fuente principal, pero el modelo también puede usar la búsqueda web y su propio conocimiento. Inicio y seguimiento.
flash_mode bool opcional
Solicita el nivel de servicio prioritario del proveedor. Solo tiene efecto en los modelos que lo admiten. Inicio y seguimiento.
tools array opcional
Elementos { "name", "use", "mcp_tool_id"? }. Consulta Herramientas. Inicio y seguimiento.
ai_model_id uint opcional
Modelo que se usará, obtenido de GET /ai_model. Se ignora si se define auto_model_type. Solo al inicio.
auto_model_type string opcional
best, balanced o cheapest. El enrutamiento automático elige el mejor modelo para cada mensaje y tiene prioridad sobre ai_model_id. Solo al inicio.
ai_style string opcional por defecto prompty
Estilo de respuesta: el valor tone de un estilo de IA. Solo al inicio.
reasoning_effort string opcional
Nivel de razonamiento para los modelos de razonamiento. Un valor no admitido se sustituye por el predeterminado del modelo; en los modelos sin razonamiento se guarda null. Solo al inicio.
agent_id uint opcional
Inicia un chat con un agente: en cada turno se usan el modelo, el prompt de sistema, las herramientas, los servidores MCP, las bases de conocimiento y los archivos del agente. El agente debe ser tuyo. Solo al inicio.
invited_agent_ids uint[] opcional
Agentes tuyos que se pueden mencionar (@mentioned) en esta conversación. Se ignora cuando se define agent_id. Solo al inicio.
team_id uint opcional
Crea una conversación de equipo (el equipo obtiene acceso chat_contribute). Consulta Cuándo responde la IA. Solo al inicio.
X-Project-ID integer en header opcional
Solo al inicio. Archiva la conversación en este proyecto. Si el cuerpo no incluye un modelo, se usa el modelo predeterminado del proyecto. En cada turno, los archivos del proyecto se añaden al contexto. El campo project_id del cuerpo existe, pero no se lee: usa este header.

Selección del modelo. Primero cuenta auto_model_type, después ai_model_id y, por último, el modelo predeterminado del proyecto (con X-Project-ID). Si no se aplica ninguno, la conversación se crea igualmente, pero el envío falla con 503 {"error":"no AI model is selected, try reselecting model"}, así que al iniciar envía siempre un modelo o un tipo automático. No existe ningún ID de modelo literal "auto". Los chats con agentes y las menciones con @agent usan el modelo del agente (estilo prompty, sin nivel de razonamiento). El modelo debe estar permitido por las reglas de acceso a modelos y de residencia de datos de tu espacio de trabajo; si no, el envío falla con 503.

Herramientas

Cada entrada es { "name": ..., "use": true | false }. Nombres: web_search, image_generation, video_generation, speech_generation, music_generation, remote_mcp.

  • Una herramienta solo llega al modelo si este la incluye entre sus capacidades tools. Si no, se ignora sin avisar.
  • remote_mcp necesita además "mcp_tool_id": <id>, el ID de uno de tus servidores MCP remotos configurados. Añade una entrada por servidor.
  • Selección automática de herramientas. En los modelos que admiten herramientas, un pequeño clasificador puede activar web_search e image_generation (y la ejecución de código) según el mensaje, aunque no lo hayas pedido. Si envías una entrada para una herramienta, aunque sea {"name":"web_search","use":false}, el clasificador ya no la añade. La ejecución de código la decide únicamente el clasificador.

POST /conversation

POST /api/v1/conversation

Crea una conversación, envía el primer mensaje y espera la respuesta completa de la IA, incluidas las llamadas a herramientas. La respuesta es el mensaje terminado de la IA; el ID de la nueva conversación es su conversation_context_id. La conversación recibe un nombre automático después del primer intercambio. Acepta el cuerpo de la solicitud descrito arriba y, de forma opcional, el header 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);

Respuesta. 200 con el mensaje de la IA (consulta el objeto Message). Cuando la IA no debe responder (una conversación de equipo con varios miembros y sin mención a @ai ni a ningún agente), el cuerpo es 200 {"conversation_context_id": 5120}.

Errores

EstadoCuerpoCuándo
400{"error": "..."}JSON mal formado.
400{"error": "...", "error_code": "..."}Bloqueado por las reglas de datos sensibles de tu espacio de trabajo, o error de un servidor MCP. Cuando la IA no responde, un bloqueo por datos sensibles usa la clave code en lugar de error_code.
400{"error": "Knowledge base not found"}Base de conocimiento desconocida.
429{"error": "Quota exceeded", "reason": "..."}Se comprueba antes de crear nada. reason es credit_count, user_count, subscription, trial_expired o unknown.
503{"error": "could not create conversation"}Por ejemplo, un agent_id que no es tuyo o un team_id desconocido (un fallo al buscar el equipo también puede aparecer como 500).
503{"error": "<message>"}Cualquier otro fallo: ningún modelo seleccionado, modelo restringido, error del proveedor.

Si el proveedor falla a mitad de la generación, el mensaje de la IA se guarda igualmente con error_text y error_id, y POST /conversation/:id/retry puede volver a ejecutarlo.

POST /conversation/stream

POST /api/v1/conversation/stream

Mismos headers y cuerpo que POST /conversation, pero responde en cuanto se guarda el mensaje del usuario. La generación continúa en segundo plano y la respuesta llega por el WebSocket o mediante sondeo (polling).

{ "conversation_context_id": 5120 }

Los errores detectados antes de guardar el mensaje del usuario (cuota 429, agente no válido, ningún modelo seleccionado, modelo restringido, bloqueo por datos sensibles) se siguen devolviendo de forma síncrona, con los mismos formatos de arriba. Los errores posteriores al 200 (selección de modelo o de herramientas, obtención de la base de conocimiento, error del proveedor) solo se notifican como evento error_message del WebSocket y quedan guardados en un mensaje de la IA con error_text y error_id.

Cómo obtener la respuesta

  • WebSocket (recomendado). Conéctate a GET /conversation/{conversation_context_id}/ws. Los eventos emitidos antes de que te conectes se guardan en búfer y se reenvían al primer cliente, así que si te conectas justo después del 200 no pierdes nada.
  • Sondeo. Llama a GET /conversation/:id?page=1&size=2 y lee el mensaje más reciente de la IA (isUser: false, el primero de la lista, ya que el orden va del más reciente al más antiguo). El mensaje de la IA se crea con status: "thinking" y se guarda con cada fragmento, así que verás generating con un text cada vez más largo y, al final, finished. Detente en finished y comprueba error_id. Justo después del 200, puede que el mensaje de la IA aún no exista; si el mensaje más reciente sigue siendo el tuyo, la generación todavía no ha empezado.

El streaming solo es en tiempo real en los modelos con stream_support: true. En los demás, la respuesta aparece de una sola vez cuando está terminada.

POST /conversation/:id

POST /api/v1/conversation/:id

Envía un mensaje de seguimiento y espera la respuesta de la IA. El modelo, el estilo y el nivel de razonamiento salen de la configuración next_message_* de la conversación; cámbiala antes con PATCH /conversation/:id/model.

Parámetros de ruta
id string en path obligatorio
ID o slug de la conversación.
Cuerpo
message string opcional
El texto de seguimiento.
attachments array opcional
Como en el cuerpo de la solicitud.
knowledge_base_ids uint[] opcional
Bases de conocimiento para este turno.
knowledge_base_limit_node_ids uint[] opcional
Limita la recuperación a estos nodos.
world_knowledge bool opcional
Permite usar conocimiento más allá de la base de conocimiento.
flash_mode bool opcional
Nivel de servicio prioritario, donde esté disponible.
tools array opcional
Herramientas para este 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." }'

Respuesta. Igual que en POST /conversation: 200 con el mensaje de la IA, o 200 {"conversation_context_id": ...} cuando la IA no responde. Si la conversación tiene una comparación de modelos activa, el mensaje se envía en paralelo a todos los hilos y la respuesta es un array JSON de mensajes de la IA, uno por hilo.

Errores. Los mismos que en POST /conversation, más 404 {"error":"Conversation not found"}. Solo pueden enviar mensajes el propietario y los usuarios o equipos con chat_contribute; cualquier otra persona recibe 503 {"error":"you are not allowed to send messages in this conversation"}.

POST /conversation/:id/stream

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

Igual que POST /conversation/:id, pero devuelve 200 {"conversation_context_id": <id>} de inmediato. Sigue el progreso por el WebSocket o mediante sondeo, como con POST /conversation/stream. Con una comparación de modelos activa, todos los hilos se transmiten por el WebSocket de la conversación principal.

Parámetros de ruta
id string en path obligatorio
ID o slug de la conversación.

Cuándo responde la IA

La IA responde a un mensaje cuando se cumple cualquiera de estas condiciones:

  • solo un usuario tiene acceso de colaboración a la conversación (el caso normal, que incluye todas las conversaciones creadas con una clave de API y sin team_id);
  • el texto contiene @ai (distingue entre mayúsculas y minúsculas);
  • es un chat con un agente;
  • el texto menciona (@mentions) la etiqueta de un agente invitado a la conversación.

En caso contrario, el mensaje se guarda como un mensaje normal del usuario, visible para los demás participantes, y el endpoint devuelve 200 {"conversation_context_id": <id>} sin llamar a ningún modelo, incluso en los endpoints sin streaming.

Agentes en una conversación

  • Si indicas agent_id al crear la conversación, esta se convierte en un chat con un agente. Su modelo no se puede cambiar después.
  • En una conversación de un solo usuario, @tag dirige ese turno a tu propio agente con esa etiqueta o a un agente compartido contigo (si hay conflicto de etiquetas, tienen prioridad tus agentes). En una conversación con varios usuarios, solo se puede mencionar a los agentes invitados con invited_agent_ids o con POST /conversation/:id/agents.
  • El agente que responde queda registrado en el mensaje de la IA (agent_id, agent).

Streaming

GET /conversation/:id/ws

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

Un WebSocket que envía cada actualización de la conversación. Conéctate a wss://api.stickyprompts.com/api/v1/conversation/5120/ws y envía el header Authorization: Bearer STICKY-API-... en la solicitud de upgrade. Los objetos WebSocket del navegador no pueden definir headers, así que esta opción es para clientes del lado del servidor. El servidor ignora todo lo que envíes; solo lee para detectar el cierre.

Parámetros de ruta
id integer en path obligatorio
El ID numérico de la conversación. Con un slug te conectas a otro stream distinto y vacío.

Todas las tramas son tramas de texto JSON con la forma {"type": <string>, "data": <payload>}:

typedataCuándo
messageMessage (instantánea completa)Un mensaje nuevo de otro participante y cada actualización de un mensaje de la IA durante la generación. text, parts y thinking contienen el contenido acumulado hasta el momento, no un delta: sustituye tu copia según data.ID. status pasa por thinking, generating y finished.
ai_message_endninguno (la clave no aparece)La generación ha terminado, con éxito o sin él. Cierra el turno.
error_messageMessage con error_text, error_id, error_code opcional y status: "finished"Ha fallado la generación o la preparación. Trátalo como el final del turno: tras los fallos de preparación suele llegar un ai_message_end, pero no está garantizado tras errores del proveedor a mitad de la generación.
ai_model_change{ "ai_model_id", "ai_style", "auto_model_type", "reasoning_effort" }Alguien ha llamado a PATCH /conversation/:id/model.
run_dataObjeto RunHistorySolo en los turnos que proceden de la ejecución de un prompt guardado, junto con los eventos message. Consulta Prompts y ejecuciones.
{"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"}
  • Tus propios mensajes de usuario no se reenvían a tu conexión. La clave actúa en nombre de su propietario, así que un cliente que usa la clave solo ve los mensajes de la IA y los de otras personas.
  • Mientras no hay ningún cliente conectado, los eventos se guardan en búfer, se reenvían a la siguiente conexión y después se borran. Un cliente que se conecta mucho después de un turno puede recibir primero eventos obsoletos de ese turno: ignora los mensajes cuyo ID ya tengas en estado terminado.
  • Los streams residen en la memoria del servidor de la API. Si te conectas y no recibes nada de un turno que sabes que está en curso, recurre al sondeo de 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

Un endpoint heredado de Server-Sent Events. Usa el WebSocket o el sondeo en su lugar. En el código actual, la ruta de envío de la conversación solo escribe en el WebSocket, así que este stream solo muestra eventos error_message y run_data en caso de errores de ejecución de prompts y, por lo demás, permanece abierto sin enviar contenido de los mensajes.

Parámetros de ruta
id integer en path obligatorio
ID numérico de la conversación.

Devuelve 404 {"error":"Stream not found"} cuando el servidor no tiene ningún stream en memoria para la conversación. En caso contrario, responde con Content-Type: text/event-stream y tramas event:<name>\ndata:<json>\n\n, donde <name> es message, error_message (array de {run_id, error_id, error_text, model_name}), run_data (array de RunHistory) o close (data: Stream closed).

Lectura de conversaciones

GET /conversation/:id

GET /api/v1/conversation/:id

Devuelve una conversación con una página de sus mensajes, del más reciente al más antiguo. Los metadatos de paginación están en los headers de respuesta X-Total-Count (total de mensajes), X-Page y X-Page-Size.

Parámetros de ruta
id string en path obligatorio
ID o slug de la conversación, o su slug público de compartición.
Parámetros de consulta
page integer en query opcional por defecto 1
Número de página.
size integer en query opcional por defecto 10
Mensajes por página.

Acceso. El propietario; los usuarios con chat_view o chat_contribute porque se les ha compartido; o, cuando :id es el slug público de una conversación compartida, quien permita el tipo de compartición (public: cualquiera; internal: cualquier usuario con sesión iniciada; workspace: los miembros del espacio de trabajo del propietario).

{
  "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
}
CampoSignificado
credits_usedCréditos de toda la conversación, incluidos los hilos de comparación (cadena decimal).
messagesEsta página de mensajes, del más reciente al más antiguo. Los mensajes de la IA llevan las citas renderizadas en text y el original en raw_text.
next_message_ai_model_id, next_message_ai_modelEl modelo que usará el siguiente turno. Si no hay ninguno, se usa el del último mensaje de la IA.
next_message_ai_model_styleEstilo del siguiente turno. Atención: la clave no coincide con next_message_ai_style del objeto de conversación.
next_message_auto_model_typebest, balanced o cheapest cuando hay enrutamiento automático.
forked_from_conversation_id, _slug, _nameSe rellenan en las bifurcaciones (forks).
has_sub_conversationsExiste una comparación de modelos.
unsupported_filesArchivos adjuntados anteriormente que el siguiente modelo no puede leer.
current_user_permissionchat_contribute para el propietario; para los demás, su acceso más alto: chat_view, chat_contribute o none.
team_shares{team_id, team, access_type} por cada equipo con el que se comparte la conversación.

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

GET /conversation

GET /api/v1/conversation

Lista tus conversaciones de chat, empezando por la de actividad más reciente. Solo aparecen las conversaciones de chat interactivas con al menos un mensaje.

Parámetros de consulta
page integer en query opcional por defecto 1
Número de página. Los totales están en los headers X-Total-Count, X-Page y X-Page-Size.
size integer en query opcional por defecto 10
Elementos por página.
search string en query opcional
Busca la subcadena en el texto de cualquier mensaje o en el nombre de la conversación.
agent_id uint en query opcional
Lista los chats de este agente. Sin este parámetro, se excluyen los chats con agentes.
team_id uint en query opcional
Lista las conversaciones compartidas con este equipo con chat_contribute. Sin este parámetro, obtienes las conversaciones de las que eres propietario y que no son conversaciones de equipo.
X-Project-ID integer en header opcional
Solo las conversaciones de este proyecto.

Respuesta. 200 con un array de objetos de conversación (si no hay ninguno, []). Cada uno incluye precargados user y project, además de message_count, credits_used y, en messages, solo el último mensaje (con attachments vacío). Atención a las claves en camelCase publicSlug, isPublic, userId, projectId, runId, isInteractive y forkedFromConversationId, que conviven con las de 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

Devuelve los distintos prompts guardados que se han ejecutado en esta conversación, incluidos los hilos de comparación: un array de { "id", "name", "slug", "ai_model_id", "user_id" }, o []. El orden no está definido.

Parámetros de ruta
id string en path obligatorio
ID o slug de la conversación.

Gestión de conversaciones

PATCH /conversation/:id

PATCH /api/v1/conversation/:id

Cambia el nombre de una conversación y la sube al principio de la lista. Solo el propietario.

Parámetros de ruta
id string en path obligatorio
ID o slug de la conversación.
Cuerpo
name string obligatorio
El nuevo nombre. Si está vacío o falta, se devuelve 400.

Respuesta. 200 con el objeto de conversación. Errores. 403 {"error":"You are not allowed to edit this conversation"}.

PATCH /conversation/:id/model

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

Define el modelo, el estilo y el nivel de razonamiento de los turnos siguientes. Pueden usarlo el propietario y los usuarios con chat_contribute. También emite un evento ai_model_change por el WebSocket.

Parámetros de ruta
id string en path obligatorio
ID o slug de la conversación.
Cuerpo
ai_model_id uint opcional
Modelo para los turnos siguientes. Si no existe, 400 {"error":"AI model not found"}.
ai_style string opcional
Estilo para los turnos siguientes.
auto_model_type string opcional
best, balanced, cheapest o null. Siempre se escribe: si lo omites o envías null, se desactiva el enrutamiento automático. Envíalo en cada llamada para mantenerlo activo.
reasoning_effort string opcional
Se ajusta a los niveles que admite el modelo nuevo o el actual.

Respuesta. 200 {"message":"Model changed successfully"}. Errores. 400 {"error":"Cannot change model for agent conversations"}, 403 si no tienes acceso.

POST /conversation/:id/fork

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

Crea una conversación nueva llamada "Forked from <name>" con copias de todos los mensajes hasta message_id inclusive. Se copian los adjuntos, las herramientas y los registros de aprobación de MCP, y se conservan el modelo, el estilo y la configuración de razonamiento. Debes ser el propietario de la conversación de origen o esta debe estar compartida; si no, recibes 403.

Parámetros de ruta
id string en path obligatorio
ID o slug de la conversación de origen.
Cuerpo
message_id uint obligatorio
El último mensaje que se copia. Debe ser un mensaje normal de esta conversación o de uno de sus hilos de comparación (si no, 404 o 400).

Respuesta. 200 con una respuesta de conversación reducida: conversation_context_id, conversation_context_name, conversation_context_slug, public_slug, is_public, user_name, messages (aquí, del más antiguo al más reciente) y credits_used: "0". Las demás claves llevan su valor cero.

DELETE /conversation/:id

DELETE /api/v1/conversation/:id

Elimina de forma lógica (soft delete) la conversación y sus mensajes, y oculta sus ejecuciones de prompts del historial de ejecuciones. Solo el propietario.

Parámetros de ruta
id integer en path obligatorio
ID numérico de la conversación.
curl -X DELETE https://api.stickyprompts.com/api/v1/conversation/5120 \
  -H "Authorization: Bearer $STICKY_API_KEY"

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

POST /conversation/:id/retry

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

Vuelve a ejecutar el último turno cuando el mensaje más reciente de la IA terminó con un error. El mensaje fallido se regenera en su sitio (mismo ID) con el modelo y el estilo actuales de la conversación, o con los del agente. Solo el propietario. Sin cuerpo.

Parámetros de ruta
id string en path obligatorio
ID o slug de la conversación.

Respuesta. 200 con:

  • en un turno de chat normal, un Message. En los modelos con stream_support: true, la llamada responde de inmediato con el mensaje en status: "thinking" y el contenido llega por el WebSocket o mediante sondeo; en los demás modelos, devuelve el mensaje terminado;
  • en un turno que procede de un prompt guardado, un array de objetos RunHistory.

Errores. 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

Cuando el credits_used de una conversación supera su expensive_warning_threshold (1000 de forma predeterminada), la aplicación web muestra un aviso de uso. Esta llamada duplica el umbral hasta dejarlo por encima del uso actual. Solo el propietario.

Parámetros de ruta
id string en path obligatorio
ID o slug de la conversación.

Respuesta. 200 con el objeto de conversación y su nuevo expensive_warning_threshold. Errores. 400 si el uso sigue por debajo del umbral.

POST /conversation/:id/agents

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

Invita a uno de tus agentes a la conversación para que se le pueda mencionar (@mentioned). Solo el propietario; no se permite en los chats con agentes (400).

Parámetros de ruta
id string en path obligatorio
ID o slug de la conversación.
Cuerpo
agent_id uint obligatorio
Un agente del que eres propietario.

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

DELETE /conversation/:id/agents/:agentId

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

Quita un agente invitado. Solo el propietario.

Parámetros de ruta
id string en path obligatorio
ID o slug de la conversación.
agentId integer en path obligatorio
ID numérico del agente.

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

Aprobaciones de herramientas MCP

Esto se aplica cuando un turno usa un servidor MCP remoto (una herramienta remote_mcp o un agente con servidores MCP) cuya configuración tiene require_approval: true y el modelo funciona con el protocolo OpenAI Responses, la única vía que genera aprobaciones. Las ejecuciones de flujos de trabajo siempre se aprueban automáticamente.

  1. El modelo pide llamar a una herramienta MCP. La generación se detiene y el mensaje de la IA recibe en mcp_approval_requests una entrada por cada llamada solicitada. En la ruta de streaming, además, se añade al texto del mensaje el texto Approval required for action: <name> con la etiqueta del servidor y los argumentos.
  2. Las claves de cada entrada son nombres de campo de Go (sin etiquetas JSON). Approved: null significa que está pendiente; true o false, que ya se ha decidido.
{
  "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. Decide con POST /conversation/:id/approve, usando el ID numérico de la entrada, no ApprovalRequestID.
  2. El servidor registra la decisión y ejecuta un nuevo turno de la IA que se la transmite al modelo. La continuación es un mensaje nuevo de la IA.

POST /conversation/:id/approve

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

Aprueba o rechaza una llamada pendiente a una herramienta MCP.

Parámetros de ruta
id integer en path obligatorio
ID numérico de la conversación.
Cuerpo
approval_request_id uint obligatorio
El ID de la entrada en mcp_approval_requests.
is_approved bool obligatorio
true para aprobar, false para rechazar.
use_stream bool opcional
true: responde de inmediato y la continuación llega por el WebSocket o mediante sondeo. false: espera a que termine la continuación.

Respuesta. 200 con la cadena JSON "Successfully handled approval", no con un objeto. El mensaje de continuación no se devuelve: léelo con GET /conversation/:id o por el WebSocket.

Errores. 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 continuación usa el modelo fijo del siguiente mensaje de la conversación; en una conversación con enrutamiento automático, esto no es compatible.

Comparación de modelos

Una subconversación ejecuta el mismo mensaje con dos o más modelos en paralelo, con un «hilo» por modelo. Mientras está activa, los mensajes de seguimiento a la conversación principal van a todos los hilos. Al cerrarla, se elige un hilo ganador cuyo historial continúa en la conversación principal. Es la parte de la API de Comparar modelos.

POST /conversation/:id/sub-conversation

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

Inicia una comparación y espera a que terminen todos los hilos.

Parámetros de ruta
id string en path obligatorio
ID o slug de tu conversación (solo el propietario). Los valores literales null o undefined crean primero una conversación vacía nueva.
Cuerpo
ai_model_ids uint[] obligatorio
Al menos 2 IDs de modelo.
message string opcional
El mensaje que se envía a todos los modelos.
prompt_instance_id uint opcional
En lugar de message, ejecuta una instancia de prompt guardado en cada hilo.
attachments array opcional
Solo se usa con prompt_instance_id.
flash_mode bool opcional
Solo se usa con prompt_instance_id.
reasoning_effort string opcional
Solo se usa con prompt_instance_id.

El estilo es el de la conversación; si no tiene, tu estilo preferido; y si tampoco hay, prompty.

Respuesta. 200 con el Message contenedor (type: "sub_conversation"). Su sub_conversation contiene parent_conversation_context_id, threads, selected_thread_id e is_active: true. Cada hilo tiene ai_model_id, ai_model, thread_conversation_context_id, thread_conversation_context (con messages), unsupported_files y credits_used.

Errores. 400 con menos de 2 modelos, 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

La variante con streaming: mismos parámetros de ruta y mismo cuerpo, pero responde en cuanto existen los mensajes del usuario. Después, las respuestas de los hilos se transmiten por el WebSocket de la conversación principal, y en la respuesta el messages de cada hilo contiene solo el mensaje del usuario.

POST /sub-conversation/:id/close

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

Cierra una comparación activa en una conversación de la que eres propietario.

Parámetros de ruta
id integer en path obligatorio
ID numérico de la subconversación (sub_conversation_id o sub_conversation.ID).
Cuerpo
ai_model_id uint obligatorio
Pasa a ser el modelo del siguiente mensaje de la conversación principal. Debe ser mayor que 0.
ai_style string opcional
Pasa a ser el estilo de la conversación principal.
thread_id uint opcional
El hilo ganador. 0 significa ninguno.

Respuesta. 204 No Content. Errores. 404 si no existe o no está activa.

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

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

Cambia el hilo cuyos mensajes cuentan como historial de la conversación. La subconversación debe estar cerrada; solo el propietario.

Parámetros de ruta
id integer en path obligatorio
ID numérico de la subconversación.
Cuerpo
thread_id uint obligatorio
Mayor que 0.

Respuesta. 204 No Content.

Compartir

Los cuatro endpoints de compartir usan el ID numérico de la conversación y solo puede llamarlos el propietario (en caso contrario, 404 {"error":"Conversation not found"}).

POST /conversation/:id/share

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

Comparte la conversación mediante un enlace. Genera un slug público de 32 caracteres si aún no existe y establece isPublic: true. Lee la conversación compartida con GET /conversation/{public_slug}.

Parámetros de ruta
id integer en path obligatorio
ID numérico de la conversación.
Cuerpo
share_type string obligatorio
workspace (miembros de tu espacio de trabajo), internal (cualquier usuario de StickyPrompts con sesión iniciada que tenga el enlace) o public (cualquier persona con el enlace).

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

POST /conversation/:id/revoke-share

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

Establece isPublic: false. El slug se conserva. Sin cuerpo.

Parámetros de ruta
id integer en path obligatorio
ID numérico de la conversación.

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

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

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

Sustituye el slug público para que los enlaces antiguos dejen de funcionar. La ruta se escribe regenrate, exactamente como se muestra. Sin cuerpo.

Parámetros de ruta
id integer en path obligatorio
ID numérico de la conversación.

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

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

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

Comparte la conversación en modo de solo lectura con tus equipos. La lista representa el estado deseado completo para tus equipos: se deja de compartir con cualquier equipo al que pertenezcas que no figure en ella, salvo con los equipos que ya tienen chat_contribute (como el equipo para el que se creó una conversación de equipo). También garantiza que exista un slug público.

Parámetros de ruta
id integer en path obligatorio
ID numérico de la conversación.
Cuerpo
shares array obligatorio
Elementos { "team_id": uint, "access_type": "chat_view" | "none" }. Aquí solo se puede conceder chat_view; chat_contribute se rechaza con 400.

Respuesta. 200 {"message":"Conversation shared successfully with teams","publicSlug":"<32 chars>"}. Errores. 403 {"error":"You are not a member of team <name>"}, 404 si el equipo no existe.

Mensajes

POST /message/:id/export

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

Exporta un mensaje a un archivo DOCX o PDF, ya sea como enlace de descarga o directamente a una integración de almacenamiento conectada, como Google Drive. Debes ser el propietario de la conversación o tener al menos chat_view sobre ella.

Parámetros de ruta
id integer en path obligatorio
ID numérico del mensaje.
Cuerpo
format string obligatorio
docx o pdf.
target string opcional
Vacío o local: genera un archivo descargable. Si no, el identificador de un proveedor de almacenamiento conectado, por ejemplo google_drive.
folder_id string opcional
Carpeta de destino en el proveedor. Obligatorio para destinos que no sean locales.
name string opcional
Nombre del archivo para los destinos de proveedor (la extensión se añade sola). De forma predeterminada, el título de la conversación.

Respuesta. 200 con exactamente una de estas dos formas:

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

Errores. 400 {"error":"invalid format"}, 400 {"error":"invalid folder_id"}, 400 {"error":"unknown export target"}, 403 {"error":"you are not allowed to export this message"} o una carpeta de destino sin permiso de escritura, 404 si no se encuentra el mensaje o la integración no está conectada, 409 {"error":"reconnect_required"}, 501 si el proveedor no admite subidas, 500.