Confier une demande et lire la réponse
Écrire une demande à l'assistant d'un utilisateur, lire ses réponses — un texte, une liste, une question ou une proposition — et savoir quand il a terminé.
1Écrire la demande
D'abord, ce que l'utilisateur a sous les yeux
Une application qui montre des fiches — une application mobile, un panneau de votre logiciel — dit à l'assistant, juste avant la demande, ce que l'utilisateur regarde : l'assistant comprend alors « ce client » ou « relance-le » sans qu'on nomme la fiche, et la relit lui-même, avec les droits de l'utilisateur. Cette étape est facultative.
POST https://staging-api.docage.com/Assistant/Context
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…
Content-Type: application/json
{
"ClientMessageId": "00000000-0000-4000-8000-000000000702",
"ScreenTitle": "Camille Martin",
"RecordType": "Contact",
"RecordId": "00000000-0000-4000-8000-000000000708",
"RecordName": "Camille Martin"
}curl -X POST "https://staging-api.docage.com/Assistant/Context" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "Content-Type: application/json" \
-d '{
"ClientMessageId": "00000000-0000-4000-8000-000000000702",
"ScreenTitle": "Camille Martin",
"RecordType": "Contact",
"RecordId": "00000000-0000-4000-8000-000000000708",
"RecordName": "Camille Martin"
}'using HttpClient client = new();
using HttpRequestMessage request = new(HttpMethod.Post, "https://staging-api.docage.com/Assistant/Context");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
request.Content = new StringContent("""
{
"ClientMessageId": "00000000-0000-4000-8000-000000000702",
"ScreenTitle": "Camille Martin",
"RecordType": "Contact",
"RecordId": "00000000-0000-4000-8000-000000000708",
"RecordName": "Camille Martin"
}
""", Encoding.UTF8, "application/json");
using HttpResponseMessage response = await client.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(result);const response = await fetch("https://staging-api.docage.com/Assistant/Context", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"ClientMessageId": "00000000-0000-4000-8000-000000000702",
"ScreenTitle": "Camille Martin",
"RecordType": "Contact",
"RecordId": "00000000-0000-4000-8000-000000000708",
"RecordName": "Camille Martin"
})
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;ClientMessageId: celui de la demande qui suit. Le contexte ne sert qu'à elle, jamais à une demande que l'utilisateur écrit depuis une autre application. Sans lui, il sert à sa prochaine demande qui n'a pas le sien.RecordTypeetRecordIddésignent la fiche affichée, par le nom de son objet (« Contact », « Company », « PipelineItem ») ; pour une liste,RecordTypeetListName(« Mes contacts »), sansRecordId.ScreenTitleest le titre de l'écran.- Un contexte décrit l'écran, jamais ses données : l'assistant ne lit de la fiche que ce que l'utilisateur a le droit de voir.
- Il vaut deux minutes et ne sert qu'une fois ; un second dépôt pour la même demande remplace le premier.
422: il ne décrit rien, il désigne une fiche sans son type, ou il dépasse 8 000 caractères.
Puis la demande
POST https://staging-api.docage.com/Conversations/00000000-0000-4000-8000-000000000701/Messages
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…
Content-Type: application/x-www-form-urlencoded
bodyText=Quels%20sont%20mes%20rendez-vous%20de%20demain%20%3F&clientMessageId=00000000-0000-4000-8000-000000000702curl -X POST "https://staging-api.docage.com/Conversations/00000000-0000-4000-8000-000000000701/Messages" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d 'bodyText=Quels%20sont%20mes%20rendez-vous%20de%20demain%20%3F&clientMessageId=00000000-0000-4000-8000-000000000702'using HttpClient client = new();
using HttpRequestMessage request = new(HttpMethod.Post, "https://staging-api.docage.com/Conversations/00000000-0000-4000-8000-000000000701/Messages");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
request.Content = new StringContent("bodyText=Quels%20sont%20mes%20rendez-vous%20de%20demain%20%3F&clientMessageId=00000000-0000-4000-8000-000000000702", Encoding.UTF8, "application/x-www-form-urlencoded");
using HttpResponseMessage response = await client.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(result);const response = await fetch("https://staging-api.docage.com/Conversations/00000000-0000-4000-8000-000000000701/Messages", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/x-www-form-urlencoded"
},
body: "bodyText=Quels%20sont%20mes%20rendez-vous%20de%20demain%20%3F&clientMessageId=00000000-0000-4000-8000-000000000702"
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;Le corps est un formulaire (application/x-www-form-urlencoded ou multipart/form-data), pas du JSON :
bodyText: la demande, 10 000 caractères au plus ;clientMessageId(facultatif) : un identifiant que vous donnez au message — un renvoi du même message, après une coupure, ne le double pas, et ne relance pas l'assistant.
La réponse est votre message, enregistré — en voici un extrait :
{
"Id": "00000000-0000-4000-8000-000000000706",
"ClientMessageId": "00000000-0000-4000-8000-000000000702",
"BodyText": "Quels sont mes rendez-vous de demain ?"
}Gardez son Id : il désigne aussi le traitement de la demande (étape 3). L'assistant se met au travail aussitôt, et répond dans la
conversation : quelques secondes pour une question, davantage pour une tâche qui lit beaucoup de fiches.
2Lire ses réponses
GET https://staging-api.docage.com/Conversations/00000000-0000-4000-8000-000000000701/Messages?after=2026-11-09T09:15:02.000Z&take=50
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…curl "https://staging-api.docage.com/Conversations/00000000-0000-4000-8000-000000000701/Messages?after=2026-11-09T09:15:02.000Z&take=50" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…"using HttpClient client = new();
using HttpRequestMessage request = new(HttpMethod.Get, "https://staging-api.docage.com/Conversations/00000000-0000-4000-8000-000000000701/Messages?after=2026-11-09T09:15:02.000Z&take=50");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
using HttpResponseMessage response = await client.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(result);const response = await fetch("https://staging-api.docage.com/Conversations/00000000-0000-4000-8000-000000000701/Messages?after=2026-11-09T09:15:02.000Z&take=50", {
headers: {
Authorization: `Bearer ${token}`
}
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;after rend les messages postérieurs à cet instant (UTC), dans l'ordre ; sans lui, les derniers (take, 200 d'office, 500 au plus).
Une réponse de l'assistant porte Origin à 3, et sa carte (Card) :
{
"Id": "00000000-0000-4000-8000-000000000703",
"Origin": 3,
"AuthorName": "Assistant",
"OccurredAtUtc": "2026-11-09T09:15:09.412Z",
"BodyText": "Tu as **2 rendez-vous** demain : …",
"Card": {
"Kind": 1,
"Html": "<p>Tu as <strong>2 rendez-vous</strong> demain : …</p>",
"Receipt": "Aucune action",
"Records": [],
"ProposalId": null,
"Choices": []
}
}BodyText: la réponse en Markdown ;Card.Html, la même rendue en HTML par le serveur — à assainir encore avant de l'afficher dans une page.Card.Kinddit ce qu'est le message :
Kind |
Le message |
|---|---|
1 |
Une réponse. |
2 |
Une proposition : une action qui attend la décision de l'utilisateur — voir Accepter ou refuser une proposition. |
3 |
Une question : Choices propose des réponses ; répondez par un nouveau message. |
4 |
Un compte rendu : une limite atteinte, ce qui est fait et ce qui reste. |
5 |
Une erreur : l'assistant n'a pas pu répondre — fermé, indisponible —, le texte dit pourquoi. |
6 |
Une liste de fiches : ListTitle, ListTotal, les premiers noms (ListNames) et le filtre qui la compose (ListFilter). |
Card.Receipt, écrit par le serveur et non par l'assistant, dit ce que la demande a réellement fait : « Aucune action », ou « Fait : … » avec ce qui a changé, et les propositions en attente.Card.Records: les fiches créées ou modifiées —Created(vrai pour une fiche créée),Route(la route de son type : « Contacts »),Type(le nom de son type dans la langue de l'utilisateur, vide quand la route en sert plusieurs),Name,Id—, chacune à relire par la lecture de sa route (GET https://staging-api.docage.com/Contacts/{id}pour un contact).- Une liste (carte de genre
6) se rouvre par la lecture de sa route,ListRoute(« Contacts », « PipelineItems ») :ListFiltery passe tel quel enFilteret, pour des cartes ou des objets personnalisés,ListDefinitionIdenDefinitionId— voir Lire des listes. Elle rend les fiches que l'utilisateur voit aujourd'hui ;ListTotalest leur nombre au moment de la carte, etListEntityKeynomme le type (« Contact », ou un type de carte : « PipelineItem_TICKET »).
3Savoir que l'assistant a terminé
Lisez où en est le traitement de la demande, par l'Id de son message :
GET https://staging-api.docage.com/Assistant/Messages/00000000-0000-4000-8000-000000000706/Run
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…curl "https://staging-api.docage.com/Assistant/Messages/00000000-0000-4000-8000-000000000706/Run" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…"using HttpClient client = new();
using HttpRequestMessage request = new(HttpMethod.Get, "https://staging-api.docage.com/Assistant/Messages/00000000-0000-4000-8000-000000000706/Run");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
using HttpResponseMessage response = await client.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(result);const response = await fetch("https://staging-api.docage.com/Assistant/Messages/00000000-0000-4000-8000-000000000706/Run", {
headers: {
Authorization: `Bearer ${token}`
}
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;{
"RunId": "00000000-0000-4000-8000-000000000707",
"Status": 3,
"Working": false,
"PendingProposalIds": [],
"StartedAtUtc": "2026-11-09T09:15:03.120Z",
"FinishedAtUtc": "2026-11-09T09:15:09.530Z"
}Working: vrai tant que l'assistant travaille à la demande — interrogez de nouveau, toutes les deux ou trois secondes. Faux : il a rendu la main, et ses réponses sont dans la conversation (étape 2).Statusdit comment il l'a rendue :
Status |
Le traitement |
|---|---|
0 |
En attente de son tour : une autre demande de l'utilisateur passe avant. |
1 |
En cours. |
2 |
Il attend l'utilisateur : une proposition à décider, ou une question à laquelle répondre par un nouveau message. |
3 |
Terminé. |
4 |
En échec : une carte d'erreur (Kind 5) dit pourquoi, quand l'assistant a pu l'écrire. |
5 |
Interrompu par un redémarrage du service : rien ne reprend seul, écrivez de nouveau la demande. |
6 |
Annulé. |
PendingProposalIds: les propositions qui attendent la décision de l'utilisateur, les plus anciennes d'abord — voir Accepter ou refuser une proposition.
La réponse est null quand aucun traitement de l'utilisateur ne suit ce message : il n'est pas le sien, il n'est pas dans sa
conversation de l'assistant, ou un message plus récent a repris le traitement — interrogez alors par ce dernier.
Les pièges
- Un corps JSON n'est pas lu : la demande arriverait vide (
400, « Un message vide n'est pas envoyé »). - Dédoublonnez par
Id: avecafter, le dernier message déjà lu peut revenir. Une carte qui change — une proposition décidée — garde sonIdet sa date : relisez les derniers messages plutôt que les seuls nouveaux. - Sans contexte, l'assistant ne voit pas votre écran : déposez-le juste avant la demande (étape 1), ou nommez les fiches (« le contact Camille Martin », « le devis D-2026-0042 »). Il relit les vingt derniers messages de la conversation : une suite de demandes se comprend.
- Ni pièce jointe, ni message sans texte : la conversation de l'assistant n'accepte aucune pièce jointe, et seul un message qui porte du texte lui confie une demande. Une demande se dicte : voir Dicter un message.
- Une demande à la fois par utilisateur : une autre attend son tour (
Status0). Un message écrit pendant qu'une proposition ou une question attend reprend la demande en cours, comme une réponse : son traitement se lit alors par ce nouveau message. - Aucun webhook ne part de cette conversation : elle se lit par l'API, et la fin d'un traitement par l'étape 3.
- Une demande a ses bornes — quarante actions, vingt modifications, dix minutes — et l'organisation un plafond quotidien par utilisateur : atteints, l'assistant rend un compte rendu de ce qui est fait.
- Marquez la conversation lue (
POST https://staging-api.docage.com/Conversations/{id}/Read) une fois les réponses affichées : sinon elles restent non lues dans l'application. - L'utilisateur peut noter chaque réponse, comme avec le pouce de l'application : voir Noter une réponse de l'assistant.