Conversations et messages

Démarrez des chats, envoyez des messages de suivi, recevez les réponses en streaming par WebSocket, comparez des modèles, approuvez les appels d'outils MCP et partagez des conversations avec l'API REST.

24 min de lecture · Mis à jour le 28 septembre 2026

Une conversation est un fil de chat, exactement ce que votre équipe voit sous Chat History dans l’application. Vous la démarrez avec un message, le modèle répond, puis vous poursuivez avec des messages de suivi. Chaque requête décrite ici s’exécute au nom de l’utilisateur propriétaire de la clé API, avec les mêmes accès, quotas et règles d’espace de travail que cet utilisateur dans le navigateur.

Tous les chemins sont relatifs à https://api.stickyprompts.com/api/v1. Les corps de requête sont en JSON. Les fichiers sont d’abord envoyés via les endpoints de fichiers, puis référencés par leur ID.

Fonctionnement du chat

  1. Démarrez avec POST /conversation (attend la réponse complète) ou POST /conversation/stream (rend la main aussitôt ; la réponse arrive par le WebSocket). Les deux créent la conversation et envoient le premier message en un seul appel.
  2. Poursuivez avec POST /conversation/:id ou POST /conversation/:id/stream.
  3. Changez de modèle, de style ou de niveau de raisonnement pour les tours suivants avec PATCH /conversation/:id/model. Les corps des messages de suivi n’acceptent pas de modèle : la conversation mémorise ses réglages « prochain message ».
  4. Lisez l’historique avec GET /conversation/:id (paginé, du plus récent au plus ancien) et listez les conversations avec GET /conversation.
  5. Suivez la progression en direct avec le WebSocket GET /conversation/:id/ws, ou interrogez régulièrement GET /conversation/:id et lisez le status du message de l’IA.

Identifiants. La plupart des endpoints acceptent comme :id soit l’ID numérique de la conversation, soit son slug de 8 caractères. Les endpoints de partage, la suppression, l’approbation ainsi que les flux WebSocket et SSE n’acceptent que l’ID numérique ; chaque cas est signalé plus bas.

L’objet Message

Renvoyé par les endpoints d’envoi sans streaming, à l’intérieur de GET /conversation/:id, et comme data des événements 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": []
}
ChampTypeSignification
conversation_context_iduint ou nullLa conversation à laquelle appartient ce message.
ai_model_id, ai_modeluint, objectLe modèle qui a produit ce message (IA) ou qui a été sélectionné pour lui (utilisateur).
agent_id, agentuint, objectL’agent qui a répondu, le cas échéant.
is_auto_modelbooltrue lorsque le routage automatique a choisi le modèle.
prompt_iduint ou nullPrompt enregistré utilisé pour ce message utilisateur.
textstringTexte du message. Dans les messages de l’IA, les marqueurs de citation sont déjà rendus.
raw_textstringLe texte de l’IA brut, non traité (chemins de lecture et de streaming uniquement).
partsarrayBlocs structurés. type vaut text, thinking, web_search, web_fetch, code_execution, image_generation ou mcp_call, avec l’objet *_part correspondant. static vaut true lorsqu’il n’y a rien à déplier.
thinkingstring ou nullTexte de raisonnement des modèles de raisonnement.
isUserbooltrue pour les messages utilisateur, false pour ceux de l’IA.
statusstringthinking, generating ou finished.
error_text, error_id, error_codestring ou nullRenseignés en cas d’échec de la génération. error_id est une référence de support de 10 caractères ; error_code est un code machine propre à certaines catégories d’échec, comme les blocages liés aux données sensibles et les erreurs MCP.
mcp_approval_requestsarrayApprobations d’outils MCP en attente ou déjà décidées. Voir Approbation des outils MCP.
toolsarrayOutils activés pour ce tour.
citationsarrayCitations web et de bases de connaissances : url, file_path, title, info (kb_id, file_id, plages de pages et de lignes), citation_index, etc.
knowledge_base_ids, world_knowledgeuint[], boolBases de connaissances interrogées pour ce tour, et possibilité ou non pour le modèle d’aller au-delà.
response_duration_msint ou nullDurée de génération.
typestringnormal ou sub_conversation (conteneur d’une comparaison de modèles).
attachmentsarrayFichiers et transcriptions joints, chacun avec une première clé "type": "file" ou "type": "transcription". Toujours un tableau.

Statut. thinking signifie que la ligne du message de l’IA existe mais que rien n’est encore généré ; generating, que la sortie partielle arrive en streaming ; finished, que la génération est terminée. Un message de l’IA en échec est lui aussi finished, avec error_text et error_id renseignés : vérifiez donc ces champs, pas seulement status. Les messages utilisateur sont toujours finished.

Crédits. Chaque champ credits_used est un nombre décimal sérialisé sous forme de chaîne JSON, par exemple "0.4213".

Envoyer des messages

Corps de la requête

POST /conversation et POST /conversation/stream acceptent tous les champs ci-dessous. Les endpoints de suivi n’acceptent que les champs marqués « suivi » ; les autres sont ignorés sans avertissement. Côté validation, aucun champ n’est strictement obligatoire, mais envoyez toujours un message et un modèle.

Corps de la requête
message string facultatif
Le message de l’utilisateur. Mentionnez @<agent-tag> pour confier ce tour à l’un de vos agents. Démarrage et suivi.
attachments array facultatif
Éléments { "id": uint, "type": "file" | "transcription", "temp": bool }. Fichiers envoyés ou transcriptions terminées. temp: true utilise la pièce jointe pour ce tour seulement, sans l’enregistrer sur le message. Démarrage et suivi.
knowledge_base_ids uint[] facultatif
Bases de connaissances dont tirer le contexte pour ce tour. Démarrage et suivi.
knowledge_base_limit_node_ids uint[] facultatif
Limite la recherche à ces dossiers ou fichiers de base de connaissances. Démarrage et suivi.
world_knowledge bool facultatif par défaut false
false : réponse fondée uniquement sur la base de connaissances. true : la base de connaissances reste prioritaire, mais le modèle peut aussi recourir à la recherche web et à ses propres connaissances. Démarrage et suivi.
flash_mode bool facultatif
Demande le niveau de service prioritaire du fournisseur. N’a d’effet que sur les modèles qui le prennent en charge. Démarrage et suivi.
tools array facultatif
Éléments { "name", "use", "mcp_tool_id"? }. Voir Outils. Démarrage et suivi.
ai_model_id uint facultatif
Modèle à utiliser, issu de GET /ai_model. Ignoré si auto_model_type est défini. Démarrage uniquement.
auto_model_type string facultatif
best, balanced ou cheapest. Le routage automatique choisit le meilleur modèle pour chaque message et prime sur ai_model_id. Démarrage uniquement.
ai_style string facultatif par défaut prompty
Style de réponse : la valeur tone d’un style d’IA. Démarrage uniquement.
reasoning_effort string facultatif
Niveau de raisonnement des modèles de raisonnement. Une valeur non prise en charge est remplacée par la valeur par défaut du modèle ; pour les modèles sans raisonnement, la valeur enregistrée est null. Démarrage uniquement.
agent_id uint facultatif
Démarre un chat d’agent : le modèle, le prompt système, les outils, les serveurs MCP, les bases de connaissances et les fichiers de l’agent servent à chaque tour. L’agent doit vous appartenir. Démarrage uniquement.
invited_agent_ids uint[] facultatif
Ceux de vos agents que l’on peut mentionner (@mentioned) dans cette conversation. Ignoré lorsque agent_id est défini. Démarrage uniquement.
team_id uint facultatif
Crée une conversation d’équipe (l’équipe obtient l’accès chat_contribute). Voir Quand l’IA répond. Démarrage uniquement.
X-Project-ID integer dans header facultatif
Démarrage uniquement. Classe la conversation dans ce projet. Si le corps ne précise aucun modèle, c’est le modèle par défaut du projet qui est utilisé. À chaque tour, les fichiers du projet sont ajoutés au contexte. Le champ project_id existe dans le corps mais n’est pas lu : utilisez ce header.

Choix du modèle. auto_model_type l’emporte, puis ai_model_id, puis le modèle par défaut du projet (avec X-Project-ID). Si aucun ne s’applique, la conversation est tout de même créée, mais l’envoi échoue avec 503 {"error":"no AI model is selected, try reselecting model"} : envoyez donc toujours un modèle ou un type automatique au démarrage. Il n’existe pas d’ID de modèle littéral "auto". Les chats d’agent et les mentions @agent utilisent le modèle de l’agent (style prompty, sans niveau de raisonnement). Le modèle doit être autorisé par les règles d’accès aux modèles et de résidence des données de votre espace de travail, faute de quoi l’envoi échoue avec 503.

Outils

Chaque entrée a la forme { "name": ..., "use": true | false }. Noms possibles : web_search, image_generation, video_generation, speech_generation, music_generation, remote_mcp.

  • Un outil n’atteint le modèle que si celui-ci le déclare dans ses capacités tools. Sinon, il est ignoré sans avertissement.
  • remote_mcp exige aussi "mcp_tool_id": <id>, l’ID de l’un de vos serveurs MCP distants configurés. Ajoutez une entrée par serveur.
  • Sélection automatique des outils. Sur les modèles qui prennent en charge les outils, un petit classifieur peut activer web_search et image_generation (ainsi que l’exécution de code) en fonction du message, même sans demande de votre part. Envoyer une entrée pour un outil, même {"name":"web_search","use":false}, empêche le classifieur de l’ajouter. L’exécution de code relève uniquement du classifieur.

POST /conversation

POST /api/v1/conversation

Crée une conversation, envoie le premier message et attend la réponse complète de l’IA, appels d’outils compris. La réponse est le Message de l’IA terminé ; l’ID de la nouvelle conversation figure dans son conversation_context_id. La conversation reçoit un nom automatique après le premier échange. Accepte le corps de requête ci-dessus, ainsi que le header facultatif 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);

Réponse. 200 avec le Message de l’IA (voir l’objet Message). Lorsque l’IA n’est pas censée répondre (conversation d’équipe à plusieurs membres, sans mention @ai ni mention d’agent), le corps est à la place 200 {"conversation_context_id": 5120}.

Erreurs

StatutCorpsCas
400{"error": "..."}JSON mal formé.
400{"error": "...", "error_code": "..."}Blocage par les règles de votre espace de travail relatives aux données sensibles, ou erreur d’un serveur MCP. Lorsque l’IA ne répond pas, un blocage lié aux données sensibles utilise la clé code au lieu de error_code.
400{"error": "Knowledge base not found"}Base de connaissances inconnue.
429{"error": "Quota exceeded", "reason": "..."}Vérifié avant toute création. reason vaut credit_count, user_count, subscription, trial_expired ou unknown.
503{"error": "could not create conversation"}Par exemple un agent_id qui ne vous appartient pas ou un team_id inconnu (l’échec de la recherche d’équipe peut aussi se traduire par un 500).
503{"error": "<message>"}Tout autre échec : aucun modèle sélectionné, modèle soumis à restriction, erreur du fournisseur.

Si le fournisseur échoue en cours de génération, le message de l’IA est tout de même enregistré avec error_text et error_id, et POST /conversation/:id/retry permet de le relancer.

POST /conversation/stream

POST /api/v1/conversation/stream

Mêmes headers et même corps que POST /conversation, mais l’appel rend la main dès que le message utilisateur est enregistré. La génération se poursuit en arrière-plan et la réponse arrive par le WebSocket ou par interrogation périodique.

{ "conversation_context_id": 5120 }

Les erreurs détectées avant l’enregistrement du message utilisateur (quota 429, agent non valide, aucun modèle sélectionné, modèle soumis à restriction, blocage lié aux données sensibles) sont toujours renvoyées de façon synchrone, sous la même forme que ci-dessus. Les erreurs survenant après le 200 (choix du modèle ou des outils, récupération de la base de connaissances, erreur du fournisseur) ne sont signalées que par un événement WebSocket error_message et enregistrées sur un message de l’IA avec error_text et error_id.

Récupérer la réponse

  • WebSocket (recommandé). Connectez-vous à GET /conversation/{conversation_context_id}/ws. Les événements émis avant votre connexion sont mis en mémoire tampon et rejoués au premier client : en vous connectant juste après le 200, vous ne perdez rien.
  • Interrogation périodique (polling). Appelez GET /conversation/:id?page=1&size=2 et lisez le message de l’IA le plus récent (isUser: false, en tête de liste puisque l’ordre va du plus récent au plus ancien). Le message de l’IA est créé avec status: "thinking" puis enregistré à chaque fragment : vous voyez donc generating avec un text qui s’allonge, puis finished. Arrêtez-vous à finished et vérifiez error_id. Juste après le 200, le message de l’IA n’existe pas forcément encore ; si le message le plus récent est toujours le vôtre, la génération n’a pas commencé.

Le streaming n’est en temps réel que pour les modèles avec stream_support: true. Pour les autres, la réponse apparaît d’un seul bloc une fois terminée.

POST /conversation/:id

POST /api/v1/conversation/:id

Envoie un message de suivi et attend la réponse de l’IA. Le modèle, le style et le niveau de raisonnement proviennent des réglages next_message_* de la conversation ; modifiez-les d’abord avec PATCH /conversation/:id/model.

Paramètres de chemin
id string dans path obligatoire
ID ou slug de la conversation.
Corps de la requête
message string facultatif
Le texte du message de suivi.
attachments array facultatif
Comme dans le corps de requête.
knowledge_base_ids uint[] facultatif
Bases de connaissances pour ce tour.
knowledge_base_limit_node_ids uint[] facultatif
Limite la recherche à ces nœuds.
world_knowledge bool facultatif
Autorise des connaissances au-delà de la base de connaissances.
flash_mode bool facultatif
Niveau de service prioritaire, lorsqu’il est pris en charge.
tools array facultatif
Outils pour ce tour.
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." }'

Réponse. Identique à POST /conversation : 200 avec le Message de l’IA, ou 200 {"conversation_context_id": ...} lorsque l’IA ne répond pas. Si la conversation comporte une comparaison de modèles active, le message part vers chaque fil en parallèle et la réponse est un tableau JSON de Messages de l’IA, un par fil.

Erreurs. Comme pour POST /conversation, plus 404 {"error":"Conversation not found"}. Seuls le propriétaire et les utilisateurs ou équipes disposant de chat_contribute peuvent envoyer des messages ; les autres reçoivent 503 {"error":"you are not allowed to send messages in this conversation"}.

POST /conversation/:id/stream

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

Identique à POST /conversation/:id, mais renvoie immédiatement 200 {"conversation_context_id": <id>}. Suivez la progression par le WebSocket ou par interrogation périodique, comme pour POST /conversation/stream. En cas de comparaison de modèles active, chaque fil est diffusé sur le WebSocket de la conversation parente.

Paramètres de chemin
id string dans path obligatoire
ID ou slug de la conversation.

Quand l’IA répond

L’IA répond à un message dès que l’une de ces conditions est remplie :

  • un seul utilisateur dispose d’un accès en contribution à la conversation (le cas normal, y compris pour toute conversation créée avec une clé API sans team_id) ;
  • le texte contient @ai (sensible à la casse) ;
  • il s’agit d’un chat d’agent ;
  • le texte mentionne (@mentions) le tag d’un agent invité dans la conversation.

Sinon, le message est enregistré comme simple message utilisateur, visible des autres participants, et l’endpoint renvoie 200 {"conversation_context_id": <id>} sans appeler de modèle, même sur les endpoints sans streaming.

Les agents dans une conversation

  • Un agent_id fourni à la création fait de la conversation un chat d’agent. Son modèle ne peut plus être modifié ensuite.
  • Dans une conversation à un seul utilisateur, @tag confie ce tour à votre propre agent portant ce tag, ou à un agent partagé avec vous (vos propres agents l’emportent en cas de tag identique). Dans une conversation à plusieurs utilisateurs, seuls les agents invités avec invited_agent_ids ou POST /conversation/:id/agents peuvent être mentionnés.
  • L’agent qui répond est enregistré sur le message de l’IA (agent_id, agent).

Streaming

GET /conversation/:id/ws

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

Un WebSocket qui transmet chaque mise à jour de la conversation. Connectez-vous à wss://api.stickyprompts.com/api/v1/conversation/5120/ws et envoyez le header Authorization: Bearer STICKY-API-... avec la requête d’upgrade. Les objets WebSocket des navigateurs ne permettent pas de définir des headers : cette connexion est donc réservée aux clients côté serveur. Le serveur ignore tout ce que vous envoyez ; il ne lit le flux que pour en détecter la fermeture.

Paramètres de chemin
id integer dans path obligatoire
L’ID numérique de la conversation. Avec un slug, vous vous connectez à un autre flux, vide.

Chaque trame est une trame texte JSON {"type": <string>, "data": <payload>} :

typedataCas
messageMessage (instantané complet)Un nouveau message utilisateur d’un autre participant, et chaque mise à jour d’un message de l’IA pendant la génération. text, parts et thinking contiennent tout le contenu accumulé jusque-là, et non un delta : remplacez votre copie en vous fondant sur data.ID. status passe par thinking, generating, puis finished.
ai_message_endaucune (clé absente)Génération terminée, avec ou sans succès. Fin du tour.
error_messageMessage avec error_text, error_id, error_code facultatif, status: "finished"La génération ou sa préparation a échoué. Considérez-le comme final : un ai_message_end suit généralement les échecs de préparation, mais ce n’est pas garanti après une erreur du fournisseur en cours de génération.
ai_model_change{ "ai_model_id", "ai_style", "auto_model_type", "reasoning_effort" }Quelqu’un a appelé PATCH /conversation/:id/model.
run_dataObjet RunHistoryUniquement pour les tours pilotés par l’exécution d’un prompt enregistré, en plus des événements message. Voir Prompts et exécutions.
{"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"}
  • Vos propres messages utilisateur ne vous sont pas renvoyés en écho sur votre connexion. La clé agit au nom de son propriétaire : un client piloté par une clé ne voit donc que les messages de l’IA et ceux des autres personnes.
  • Tant qu’aucun client n’est connecté, les événements sont mis en mémoire tampon, rejoués à la connexion suivante, puis effacés. Un client qui se connecte longtemps après un tour peut d’abord recevoir des événements périmés de ce tour : ignorez les messages dont vous détenez déjà l’ID dans un état terminé.
  • Les flux résident dans la mémoire du serveur d’API. Si vous êtes connecté sans rien recevoir pour un tour que vous savez en cours, rabattez-vous sur l’interrogation périodique 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
Autorisation conversations_get

Un ancien endpoint Server-Sent Events. Utilisez plutôt le WebSocket ou l’interrogation périodique. Dans le code actuel, l’envoi de messages n’écrit que dans le WebSocket : ce flux ne transmet donc que les événements error_message et run_data liés aux erreurs d’exécution de prompts, et reste sinon ouvert sans envoyer le moindre contenu de message.

Paramètres de chemin
id integer dans path obligatoire
ID numérique de la conversation.

Il renvoie 404 {"error":"Stream not found"} lorsque le serveur ne détient aucun flux en mémoire pour la conversation. Sinon, il répond avec Content-Type: text/event-stream et des trames event:<name>\ndata:<json>\n\n, où <name> vaut message, error_message (tableau de {run_id, error_id, error_text, model_name}), run_data (tableau de RunHistory) ou close (data: Stream closed).

Lire les conversations

GET /conversation/:id

GET /api/v1/conversation/:id

Renvoie une conversation avec une page de ses messages, du plus récent au plus ancien. Les métadonnées de pagination figurent dans les headers de réponse X-Total-Count (nombre total de messages), X-Page et X-Page-Size.

Paramètres de chemin
id string dans path obligatoire
ID de la conversation, slug, ou slug de partage public de la conversation.
Paramètres de requête
page integer dans query facultatif par défaut 1
Numéro de page.
size integer dans query facultatif par défaut 10
Messages par page.

Accès. Le propriétaire ; les utilisateurs disposant de chat_view ou chat_contribute par partage ; ou, lorsque :id est le slug public d’une conversation partagée, toute personne autorisée par le type de partage (public : tout le monde, internal : tout utilisateur connecté, workspace : les membres de l’espace de travail du propriétaire).

{
  "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
}
ChampSignification
credits_usedCrédits de toute la conversation, fils de comparaison compris (chaîne décimale).
messagesCette page de messages, du plus récent au plus ancien. Dans les messages de l’IA, les citations sont rendues dans text et l’original figure dans raw_text.
next_message_ai_model_id, next_message_ai_modelLe modèle qu’utilisera le prochain tour. À défaut, celui du dernier message de l’IA.
next_message_ai_model_styleLe style du prochain tour. Attention : la clé diffère de next_message_ai_style dans l’objet conversation.
next_message_auto_model_typebest, balanced ou cheapest en cas de routage automatique.
forked_from_conversation_id, _slug, _nameRenseignés sur les conversations dérivées (forks).
has_sub_conversationsUne comparaison de modèles existe.
unsupported_filesFichiers joints précédemment que le prochain modèle ne sait pas lire.
current_user_permissionchat_contribute pour le propriétaire, sinon votre accès le plus élevé : chat_view, chat_contribute ou none.
team_shares{team_id, team, access_type} pour chaque équipe avec laquelle la conversation est partagée.

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

GET /conversation

GET /api/v1/conversation

Liste vos conversations de chat, de la plus récemment active à la plus ancienne. Seules les conversations de chat interactives comportant au moins un message y figurent.

Paramètres de requête
page integer dans query facultatif par défaut 1
Numéro de page. Les totaux figurent dans les headers X-Total-Count, X-Page et X-Page-Size.
size integer dans query facultatif par défaut 10
Éléments par page.
search string dans query facultatif
Recherche d’une sous-chaîne dans le texte de n’importe quel message ou dans le nom de la conversation.
agent_id uint dans query facultatif
Liste les chats de cet agent. Sans ce paramètre, les chats d’agent sont exclus.
team_id uint dans query facultatif
Liste les conversations partagées avec cette équipe avec chat_contribute. Sans ce paramètre, vous obtenez les conversations qui vous appartiennent et qui ne sont pas des conversations d’équipe.
X-Project-ID integer dans header facultatif
Uniquement les conversations de ce projet.

Réponse. 200 avec un tableau d’objets conversation (vide : []). Chacun inclut user et project préchargés, message_count, credits_used et, dans messages, uniquement le dernier message (avec attachments vidé). Notez les clés en camelCase publicSlug, isPublic, userId, projectId, runId, isInteractive et forkedFromConversationId, à côté des clés en 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
Autorisation conversations_get

Renvoie les prompts enregistrés distincts exécutés dans cette conversation, fils de comparaison compris : un tableau de { "id", "name", "slug", "ai_model_id", "user_id" }, ou []. L’ordre n’est pas défini.

Paramètres de chemin
id string dans path obligatoire
ID ou slug de la conversation.

Gérer les conversations

PATCH /conversation/:id

PATCH /api/v1/conversation/:id

Renomme une conversation et la place en tête de liste. Réservé au propriétaire.

Paramètres de chemin
id string dans path obligatoire
ID ou slug de la conversation.
Corps de la requête
name string obligatoire
Le nouveau nom. Vide ou absent, il provoque un 400.

Réponse. 200 avec l’objet conversation. Erreurs. 403 {"error":"You are not allowed to edit this conversation"}.

PATCH /conversation/:id/model

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

Définit le modèle, le style et le niveau de raisonnement des tours suivants. Autorisé pour le propriétaire et les utilisateurs disposant de chat_contribute. Diffuse aussi un événement WebSocket ai_model_change.

Paramètres de chemin
id string dans path obligatoire
ID ou slug de la conversation.
Corps de la requête
ai_model_id uint facultatif
Modèle des tours suivants. 400 {"error":"AI model not found"} s’il est inconnu.
ai_style string facultatif
Style des tours suivants.
auto_model_type string facultatif
best, balanced, cheapest ou null. Toujours écrit : l’omettre ou envoyer null désactive le routage automatique. Envoyez-le à chaque appel pour rester en routage automatique.
reasoning_effort string facultatif
Ajusté aux niveaux pris en charge par le nouveau modèle ou par le modèle actuel.

Réponse. 200 {"message":"Model changed successfully"}. Erreurs. 400 {"error":"Cannot change model for agent conversations"}, 403 si vous n’avez pas l’accès.

POST /conversation/:id/fork

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

Crée une nouvelle conversation nommée "Forked from <name>" contenant une copie de chaque message jusqu’à message_id inclus. Les pièces jointes, les outils et les enregistrements d’approbation MCP sont copiés, et les réglages de modèle, de style et de raisonnement sont conservés. La conversation source doit vous appartenir ou être partagée, sinon vous obtenez un 403.

Paramètres de chemin
id string dans path obligatoire
ID ou slug de la conversation source.
Corps de la requête
message_id uint obligatoire
Le dernier message à copier. Il doit s’agir d’un message normal de cette conversation ou de l’un de ses fils de comparaison (sinon 404 ou 400).

Réponse. 200 avec une réponse de conversation réduite : conversation_context_id, conversation_context_name, conversation_context_slug, public_slug, is_public, user_name, messages (ici du plus ancien au plus récent) et credits_used: "0". Les autres clés contiennent leur valeur zéro.

DELETE /conversation/:id

DELETE /api/v1/conversation/:id

Supprime de façon logique (soft delete) la conversation et ses messages, et masque ses exécutions de prompts dans l’historique des exécutions. Réservé au propriétaire.

Paramètres de chemin
id integer dans path obligatoire
ID numérique de la conversation.
curl -X DELETE https://api.stickyprompts.com/api/v1/conversation/5120 \
  -H "Authorization: Bearer $STICKY_API_KEY"

Réponse. 200 {"message":"Conversation deleted successfully"}. Erreurs. 404 {"error":"Conversation not found or you do not have permission to delete it"}.

POST /conversation/:id/retry

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

Relance le dernier tour lorsque le dernier message de l’IA s’est soldé par une erreur. Le message en échec est régénéré sur place (même ID) avec le modèle et le style actuels de la conversation, ou avec ceux de l’agent. Réservé au propriétaire. Sans corps.

Paramètres de chemin
id string dans path obligatoire
ID ou slug de la conversation.

Réponse. 200 avec :

  • pour un tour de chat ordinaire, un Message. Sur les modèles avec stream_support: true, l’appel rend la main aussitôt avec le message en status: "thinking", et le contenu arrive par le WebSocket ou par interrogation périodique ; sur les autres modèles, il renvoie le message terminé ;
  • pour un tour issu d’un prompt enregistré, un tableau d’objets RunHistory.

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

Lorsque le credits_used d’une conversation dépasse son expensive_warning_threshold (1000 par défaut), l’application web affiche un avertissement d’utilisation. Cet appel double le seuil jusqu’à ce qu’il dépasse l’utilisation actuelle. Réservé au propriétaire.

Paramètres de chemin
id string dans path obligatoire
ID ou slug de la conversation.

Réponse. 200 avec l’objet conversation et son nouveau expensive_warning_threshold. Erreurs. 400 si l’utilisation reste inférieure au seuil.

POST /conversation/:id/agents

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

Invite l’un de vos agents dans la conversation pour qu’il puisse être mentionné (@mentioned). Réservé au propriétaire ; impossible dans les chats d’agent (400).

Paramètres de chemin
id string dans path obligatoire
ID ou slug de la conversation.
Corps de la requête
agent_id uint obligatoire
Un agent qui vous appartient.

Réponse. 200 {"message":"Agent invited successfully"}. Erreurs. 404 {"error":"Agent not found or not owned by user"}, 403.

DELETE /conversation/:id/agents/:agentId

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

Retire un agent invité. Réservé au propriétaire.

Paramètres de chemin
id string dans path obligatoire
ID ou slug de la conversation.
agentId integer dans path obligatoire
ID numérique de l’agent.

Réponse. 200 {"message":"Agent removed successfully"}. Erreurs. 400 {"error":"Invalid agent ID"}, 404.

Approbation des outils MCP

Cette section s’applique lorsqu’un tour utilise un serveur MCP distant (un outil remote_mcp, ou un agent doté de serveurs MCP) dont la configuration comporte require_approval: true, et que le modèle fonctionne sur le protocole OpenAI Responses, le seul qui produise des demandes d’approbation. Les exécutions de workflows approuvent toujours automatiquement.

  1. Le modèle demande à appeler un outil MCP. La génération s’interrompt et le message de l’IA reçoit, dans mcp_approval_requests, une entrée par appel demandé. Sur le chemin de streaming, le texte Approval required for action: <name>, accompagné du libellé du serveur et des arguments, est aussi ajouté au texte du message.
  2. Chaque entrée utilise comme clés les noms de champs Go (sans tags JSON). Approved: null signifie que la demande est en attente ; true ou false, qu’elle a été tranchée.
{
  "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. Décidez avec POST /conversation/:id/approve, en utilisant l’ID numérique de l’entrée, et non ApprovalRequestID.
  2. Le serveur enregistre la décision et lance un nouveau tour de l’IA qui la transmet au modèle. La suite est un nouveau message de l’IA.

POST /conversation/:id/approve

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

Approuve ou rejette un appel d’outil MCP en attente.

Paramètres de chemin
id integer dans path obligatoire
ID numérique de la conversation.
Corps de la requête
approval_request_id uint obligatoire
L’ID de l’entrée dans mcp_approval_requests.
is_approved bool obligatoire
true pour approuver, false pour rejeter.
use_stream bool facultatif
true : rend la main aussitôt ; la suite arrive par le WebSocket ou par interrogation périodique. false : attend la fin de la suite.

Réponse. 200 avec la chaîne JSON "Successfully handled approval", et non un objet. Le message de suite n’est pas renvoyé : lisez-le avec GET /conversation/:id ou par le WebSocket.

Erreurs. 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 suite utilise le modèle fixe défini pour le prochain message de la conversation ; ce fonctionnement n’est pas pris en charge sur une conversation en routage automatique.

Comparaison de modèles

Une sous-conversation exécute le même message sur deux modèles ou plus, côte à côte, avec un « fil » par modèle. Tant qu’elle est active, les messages de suivi envoyés à la conversation parente partent vers chaque fil. Sa clôture désigne un fil gagnant, dont l’historique se poursuit dans la conversation parente. C’est le pendant API de Comparer les modèles.

POST /conversation/:id/sub-conversation

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

Lance une comparaison et attend la fin de chaque fil.

Paramètres de chemin
id string dans path obligatoire
ID ou slug de votre conversation (réservé au propriétaire). Les valeurs littérales null ou undefined créent d’abord une nouvelle conversation vide.
Corps de la requête
ai_model_ids uint[] obligatoire
Au moins 2 ID de modèles.
message string facultatif
Le message envoyé à chaque modèle.
prompt_instance_id uint facultatif
À la place de message, exécute une instance de prompt enregistré dans chaque fil.
attachments array facultatif
Utilisé uniquement avec prompt_instance_id.
flash_mode bool facultatif
Utilisé uniquement avec prompt_instance_id.
reasoning_effort string facultatif
Utilisé uniquement avec prompt_instance_id.

Le style est celui de la conversation, à défaut votre style préféré, à défaut prompty.

Réponse. 200 avec le Message conteneur (type: "sub_conversation"). Son sub_conversation contient parent_conversation_context_id, threads, selected_thread_id et is_active: true. Chaque fil comporte ai_model_id, ai_model, thread_conversation_context_id, thread_conversation_context (avec messages), unsupported_files et credits_used.

Erreurs. 400 avec moins de 2 modèles, 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 en streaming : mêmes paramètres de chemin et même corps, mais l’appel rend la main dès que les messages utilisateur existent. Les réponses des fils sont ensuite diffusées sur le WebSocket de la conversation parente, et, dans la réponse, les messages de chaque fil ne contiennent que le message utilisateur.

POST /sub-conversation/:id/close

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

Clôt une comparaison active dans une conversation qui vous appartient.

Paramètres de chemin
id integer dans path obligatoire
ID numérique de la sous-conversation (sub_conversation_id ou sub_conversation.ID).
Corps de la requête
ai_model_id uint obligatoire
Devient le modèle du prochain message de la conversation parente. Doit être supérieur à 0.
ai_style string facultatif
Devient le style de la conversation parente.
thread_id uint facultatif
Le fil gagnant. 0 signifie aucun.

Réponse. 204 No Content. Erreurs. 404 si la comparaison est introuvable ou inactive.

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

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

Change le fil dont les messages constituent l’historique de la conversation. La sous-conversation doit être clôturée ; réservé au propriétaire.

Paramètres de chemin
id integer dans path obligatoire
ID numérique de la sous-conversation.
Corps de la requête
thread_id uint obligatoire
Supérieur à 0.

Réponse. 204 No Content.

Partage

Les quatre endpoints de partage prennent l’ID numérique de la conversation et sont réservés au propriétaire (sinon 404 {"error":"Conversation not found"}).

POST /conversation/:id/share

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

Partage la conversation par lien. Génère un slug public de 32 caractères s’il n’en existe pas encore et définit isPublic: true. Lisez la conversation partagée avec GET /conversation/{public_slug}.

Paramètres de chemin
id integer dans path obligatoire
ID numérique de la conversation.
Corps de la requête
share_type string obligatoire
workspace (membres de votre espace de travail), internal (tout utilisateur StickyPrompts connecté disposant du lien) ou public (toute personne disposant du lien).

Réponse. 200 {"message":"Conversation shared successfully","publicSlug":"<32 chars>"}. Erreurs. 400 {"error":"Invalid share type"}.

POST /conversation/:id/revoke-share

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

Définit isPublic: false. Le slug est conservé. Sans corps.

Paramètres de chemin
id integer dans path obligatoire
ID numérique de la conversation.

Réponse. 200 {"message":"Conversation share successfully revoked"}.

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

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

Remplace le slug public pour que les anciens liens cessent de fonctionner. Le chemin s’écrit bien regenrate, exactement comme indiqué. Sans corps.

Paramètres de chemin
id integer dans path obligatoire
ID numérique de la conversation.

Réponse. 200 {"slug":"<new 32 chars>"}.

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

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

Partage la conversation en lecture seule avec vos équipes. La liste représente l’état souhaité complet pour vos équipes : toute équipe dont vous êtes membre et qui n’y figure pas perd le partage, sauf les équipes qui disposent déjà de chat_contribute (comme l’équipe pour laquelle une conversation d’équipe a été créée). L’appel garantit aussi l’existence d’un slug public.

Paramètres de chemin
id integer dans path obligatoire
ID numérique de la conversation.
Corps de la requête
shares array obligatoire
Éléments { "team_id": uint, "access_type": "chat_view" | "none" }. Seul chat_view peut être accordé ici ; chat_contribute est refusé avec 400.

Réponse. 200 {"message":"Conversation shared successfully with teams","publicSlug":"<32 chars>"}. Erreurs. 403 {"error":"You are not a member of team <name>"}, 404 pour les équipes inconnues.

Messages

POST /message/:id/export

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

Exporte un message dans un fichier DOCX ou PDF, soit sous forme de lien de téléchargement, soit directement dans une intégration de stockage connectée comme Google Drive. Vous devez être propriétaire de la conversation ou disposer au moins de chat_view sur celle-ci.

Paramètres de chemin
id integer dans path obligatoire
ID numérique du message.
Corps de la requête
format string obligatoire
docx ou pdf.
target string facultatif
Vide ou local : produit un fichier téléchargeable. Sinon, l’identifiant d’un fournisseur de stockage connecté, par exemple google_drive.
folder_id string facultatif
Dossier de destination chez le fournisseur. Obligatoire pour les cibles autres que locales.
name string facultatif
Nom du fichier pour les cibles de type fournisseur (l’extension est ajoutée). Par défaut, le titre de la conversation.

Réponse. 200 avec exactement l’un des deux corps suivants :

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

Erreurs. 400 {"error":"invalid format"}, 400 {"error":"invalid folder_id"}, 400 {"error":"unknown export target"}, 403 {"error":"you are not allowed to export this message"} ou un dossier de destination non accessible en écriture, 404 si le message est introuvable ou l’intégration non connectée, 409 {"error":"reconnect_required"}, 501 si le fournisseur ne permet pas l’envoi de fichiers, 500.