Un seul endpoint couvre le chat : vous envoyez un message utilisateur, AYETO exécute le modèle ou l'assistant (y compris les outils d'IA qu'il utilise, comme la recherche web ou la génération d'images), enregistre les deux messages dans une conversation et renvoie la réponse. La réponse est renvoyée sous la forme d'un seul objet message, ou sous la forme d'un stream si vous le demandez.
Envoyer un message
| Endpoint | POST /api/v3/chat |
| Portée | ayeto.chat |
| Limite de débit | par défaut |
| Réponse | Message, ou un stream lorsque stream vaut true |
Conversations
Chaque message appartient à une conversation, identifiée par conversation_id. C'est vous qui créez
l'id : générez un UUID (version 4) de votre côté et envoyez-le avec le premier message.
Si aucune conversation avec cet id n'existe encore, AYETO la crée ; si elle existe et appartient
à l'utilisateur de la clé, le message y est ajouté et le modèle voit les messages précédents.
La réponse ne contient pas l'id de la conversation. Si vous omettez conversation_id, une
nouvelle conversation est créée pour ce seul message et vous n'avez aucun moyen de la poursuivre :
envoyez donc toujours un id que vous conservez.
Une conversation reste liée à ce avec quoi elle a été créée :
- Assistant. Une conversation créée avec
assistant_idreçoit les réponses de cet assistant (ses instructions, ses outils, ses connaissances et son modèle) pendant toute sa durée de vie. Envoyer plus tard un autreassistant_idou unmodelne la fait pas passer à un autre assistant. Une conversation créée avec unmodelreste une simple conversation avec un modèle. - Organisation. Une conversation créée dans une organisation y reste. Un message de suivi qui ne précise aucune organisation s'exécute dans l'organisation de la conversation ; préciser une autre organisation est refusé (voir Organisations et crédits).
Pour lister, lire ou supprimer des conversations, utilisez l'API Conversations.
En-têtes de requête
| En-tête | Obligatoire | Description |
|---|---|---|
uni-api-key |
oui | Votre clé API. |
Content-Type |
oui | application/json |
language |
non | Le code de langue de l'utilisateur, par exemple EN, CZ ou FR. La langue est communiquée au modèle avec chaque message, afin qu'il puisse répondre dans cette langue. Par défaut : EN. |
Requête
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
message |
chaîne | oui | Le message de l'utilisateur, en Markdown ou en texte brut. Un message peut activer une compétence de l'assistant avec /skill-slug (voir ci-dessous). |
conversation_id |
UUID | non | Conversation à laquelle ajouter le message ; elle est créée si elle n'existe pas. Générez-le vous-même, voir Conversations. Par défaut : un nouvel id aléatoire. |
assistant_id |
UUID | l'un de assistant_id, model |
Assistant avec lequel discuter. Il doit s'agir de votre propre assistant ou d'un assistant partagé avec l'utilisateur de la clé. Lorsqu'il est défini, model est ignoré et c'est le modèle de l'assistant qui répond. |
model |
chaîne | l'un de assistant_id, model |
Id du modèle avec lequel discuter directement, sans assistant, par exemple gpt-5-mini. Les ids sont listés par Modèles et outils. Obligatoire lorsque assistant_id n'est pas défini. |
organization_id |
UUID | non | Exécute le message dans cette organisation : les crédits de l'organisation le paient et ses fichiers sont décomptés du stockage de l'organisation. L'utilisateur de la clé doit être administrateur ou membre (pas invité) de l'organisation. Par défaut : l'organisation de l'assistant lorsque l'assistant appartient à une organisation, sinon l'organisation de la conversation lorsque le message poursuit une conversation créée dans une organisation, sinon aucune (compte personnel). |
stream |
booléen | non | true renvoie la réponse sous forme de stream pendant sa génération. Par défaut : false. |
runner_version |
chaîne | non | Format du stream : "1" transmet du texte brut, "2" transmet des événements structurés (texte, raisonnement, activité des outils, statut). Utilisé uniquement lorsque stream vaut true. Par défaut : "1". Voir Streaming. |
attachments |
tableau de Pièce jointe | non | Fichiers envoyés avec le message (documents, images). |
max_tokens |
entier | non | Nombre maximal de tokens que le modèle peut générer par appel au modèle. Doit être compris entre 1 et le maximum propre au modèle. Par défaut : la valeur par défaut du modèle. |
dynamic_tools |
booléen | non | Conversations avec un modèle uniquement : donne au modèle l'ensemble standard d'outils d'IA (recherche web, génération d'images, rédaction de documents, etc.). Ignoré pour les assistants, dont les outils sont configurés sur l'assistant. Par défaut : false. |
use_vision |
booléen | non | Permet au modèle de regarder les images jointes (modèles dotés de la vision). Avec false, le modèle est informé des images jointes mais ne peut pas les regarder. Par défaut : true. |
relevant_history |
booléen | non | N'envoie au modèle que la partie de l'historique de la conversation pertinente pour le nouveau message, sélectionnée par un appel supplémentaire à un modèle. S'applique uniquement aux modèles qui le prennent en charge et aux conversations d'au moins 3 messages ; ignoré pour les modèles de raisonnement qui utilisent des outils. Par défaut : false. |
remove_tool_calls |
booléen | non | Retire les blocs d'appels d'outils du content de la réponse, pour ne garder que le texte propre au modèle. Ne s'applique pas aux streams. Par défaut : false. |
follow_options |
booléen | non | Demande au modèle de terminer sa réponse, lorsque c'est pertinent, par un bloc de code délimité étiqueté ayeto-follow-options : un objet JSON avec une question facultative, un multi_select facultatif et une liste d'options (label, prompt, description facultative) parmi lesquelles l'utilisateur peut choisir son message suivant. Utile uniquement si votre client l'affiche. Par défaut : false. |
Compétences de l'assistant. Un mot de la forme /slug (lettres minuscules, chiffres et
traits d'union, séparé par des espaces) dans message active la compétence de l'assistant ayant ce
slug, lorsque l'utilisateur de la clé en est propriétaire ou qu'elle est partagée avec lui et qu'elle appartient au même
contexte d'organisation que la requête. L'instruction et les outils de la compétence s'appliquent à ce
message. Les slugs inconnus sont laissés tels quels en texte brut.
Pièces jointes
Chaque pièce jointe est un fichier encodé dans le corps de la requête.
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
filename |
chaîne | oui | Nom du fichier avec son extension, par exemple report.pdf. |
data |
chaîne | oui | Contenu encodé en base64 sous forme de data URI, par exemple data:application/pdf;base64,JVBERi0xLjcK.... Le base64 brut sans le préfixe data:...;base64, est également accepté. Les espaces blancs, comme les sauts de ligne, dans le texte base64 sont ignorés ; tout autre caractère hors de l'alphabet base64, ou un padding incorrect, fait refuser la requête avec 422. |
mime_type |
chaîne | oui | Type MIME, par exemple application/pdf ou image/png. Pour les images, le type est détecté à partir du contenu (JPEG, PNG, WebP) et corrigé s'il diffère. |
La taille enregistrée est calculée à partir du contenu décodé. Un champ size envoyé par d'anciens clients
est ignoré.
Les pièces jointes sont enregistrées comme fichiers de l'utilisateur de la clé (ou de l'organisation, voir
organization_id) et conservées avec la conversation ; supprimer la conversation les supprime.
- Limite de taille. Chaque fichier est limité par la limite d'upload du déploiement, 50 Mo par défaut.
Un fichier plus volumineux est refusé avec
422 file is too large. - Quota de stockage. Les pièces jointes sont décomptées du quota de stockage de l'utilisateur ou de
l'organisation. Lorsqu'il est plein, la requête échoue avec
422 storage quota exceededou422 organization storage quota exceeded. - Vérifiées en premier, tout ou rien. Les pièces jointes sont décodées et enregistrées avant tout
autre élément de la requête. Lorsque l'une d'elles est refusée (base64 invalide, trop volumineuse, au-delà du
quota de stockage), la requête échoue avec
422, les pièces jointes de la même requête qui avaient déjà été enregistrées sont de nouveau supprimées et la conversation reste en l'état : le message utilisateur n'est pas ajouté et aucune nouvelle conversation n'est créée. Corrigez la pièce jointe et renvoyez le message avec le mêmeconversation_id. - Comment le modèle les lit. Le modèle est informé des fichiers joints et les lit
avec des outils intégrés : les documents sous forme de texte extrait, les images par la vision (lorsque le
modèle la prend en charge et que
use_visionvauttrue; les grandes images sont réduites pour le modèle). Les pièces jointes ne sont donc utiles qu'avec des modèles qui prennent en charge les outils d'IA. L'extraction de texte couvre les formats bureautiques, PDF et texte courants, voir Fichiers et médias.
Réponse
Sans stream, la réponse est la réponse de l'assistant au message.
| Champ | Type | Description |
|---|---|---|
id |
UUID | Id du message de réponse dans la conversation. |
timestamp |
horodatage | Date de création du message de réponse (millisecondes depuis l'epoch). |
role |
chaîne | assistant. |
content |
chaîne | La réponse en Markdown. Contient les blocs d'appels d'outils, sauf si remove_tool_calls vaut true. |
reasoning_content |
chaîne ou null | Le raisonnement du modèle ou le résumé de sa réflexion, pour les modèles qui l'exposent ; vide ou null sinon. |
model |
chaîne ou null | Id du modèle qui a produit la réponse. Pour un assistant qui utilise un modèle automatique, il s'agit du modèle effectivement choisi pour le message. |
attachments |
tableau de Pièce jointe de la réponse | Fichiers produits pour l'utilisateur pendant la réponse, comme les images générées. |
tool_runs |
objet | Réponses des sous-assistants appelés comme outils, voir Exécutions des sous-assistants. Objet vide lorsqu'il n'y en a pas. |
Pièces jointes de la réponse
| Champ | Type | Description |
|---|---|---|
filename |
chaîne | Nom du fichier. |
content_type |
chaîne | Type MIME, par exemple image/png. |
file_url |
chaîne | Lien qui télécharge le fichier. Il fonctionne sans clé API : traitez-le donc comme un secret. |
name |
chaîne ou null | Nom d'affichage facultatif. |
description |
chaîne ou null | Description facultative. |
Exécutions des sous-assistants
Un assistant peut appeler d'autres assistants comme outils. Ce qu'un tel sous-assistant a répondu est
renvoyé dans tool_runs, avec pour clé la position de l'appel d'outil parmi les blocs d'appels
d'outils de content, sous forme de chaîne : "0" est le premier appel d'outil, "1" le deuxième.
Seuls les appels d'outils auxquels un sous-assistant a répondu ont une entrée.
| Champ | Type | Description |
|---|---|---|
content |
chaîne | La réponse du sous-assistant, avec ses propres blocs d'appels d'outils. |
tool_runs |
objet | Exécutions au sein des propres appels d'outils du sous-assistant, avec la même structure. |
Appels d'outils dans la réponse
Lorsque le modèle utilise des outils d'IA pendant sa réponse, chaque appel d'outil est écrit dans content
sous la forme d'un bloc entre deux marqueurs fixes, suivi de la suite du texte du modèle :
Let me check the current exchange rate.
### calling AI tool ...
### Scraping URL: https://example.com/rates
Looking up today's EUR/CZK rate
Successfully scraped 1 page(s)
***
The current rate is 24.35 CZK per euro.
Le marqueur d'ouverture est exactement la chaîne "\n\n### calling AI tool ...\n" et le marqueur
de fermeture est "\n***\n". Entre les deux se trouve le texte de progression signalé par l'outil (Markdown).
Définissez remove_tool_calls à true pour obtenir content sans ces blocs, ou utilisez le
stream d'événements structuré, qui signale l'activité
des outils sous forme d'événements distincts.
Images et fichiers générés
Les outils qui créent des fichiers pour l'utilisateur (génération d'images, rédaction de documents) signalent le fichier de deux manières :
- le bloc d'appel d'outil dans
contentcontient un lien Markdown, pour une image; - les images générées sont listées dans
attachmentsde la réponse.
La génération d'images est disponible lorsque l'assistant dispose d'un outil de génération d'images, ou dans une
conversation avec un modèle lorsque dynamic_tools vaut true. Demandez une image dans message ;
il n'existe pas d'option distincte pour les images.
Organisations et crédits
Chaque appel au modèle est facturé en crédits, à l'utilisateur de la clé ou, lorsque la requête s'exécute dans
une organisation, sur le crédit de l'utilisateur dans cette organisation. Une requête s'exécute dans une
organisation lorsque organization_id est défini, lorsque l'assistant appartient à une
organisation, ou lorsqu'elle poursuit une conversation créée dans une organisation.
Une conversation créée dans une organisation y reste. Un message de suivi envoyé sans
organization_id (et sans assistant d'une autre organisation) s'exécute dans
l'organisation de la conversation : il y est facturé, ses pièces jointes sont décomptées du stockage de
l'organisation et l'utilisateur de la clé doit toujours en être administrateur ou membre. Un
message de suivi qui précise une autre organisation, directement ou via assistant_id, est
refusé avec 422 the conversation belongs to another organization avant que quoi que ce soit ne soit
enregistré ou facturé. Une conversation créée sans organisation n'est pas soumise à cette règle.
Avant chaque appel au modèle, AYETO estime son coût et refuse l'appel lorsque le solde
ne le couvre pas. Pendant la génération d'une réponse, celle-ci est interrompue lorsque son coût
dépasserait le solde. Dans les deux cas, l'erreur est 422 avec le détail
not enough user credit ou not enough organization credit. Les appels au modèle déjà
exécutés sont facturés, et une réponse interrompue en cours de route est conservée dans la conversation.
Voir Compte pour lire le solde de crédits.
Erreurs
Les erreurs sont renvoyées sous la forme {"detail": "..."} avec le statut HTTP ci-dessous. Lorsque stream vaut
true, les erreurs marquées pendant surviennent après le début du stream : le statut HTTP est
200 et l'erreur arrive sous la forme d'un chunk d'erreur avec le
même statut et le même détail. Les erreurs marquées avant sont renvoyées comme une réponse d'erreur normale dans
les deux modes.
| Statut | detail |
Moment | Cause |
|---|---|---|---|
401 |
API key is invalid |
avant | Voir Authentification. |
403 |
permission denied |
avant | L'utilisateur de la clé n'est pas autorisé à utiliser le chat, ou ne peut pas utiliser l'assistant. |
403 |
not shared with user |
avant | L'assistant n'appartient pas à l'utilisateur et n'est pas partagé avec lui. |
403 |
You are not allowed to chat in this conversation |
avant | conversation_id appartient à un autre utilisateur. |
403 |
User is not a member of the organization |
avant | organization_id (ou l'organisation de l'assistant, ou l'organisation de la conversation) ne fait pas partie des organisations de l'utilisateur. |
403 |
User does not have write permissions in the organization |
avant | L'utilisateur est invité dans l'organisation. |
404 |
Assistant not found |
avant | Aucun assistant avec assistant_id. |
404 |
model not found |
avant | Aucun modèle avec l'id indiqué dans model (ou le modèle de l'assistant). |
422 |
liste d'erreurs de champs | avant | Corps invalide, par exemple ni assistant_id ni model fourni (Either assistant_id or model is required). |
422 |
max_tokens must be between 1 and the maximum allowed tokens for the model |
avant | max_tokens hors limites. |
422 |
the conversation belongs to another organization |
avant | La conversation a été créée dans une organisation et la requête en précise une autre (organization_id, ou l'organisation de assistant_id). |
422 |
invalid base64 data in attachment '<filename>' |
avant | Le data d'une pièce jointe n'est pas du base64 valide. |
422 |
invalid data URI in attachment '<filename>' |
avant | Le data d'une pièce jointe commence par data: mais ne contient pas de , avant le contenu. |
422 |
file is too large |
avant | Une pièce jointe dépasse la limite d'upload. |
422 |
storage quota exceeded, organization storage quota exceeded |
avant | Une pièce jointe ne tient pas dans le quota de stockage. |
422 |
Assistant skill '/<slug>' requires unavailable tools: ... |
avant | La compétence activée a besoin d'un outil qui n'est pas disponible. |
422 |
model is deprecated |
avant | Le modèle (ou le modèle de l'assistant) est retiré. |
422 |
model is disabled |
avant | Le modèle (ou le modèle de l'assistant) est désactivé par l'administrateur. |
422 |
model is not available |
avant | Le modèle (ou le modèle de l'assistant) est un modèle automatique et aucun des modèles parmi lesquels il choisit n'est disponible. |
422 |
model does not have the required capability |
pendant | Le modèle n'est pas un modèle de chat. |
422 |
not enough user credit, not enough organization credit |
pendant | Voir Organisations et crédits. |
422 |
max iterations reached |
pendant | Le modèle a continué à appeler des outils au-delà du nombre de tours autorisé. |
403 |
llm: refusal from provider |
pendant | Le fournisseur du modèle a refusé de répondre. |
404 |
organization membership not found |
pendant | L'utilisateur a quitté l'organisation dans laquelle la requête s'exécute. |
429 |
message de limite de débit | avant | Trop de requêtes, voir Limites de débit. |
500 |
llm: error on provider side, llm: error in chat stream, llm: max token reached |
pendant | Le fournisseur du modèle a échoué, ou la réponse a atteint la limite de tokens en sortie. |
529 |
llm: provider overload error |
pendant | Le fournisseur du modèle est surchargé ; réessayez plus tard. |
500 |
An error occurred during chat processing |
pendant | Erreur inattendue (sans stream). |
Si une erreur interrompt une réponse, la partie générée jusque-là est conservée dans la conversation.
Exemples
Poser une question à un modèle et poursuivre la même conversation :
curl -X POST "https://ayeto.ai/api/v3/chat" \
-H "uni-api-key: $AYETO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"conversation_id": "3f6c1a9e-8b2d-4e57-9a1c-2d7e5b8f4a10",
"model": "gpt-5-mini",
"message": "Summarize the benefits of unit tests in three bullet points."
}'
{
"id": "b1e4c7d2-5a3f-4c89-8e6b-0f2a9d1c7e35",
"timestamp": 1759406412345,
"role": "assistant",
"content": "- **Catch regressions early** ...\n- **Document behaviour** ...\n- **Make refactoring safe** ...",
"reasoning_content": "",
"model": "gpt-5-mini",
"attachments": [],
"tool_runs": {}
}
curl -X POST "https://ayeto.ai/api/v3/chat" \
-H "uni-api-key: $AYETO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"conversation_id": "3f6c1a9e-8b2d-4e57-9a1c-2d7e5b8f4a10",
"model": "gpt-5-mini",
"message": "Now give an example for the second point in Python."
}'
Discuter avec un assistant dans une organisation et joindre un document :
curl -X POST "https://ayeto.ai/api/v3/chat" \
-H "uni-api-key: $AYETO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"conversation_id": "8d2f0b6a-1c4e-4f7a-b3d9-6e5a2c1f0b87",
"assistant_id": "c7a9e2f1-4b6d-4d38-9f0e-1a2b3c4d5e6f",
"organization_id": "5e8b1d3c-7a2f-4c6e-8d9b-0a1f2e3d4c5b",
"message": "List the payment terms in this contract.",
"remove_tool_calls": true,
"attachments": [
{
"filename": "contract.txt",
"data": "data:text/plain;base64,UGF5bWVudCBkdWUgd2l0aGluIDMwIGRheXMu",
"mime_type": "text/plain"
}
]
}'
Générer une image dans une conversation avec un modèle :
curl -X POST "https://ayeto.ai/api/v3/chat" \
-H "uni-api-key: $AYETO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"conversation_id": "0a7c3e5f-9b1d-4a26-8c4e-7f6d5b3a2c19",
"model": "gpt-5-mini",
"dynamic_tools": true,
"message": "Draw a watercolor lighthouse at sunset."
}'
{
"id": "e2d4f6a8-0b1c-4d3e-9f5a-7b6c8d9e0f12",
"timestamp": 1759406530112,
"role": "assistant",
"content": "\n\n### calling AI tool ...\n### Generating image\n...\n\n\n***\nHere is your watercolor lighthouse at sunset.",
"reasoning_content": "",
"model": "gpt-5-mini",
"attachments": [
{
"filename": "image.png",
"content_type": "image/png",
"file_url": "https://ayeto.ai/api/v1/public/file/read?id=9c1e...&secret=4f7a...",
"name": null,
"description": null
}
],
"tool_runs": {}
}
Python (requests) :
import base64
import os
import uuid
import requests
API = "https://ayeto.ai/api/v3"
HEADERS = {"uni-api-key": os.environ["AYETO_API_KEY"]}
conversation_id = str(uuid.uuid4()) # keep it to continue the conversation
def chat(message, attachments=None):
response = requests.post(
f"{API}/chat",
headers=HEADERS,
json={
"conversation_id": conversation_id,
"model": "gpt-5-mini",
"message": message,
"remove_tool_calls": True,
"attachments": attachments,
},
timeout=600, # answers that use tools can take minutes
)
if response.status_code != 200:
raise RuntimeError(f"{response.status_code}: {response.json()['detail']}")
return response.json()
with open("invoice.pdf", "rb") as f:
pdf = f.read()
answer = chat(
"What is the total amount of this invoice?",
attachments=[{
"filename": "invoice.pdf",
"data": "data:application/pdf;base64," + base64.b64encode(pdf).decode(),
"mime_type": "application/pdf",
}],
)
print(answer["content"])
print(chat("And the due date?")["content"])
JavaScript (fetch) :
const API = "https://ayeto.ai/api/v3";
const conversationId = crypto.randomUUID(); // keep it to continue the conversation
async function chat(message) {
const response = await fetch(`${API}/chat`, {
method: "POST",
headers: {
"uni-api-key": process.env.AYETO_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
conversation_id: conversationId,
assistant_id: "c7a9e2f1-4b6d-4d38-9f0e-1a2b3c4d5e6f",
message,
}),
});
const body = await response.json();
if (!response.ok) {
throw new Error(`${response.status}: ${JSON.stringify(body.detail)}`);
}
return body;
}
const answer = await chat("Draft a short reply to a customer asking about delivery times.");
console.log(answer.content);
Pour recevoir la réponse pendant sa génération, définissez "stream": true et lisez la réponse
comme décrit dans Streaming.