Faire parler un agent DaleVoz depuis votre code, l'intégrer à votre site, prouver qui parle, lui donner des outils et le piloter depuis une IA. Chaque route décrite ici est celle que sert la production.
Toutes les surfaces de DaleVoz (le widget de votre site, les applications mobiles, vos serveurs) parlent à l'agent par la même route : POST https://dalevoz.ai/api/v1/interact. Une action entre (un message, un clic sur un bouton), des traces sortent (du texte, des boutons, des cartes), et la conversation vit sur nos serveurs.
Chaque appel porte une clé dans l'en-tête Authorization: Bearer …. Une clé appartient à un espace, pas à un agent : elle ouvre tous les agents de cet espace, et aucun autre. Le serveur n'en garde qu'une empreinte, elle ne s'affiche donc qu'une seule fois, à la création.
| Clé | Où elle vit | Ce qui la protège | Ce qu'elle ouvre |
|---|---|---|---|
pk_… | Dans le HTML de votre site ou dans une application : elle est publique par nature, comme une clé publiable de paiement. | La liste des domaines autorisés de la clé, comparée aux en-têtes Origin ou Referer de chaque appel. Un domaine ouvre aussi ses sous-domaines, et www. et le domaine nu valent l'un pour l'autre. Liste vide : aucune restriction. | Parler aux agents publiés : interact, messages, history, attachments, theme, voice, site. |
sk_… | Sur votre serveur seulement. Jamais dans un navigateur, une application ou un dépôt de code. | Son secret. Traitez-la comme un mot de passe. | Les routes de conversation sans contrôle d'origine (sauf voice, réservée aux clés pk_), plus l'administration : écrire un agent, sa base de connaissances, ses segments, lire les statistiques et exporter les conversations. Elle reçoit aussi le coût de chaque tour (usage). Une clé peut être restreinte à des portées choisies à sa création (agents:read, kb:write, interact…) : un appel hors portée répond 403 avec code « portee_manquante » et le nom de la portée dans porteeRequise. |
Où les créer.
sk_ : console, Réglages, onglet « ChatGPT et Claude », section repliée « Pour un développeur : clés d'API », bouton « Créer une clé serveur ». Réservé aux personnes qui administrent l'espace.pk_ : page de l'agent, onglet « Publier », carte « Site web », partie « Pour un technicien ». C'est là aussi que se règle la liste des domaines autorisés, et que se copie la balise du widget, clé déjà remplie.Remplacez la clé et le nom de l'agent. Le nom est celui du code d'intégration (data-agent), et l'identifiant de l'agent tel qu'il apparaît dans l'adresse de la console (/agents/…) est accepté aussi. L'API répond avec la version PUBLIÉE de l'agent : un agent jamais publié rend 404.
curl -X POST https://dalevoz.ai/api/v1/interact \
-H "Authorization: Bearer VOTRE_CLE_SERVEUR" \
-H "Content-Type: application/json" \
-d '{
"agent": "mon-agent",
"sessionId": null,
"user": { "id": "client-42", "locale": "fr" },
"action": { "type": "text", "payload": { "message": "Bonjour, quels sont vos horaires ?" } }
}'const reponse = await fetch("https://dalevoz.ai/api/v1/interact", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.DALEVOZ_CLE_SERVEUR}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
agent: "mon-agent",
sessionId: null, // null au premier message
user: { id: "client-42", locale: "fr" },
action: { type: "text", payload: { message: "Bonjour, quels sont vos horaires ?" } },
}),
});
const donnees = await reponse.json();
if (!reponse.ok) throw new Error(donnees.error);
// À renvoyer au tour suivant pour continuer la même conversation
const sessionId = donnees.sessionId;
for (const trace of donnees.traces) {
if (trace.type === "text") console.log(trace.payload.message);
}import os
import requests
reponse = requests.post(
"https://dalevoz.ai/api/v1/interact",
headers={"Authorization": f"Bearer {os.environ['DALEVOZ_CLE_SERVEUR']}"},
json={
"agent": "mon-agent",
"sessionId": None, # None au premier message
"user": {"id": "client-42", "locale": "fr"},
"action": {"type": "text", "payload": {"message": "Bonjour, quels sont vos horaires ?"}},
},
timeout=60,
)
donnees = reponse.json()
reponse.raise_for_status()
session_id = donnees["sessionId"] # à renvoyer au tour suivant
for trace in donnees["traces"]:
if trace["type"] == "text":
print(trace["payload"]["message"])La réponse :
{
"sessionId": "0b6f7c1e-5d3a-4f1b-9a57-2c8e4d1a9f30",
"traces": [
{ "type": "text", "payload": { "message": "Nous sommes ouverts du mardi au samedi, de 9 h à 19 h.", "markdown": true } },
{ "type": "choice", "payload": { "buttons": [ { "label": "📅 Prendre rendez-vous", "value": "rdv" } ] } }
],
"usage": { "tokensIn": 2140, "tokensOut": 58, "costMicroUsd": 812 }
}sessionId : envoyez null au premier message, puis renvoyez celui de la réponse pour continuer la même conversation. Un identifiant inconnu ou d'un autre espace ne fait pas d'erreur : il ouvre une conversation neuve, et la réponse porte le nouvel identifiant.user.id : votre identifiant pour cette personne. Le même d'un jour à l'autre permet à l'agent de la reconnaître quand sa mémoire est activée. Avec une clé pk_, la mémoire demande en plus la signature d'identité (voir Identité signée). Jamais de secret dans ce champ.usage : présent seulement avec une clé sk_. Une clé pk_ ne le reçoit jamais, ni en JSON ni en flux.La description OpenAPI 3.1 des mêmes routes se télécharge ici : /openapi.json.
https://dalevoz.ai. Corps en JSON (Content-Type: application/json), sauf la dictée vocale, en multipart.Authorization: Bearer pk_… ou Bearer sk_…. Seule la route theme accepte aussi la clé en paramètre (?key=).slug), dans l'espace de la clé. Seule interact accepte aussi son identifiant (UUID).{ "error": "…" }, parfois complété de issues (détail de validation), code et detail. Sur les routes que le widget affiche au visiteur (interact, messages, history, attachments, voice), error est une phrase pour le visiteur, dans sa langue : pour un programme, lisez le statut HTTP et code.Refus d'origine d'une clé pk_ (statut 403) :
{
"error": "(phrase neutre pour le visiteur, dans sa langue)",
"code": "origine_non_autorisee",
"detail": "Origine non autorisée pour cette clé (boutique.exemple.fr). Ajoutez ce domaine à la clé dans la console."
}Les routes site/* rendent ce refus sous une forme plus courte, sans code : { "error": "Origine non autorisee pour cette cle" }.
Clés : pk_ (origine vérifiée) ou sk_
Fait avancer une conversation d'un tour. C'est la route du widget, des applications et des serveurs.
| En-tête | Rôle |
|---|---|
Authorization | Bearer pk_… ou sk_…, obligatoire. |
x-dalevoz-channel | Facultatif. Le canal, pour les statistiques : widget, iframe, sdk_js, ios, android, api, zendesk, playground, whatsapp, messenger, instagram, voice. Il prime sur channel du corps. Sans lui : widget pour une pk_, api pour une sk_. |
x-dalevoz-identity | Facultatif. Le jeton d'identité signé, voir Identité signée. |
| Champ du corps | Type | Rôle |
|---|---|---|
agent | string | Obligatoire. Nom technique ou identifiant de l'agent. |
sessionId | uuid | null | null au premier tour, puis celui de la réponse. Inconnu : une conversation neuve s'ouvre, sans erreur. |
action | object | Obligatoire. Voir le tableau suivant. |
user | { id?, name?, locale? } | Qui parle, déclaré par vous. locale choisit la langue des messages de service. Jamais de secret ici. |
context | object | Contexte libre, FIGÉ à la création de la conversation (provenance, version d'application…). context.segment choisit un segment de l'agent. |
contexte | object | Ce que la page sait du visiteur, FUSIONNÉ à chaque tour. Seules les variables déclarées sur l'agent (onglet Contexte) passent : une clé non déclarée est ignorée, un texte est tronqué. |
stream | boolean | true : réponse en flux text/event-stream (voir plus bas). |
consentement | date ISO | Quand le visiteur a accepté le bandeau de consentement du widget. Posée une fois sur la conversation, rendue par l'export RGPD. Une date future ou de plus de 400 jours est remplacée par l'heure du serveur. Le widget l'envoie seul. |
| action.type | payload | Quand |
|---|---|---|
launch | {} | Ouvrir ou reprendre une conversation : l'agent dit son accueil. |
text | { message, attachments?, parle? } | Un message (8 000 caractères au plus, vide permis s'il y a des pièces jointes). attachments : 5 au plus. parle : true si le message est la transcription d'une parole, la réponse est alors rédigée pour être lue à voix haute. |
choice | { value, label? } | Le clic sur un bouton : renvoyez la value du bouton, et son label pour l'historique. |
event | { name, data? } | Un événement de la surface. Le widget s'en sert pour window.dalevoz.evenement (name « dv:evenement »). |
Une pièce jointe est l'une des deux formes suivantes :
{ kind: "image", mediaType, data, filename? } : mediaType image/png, image/jpeg, image/webp ou image/gif ; data en base64 SANS le préfixe data:. L'image part telle quelle au modèle.{ kind: "text", filename, text, truncated? } : le texte d'un PDF ou d'un DOCX, extrait au préalable par /api/v1/attachments.Les pièces jointes ne sont lues que si l'agent les accepte (réglage de l'agent) ; le thème public le dit dans attachmentsEnabled.
Réponse (200). { sessionId, traces[], usage?, avertissements? }. Les traces sont à afficher dans l'ordre. Leur type :
| type | Contenu |
|---|---|
| text | payload.message (Markdown si payload.markdown vaut true). |
| choice | payload.buttons[] : { label, value, url? }. Avec url, le bouton ouvre le lien au lieu d'envoyer value. |
| multi_choice | Plusieurs boutons à cocher (min, max, validateLabel). |
| slider | Un curseur (label, min, max, step, value?, unit, validateLabel). |
| card | Une fiche : title, description?, imageUrl?, buttons?… |
| carousel | payload.cards[] : plusieurs fiches. |
| document | Un fichier à ouvrir : url, filename, caption?. |
| audio | Une note vocale : url, transcript, seconds?, mime. |
| scheduler | Un agenda à intégrer : url, label, height. |
| template | Un modèle de message WhatsApp (canaux Meta). |
| handoff | L'agent passe la main à un humain : reason?, message?. |
| systeme | Un changement d'interlocuteur : etat (attente, repris, pause, absent) et message. |
| end | La conversation est terminée. |
Flux (stream: true). La réponse est un text/event-stream de lignes « data: {json} ». Ordre : session (l'identifiant, avant tout le reste), meta (streaming : true si le texte arrive vraiment mot à mot), des delta (texte provisoire à afficher), des tool (une catégorie : recherche, web, cartes, action ou vocal, jamais le nom de l'outil) et enfin final, dont response est IDENTIQUE à la réponse JSON : remplacez le texte provisoire par final.response.traces. Une fois le flux ouvert, le statut HTTP est 200 : une erreur arrive comme événement error { message, status? }.
curl -N -X POST https://dalevoz.ai/api/v1/interact \
-H "Authorization: Bearer VOTRE_CLE_SERVEUR" \
-H "Content-Type: application/json" \
-d '{ "agent": "mon-agent", "sessionId": null,
"action": { "type": "text", "payload": { "message": "Bonjour" } },
"stream": true }'
data: {"type":"session","sessionId":"0b6f7c1e-5d3a-4f1b-9a57-2c8e4d1a9f30"}
data: {"type":"meta","streaming":true}
data: {"type":"delta","text":"Bonjour ! "}
data: {"type":"tool","kind":"recherche","phase":"start"}
data: {"type":"tool","kind":"recherche","phase":"done"}
data: {"type":"delta","text":"Nous sommes ouverts…"}
data: {"type":"final","response":{"sessionId":"0b6f7c1e-…","traces":[…]}}Erreurs.
| Statut | Cause |
|---|---|
| 400 | Corps JSON invalide, ou requête invalide (issues détaille). |
| 401 | Clé absente, invalide ou révoquée ; jeton d'identité refusé ; clé qui exige une identité signée et reçoit user.id. |
| 403 | Clé pk_ appelée depuis un domaine qui n'est pas dans sa liste. |
| 404 | Agent inconnu, ou jamais publié. |
| 423 | Agent en pause. |
| 429 | Trop d'appels : plafond de la clé ou du visiteur (voir Limites). error est une phrase pour le visiteur, code vaut limite, reessayerDansS et l'en-tête Retry-After disent quand réessayer. |
| 500 | Erreur inattendue (message pour le visiteur, dans sa langue). |
| 502 | Le modèle a coupé sa réponse sans résultat (flux). |
Clés : pk_ (origine vérifiée) ou sk_
Ce qu'un conseiller humain a écrit depuis le dernier appel, pour l'afficher sur votre surface pendant qu'il a la main. À sonder toutes les quelques secondes, en renvoyant cursor tel quel dans since.
{
"status": "human",
"messages": [ { "id": "…", "at": "2026-09-23T10:12:04.512Z", "from": "human_agent", "agentName": "Julie", "text": "…" } ],
"events": [ { "id": "…", "at": "…", "etat": "repris", "message": "…" } ],
"questions": [ { "id": "…", "at": "…", "traces": [ { "type": "text", … }, { "type": "choice", … } ] } ],
"cursor": "2026-09-23T10:12:04.513Z"
}let curseur = "";
setInterval(async () => {
const url = new URL("https://dalevoz.ai/api/v1/messages");
url.searchParams.set("sessionId", sessionId);
if (curseur) url.searchParams.set("since", curseur);
const r = await fetch(url, { headers: { Authorization: `Bearer ${cle}` } }).then((x) => x.json());
for (const m of r.messages) afficherConseiller(m.agentName, m.text); // dédoublonner par m.id
curseur = r.cursor ?? curseur;
}, 4000);Clés : pk_ (origine vérifiée) ou sk_
Le fil d'une conversation, pour le réafficher quand la personne revient. Rend { items: [ { from: "user" | "bot" | "human", agentName?, traces[] } ] }, 60 éléments au plus, les plus récents. Les boutons ne sont rendus que sur le dernier élément ; les signaux (handoff, end) ne sont pas rejoués. Session inconnue, d'un autre espace ou mal formée : items vide, jamais une erreur. Une clé absente ou invalide rend 401.
Clés : pk_ (origine vérifiée) ou sk_
Extrait le texte d'un PDF ou d'un DOCX. Corps : { mediaType, data, filename? }, avec mediaType application/pdf ou application/vnd.openxmlformats-officedocument.wordprocessingml.document, data en base64 (15 000 000 caractères au plus, environ 11 Mo de fichier). Réponse : { filename, text, truncated }, le texte étant coupé à 20 000 caractères. Erreurs : 400 (type non pris en charge, fichier manquant ou trop lourd), 401, 403, 422 (extraction impossible). Les images ne passent pas par ici : elles vont directement dans interact.
// 1. Extraire le texte du PDF
const extrait = await fetch("https://dalevoz.ai/api/v1/attachments", {
method: "POST",
headers: { Authorization: `Bearer ${cle}`, "Content-Type": "application/json" },
body: JSON.stringify({ mediaType: "application/pdf", data: base64SansPrefixe, filename: "devis.pdf" }),
}).then((r) => r.json());
// 2. L'envoyer avec le message
await fetch("https://dalevoz.ai/api/v1/interact", {
method: "POST",
headers: { Authorization: `Bearer ${cle}`, "Content-Type": "application/json" },
body: JSON.stringify({
agent: "mon-agent",
sessionId,
action: {
type: "text",
payload: {
message: "Pouvez-vous vérifier ce devis ?",
attachments: [{ kind: "text", filename: extrait.filename, text: extrait.text, truncated: extrait.truncated }],
},
},
}),
});Les routes de l'appui pour parler du widget. Elles utilisent la chaîne vocale réglée sur l'agent publié.
Clé : pk_ uniquement (une sk_ reçoit 401)
Transcrit un tour parlé. Corps multipart : agent, audio (le fichier), dureeMs?, locale?. Réponse : { texte, langue, secondes }, ou { texte: "", vide: true } quand rien n'a été entendu. Limites : 8 Mo, 120 secondes, 20 appels par minute et par espace. Erreurs : 400, 401, 403, 404 (agent introuvable ou pas publié), 413 (audio trop lourd ou trop long), 429, 503 (écoute non configurée).
Clé : pk_ uniquement (une sk_ reçoit 401)
Lit un texte à voix haute avec la voix de l'agent. Corps : { agent, texte, conversationId?, locale? }. Réponse : les octets audio (Content-Type donné par le fournisseur). Le texte est coupé à 1 200 caractères. 30 appels par minute et par espace. Erreurs : 400, 401, 403, 404, 429, 503 (lecture non configurée).
Sans clé : adresse publique
L'audio d'une note vocale de l'agent, l'adresse que porte une trace audio. Publique parce qu'une balise audio et les serveurs de Meta ne savent pas poser d'en-tête. La note expire au bout de trente jours ; le cache est privé, une heure.
Clés : pk_ ou sk_, en en-tête ou en ?key=
Le thème public de la version publiée d'un agent, déjà résolu. Réponse : { resolved, agentName, title, textIdleMs, attachmentsEnabled, voiceMode, pttSilenceMs, conformite, credit }. Pas de contrôle d'origine. Mise en cache publique (60 s, 300 s côté CDN). Erreurs : 401 (clé ou agent manquant), 404.
Sans clé : adresse publique
Une image de la bibliothèque d'un espace (avatar, fond du widget). Cache immuable d'un an ; 404 not_found sinon.
Écrire un agent et sa base, lire ses résultats, depuis votre serveur. Chaque écriture est inscrite au journal de l'agent, avec le motif que vous donnez.
Clé : sk_ uniquement (une pk_ reçoit 403)
Crée l'agent, ou le réécrit s'il existe déjà un agent de ce slug dans l'espace. Réponse : { slug, cree, id }.
| Champ | Règle |
|---|---|
| slug | Obligatoire. 2 à 60 caractères : minuscules, chiffres, tirets, commençant par une lettre ou un chiffre. |
| name | Obligatoire, 120 caractères au plus. |
| systemPrompt | Obligatoire, 60 000 caractères au plus. |
| llmProvider | anthropic | openai | grok | gemini |
| llmModel | Refusé (400) sur l'offre gratuite s'il ne fait pas partie des modèles à 1 crédit ou moins ; contrôlé seulement quand il change. |
| localeDefault | 2 à 5 caractères (fr, es…). |
| status | draft | live | paused |
| maxTokens | 256 à 32 000. Absent, il est remis à 4 096, y compris sur une réécriture. |
| greetingByLocale | Accueil par langue. S'il porte des boutons, il doit finir sur une question fermée. |
| launchButtonsByLocale | Boutons d'ouverture par langue : 2 à 5, chacun commence par UN emoji, 20 caractères au plus. |
| settings | Fusionné dans les réglages existants, jamais remplacé. |
| motif | Pourquoi cette écriture, 500 caractères au plus. Journalisé. |
{
"slug": "accueil-boutique",
"name": "Accueil de la boutique",
"systemPrompt": "Tu es l'assistant de la boutique…",
"localeDefault": "fr",
"status": "draft",
"greetingByLocale": { "fr": "Bonjour ! On commence ?" },
"launchButtonsByLocale": { "fr": [ { "label": "🛍️ Produits" }, { "label": "🚚 Livraison" } ] },
"motif": "Création depuis notre back-office"
}name et systemPrompt sont exigés à chaque appel, même pour une réécriture. Erreurs : 400 (validation, règle de forme de l'accueil ou des boutons, modèle hors offre), 401, 403 (pk_), 500.
Clé : sk_ uniquement (une pk_ reçoit 403)
Un segment adapte un agent à un public (une organisation, une campagne) sans le dupliquer. Il est choisi à l'exécution par context.segment dans interact, ou par l'attribut data-segment du widget ; sans segment reconnu, c'est le segment default qui s'applique, puis l'agent tel quel.
GET ?agent= : la liste, sans le contenu des prompts : { agent, segments: [ { segment, champs, promptFragmentLongueur, systemPromptLongueur } ] }.POST { agent, segment, config, autoriserDefault?, autoriserVocal? } : crée ou remplace. Réponse { agent, segment, champs }.DELETE ?agent=&segment= : supprime. Réponse { agent, segment, supprime: true }.Champs de config (tout autre champ rend 400) : collections, carousels, carouselsDisabled, tools, promptFragment (ajouté au prompt, 20 000 car.), systemPrompt (remplace le prompt, 20 000 car.), greeting, launchButtons (6 au plus), welcomeVideoUrl, welcomeVoiceUrl.
Clé : sk_ uniquement (une pk_ reçoit 403)
Synchronise des lignes dans la base de connaissances : une ligne devient une fiche. Corps : { agent, collection?, rows[], motif? }, rows de 1 à 2 000 éléments { name, text, sourceUrl? }.
{
"agent": "accueil-boutique",
"collection": "catalogue",
"rows": [
{ "name": "Vase Olive, 24 cm", "text": "Grès émaillé, 39 €, en stock.", "sourceUrl": "https://exemple.fr/vase-olive" }
],
"motif": "Synchronisation nocturne du catalogue"
}Clé : sk_ uniquement (une pk_ reçoit 403)
Relit les fiches d'un agent, ou d'un seul lot : { agent, collection, count, rows: [ { name, sourceUrl, text } ] }. À appeler avant un sync-table partiel : lisez, remplacez ce que votre source possède, renvoyez le tout.
Clé : sk_ uniquement (une pk_ reçoit 403)
Les résultats d'un agent sur une période. Paramètres :
| Paramètre | Rôle |
|---|---|
| agent | Obligatoire (slug). |
| days | 1 à 90, 7 par défaut, compté depuis maintenant. |
| from, to | Dates ISO ; une fenêtre calendaire qui prime sur days. from doit précéder to. |
| channels | Canaux à garder, séparés par des virgules. |
| excludePrefix | Préfixes d'identifiant visiteur à écarter (vos comptes de test), séparés par des virgules. |
Réponse : agent, periode, tronque, totaux, fichesOuvertes, questionsSansFiche, demandes, rendezVous, pagesOrigine, satisfaction, sansIssue, ventes?, langues, canaux, joursActivite, heuresActivite, conversations. Erreurs : 400 (agent manquant, date invalide), 401, 403, 404.
Clé : sk_ uniquement (une pk_ reçoit 403)
L'export brut des conversations d'un agent, tours compris, tels qu'ils sont en base. from et to sont obligatoires ; channels et excludePrefix comme ci-dessus ; limit de 1 à 200 (100 par défaut) ; cursor pour la page suivante.
{
"agent": "accueil-boutique",
"periode": { "from": "2026-09-01T00:00:00.000Z", "to": "2026-09-08T00:00:00.000Z" },
"conversations": [
{ "id": "…", "createdAt": "…", "updatedAt": "…", "channel": "widget", "status": "…", "locale": "fr",
"externalUserId": "client-42", "issue": "…", "satisfaction": "…", "sujet": "…", "classifieAt": "…",
"turns": [ { "role": "user", "content": { … }, "createdAt": "…" } ] }
],
"nextCursor": "2026-09-03T17:42:10.118Z"
}Tant que nextCursor n'est pas null, repassez-le en cursor avec les mêmes from et to. Erreurs : 400, 401, 403, 404.
Trois routes conçues pour les pages d'atterrissage : le visiteur donne l'adresse de son site, l'agent la lit, puis la conversation peut continuer sur WhatsApp. Elles n'ont de sens que sur un agent où la lecture de site est activée.
Clés : pk_ (origine vérifiée) ou sk_
Corps : { agent, url, locale?, segment?, contexte? }. Vérifie en quelques secondes que le site répond, crée la conversation et rend { sessionId, statut: "encours", url } ; la lecture (8 pages au plus) continue après la réponse. Six lectures par heure et par adresse IP. Erreurs : 400 (adresse-invalide), 401, 403 (origine, ou lecture-desactivee sur l'agent), 404 (agent-inconnu), 422 (site-injoignable), 429 (trop-de-lectures).
Clés : pk_ (origine vérifiée) ou sk_
À sonder pendant la lecture : { statut, url?, pages?, titre?, message? }, statut valant aucune, encours, pret ou echec. Une lecture en cours depuis plus de deux minutes est rendue en echec. Erreurs : 400, 401, 403, 404 (session-inconnue).
Clés : pk_ (origine vérifiée) ou sk_
Corps : { sessionId, telephone }. L'agent écrit le premier sur WhatsApp, en connaissant déjà le site lu. Il faut un canal WhatsApp actif sur l'agent ; le message part par un modèle approuvé par Meta, en français. Un numéro sans indicatif est lu comme français. Un seul envoi par conversation, trois par heure et par adresse IP. Réponse : { ok: true, waId } ou { ok: true, deja: true }. Erreurs : 400 (numero-invalide), 401, 403, 404 (session-inconnue), 409 (site-non-lu), 429 (trop-d-envois), 502 (envoi-refuse), 503 (canal-absent, canal-sans-jeton).
Pour répondre à une demande d'accès ou d'effacement depuis votre serveur. Le visiteur est désigné par son identifiant : le user.id que vous envoyez, le sub du jeton signé, l'identifiant passé à dalevoz.identify(), ou le numéro WhatsApp. Tout est borné à l'espace de la clé : deux espaces qui ont chacun leur « client-42 » ne se voient jamais. ?agent= (nom technique ou UUID) borne à un agent ; sans lui, tout l'espace. Encodez l'identifiant pour une URL (encodeURIComponent).
Clé : sk_ uniquement (une pk_ reçoit 403)
Tout ce que DaleVoz garde de la personne, en un JSON : ses conversations avec leurs tours (y compris les messages retirés du fil par un conseiller, marqués deletedAt), la date de consentement de chacune (consentAt), sa fiche mémoire, ses leads, ses rendez-vous et ses commandes. Un visiteur inconnu rend des listes vides, pas un 404. Erreurs : 400 (visiteur_invalide), 401, 403 (clé pk_, ou portée visiteurs:gdpr absente de la clé), 404 (agent_introuvable).
curl "https://dalevoz.ai/api/v1/visiteurs/client-42/export" \
-H "Authorization: Bearer VOTRE_CLE_SERVEUR"
{ "userId": "client-42", "agent": null, "exportedAt": "…",
"conversations": [ { "id": "…", "agent": "accueil-boutique", "channel": "widget",
"consentAt": "2026-09-24T10:12:03.000Z", "turns": [ { "role": "user", "content": { … }, "createdAt": "…" } ] } ],
"memories": [ { "agent": "accueil-boutique", "summary": "…" } ],
"leads": [], "bookings": [], "orders": [] }Clé : sk_ uniquement (une pk_ reçoit 403)
Irréversible. Efface les conversations (et leurs tours, leads et notes vocales), la fiche mémoire, les leads qui portent l'identifiant et les commentaires Instagram rattachés. Les rendez-vous et les commandes sont des pièces de votre commerce : ils restent, sans nom, e-mail, téléphone ni texte libre. Les coûts et les ventes restent comptés, détachés de la personne. Réponse 200 avec les compteurs, même à zéro : un effacement demandé deux fois n'est pas une erreur. Mêmes erreurs que l'export.
curl -X DELETE "https://dalevoz.ai/api/v1/visiteurs/client-42" \
-H "Authorization: Bearer VOTRE_CLE_SERVEUR"
{ "userId": "client-42", "agent": null,
"deleted": { "conversations": 3, "memories": 1, "leads": 1, "instagramComments": 0 },
"anonymized": { "bookings": 1, "orders": 0 } }Par défaut, qui parle à l'agent se déclare lui-même dans user.id. Avec une clé pk_, publique par nature, n'importe qui pourrait donc poser l'identifiant d'un autre et retrouver sa conversation et ce que l'agent a retenu de lui. L'identité signée ferme cette porte : votre serveur signe un jeton court pour l'utilisateur qu'il connaît, et DaleVoz vérifie la signature.
x-dalevoz-identity de chaque appel à interact, à côté de la clé : la clé dit quelle application appelle, le jeton dit quelle personne. Un jeton valide REMPLACE le user du corps.| Élément du jeton | Règle |
|---|---|
| Algorithme | HS256 uniquement (en-tête alg). Tout autre, none compris, est refusé. |
sub | Obligatoire. L'identifiant stable de la personne chez vous, 200 caractères au plus. Devient user.id. |
exp | Obligatoire. Au plus 15 minutes après iat quand iat est présent. Cinq minutes suffisent. |
iat, nbf | Facultatifs. Une dérive d'horloge de 60 secondes est tolérée. |
name, locale | Facultatifs. Deviennent user.name et user.locale. |
| Taille | 4 096 caractères au plus. |
import { createHmac } from "node:crypto";
const b64url = (objet) => Buffer.from(JSON.stringify(objet)).toString("base64url");
// Sur VOTRE serveur, pour l'utilisateur déjà connecté chez vous.
export function jetonDaleVoz(utilisateur) {
const maintenant = Math.floor(Date.now() / 1000);
const entete = b64url({ alg: "HS256", typ: "JWT" });
const charge = b64url({
sub: utilisateur.id, // devient user.id
name: utilisateur.nom, // facultatif, devient user.name
locale: "fr", // facultatif, devient user.locale
iat: maintenant,
exp: maintenant + 5 * 60, // obligatoire, 15 minutes au plus
});
const signature = createHmac("sha256", process.env.DALEVOZ_SECRET_IDENTITE) // dvid_…
.update(`${entete}.${charge}`)
.digest("base64url");
return `${entete}.${charge}.${signature}`;
}
// Même résultat avec la bibliothèque jsonwebtoken :
// jwt.sign({ sub: utilisateur.id, name: utilisateur.nom }, process.env.DALEVOZ_SECRET_IDENTITE,
// { algorithm: "HS256", expiresIn: "5m" });await fetch("https://dalevoz.ai/api/v1/interact", {
method: "POST",
headers: {
Authorization: "Bearer pk_…",
"x-dalevoz-identity": jeton, // fourni par votre serveur
"Content-Type": "application/json",
},
body: JSON.stringify({
agent: "mon-agent",
sessionId,
// pas de user.id : c'est le jeton qui le porte
action: { type: "text", payload: { message } },
}),
});Refus possibles, tous en 401 : jeton mal formé, algorithme refusé, signature invalide, jeton expiré ou de durée de vie trop longue, pas encore valide, sans sub exploitable, trop long ; clé sans secret qui reçoit un jeton ; clé avec secret qui reçoit user.id sans jeton. Un jeton invalide n'est JAMAIS remplacé par le user.id du corps.
Une balise à coller avant la fin du body. Le widget vit dans un shadow DOM : son style ne touche pas votre page, et le vôtre ne le touche pas. La balise toute prête, clé comprise, se copie dans la console (onglet « Publier » de l'agent, carte « Site web »).
<script src="https://dalevoz.ai/dalevoz-widget.js"
data-agent="mon-agent"
data-key="pk_…"
defer></script>Le widget appelle l'API à l'adresse d'où il est chargé. Il garde la conversation dans le stockage local du navigateur : moins de 24 heures d'inactivité et la personne retrouve son fil, au-delà une nouvelle conversation s'ouvre. Il attribue aussi un identifiant de visiteur stable par navigateur, sauf si vous en donnez un (data-uid ou dalevoz.identify).
| Attribut | Rôle |
|---|---|
data-agent | Obligatoire. Le nom technique de l'agent. |
data-key | Obligatoire. La clé pk_ de l'espace. |
data-mode | widget (bulle flottante, par défaut), embed (dans votre page, toujours ouvert) ou popover (fenêtre centrée). |
data-target | En mode embed : le sélecteur CSS du conteneur (sinon le body). |
data-locale | Force la langue. Sinon, celle du navigateur (deux lettres). |
data-title | Remplace le titre du panneau réglé dans la console. |
data-accent | Remplace la couleur d'accent réglée dans la console (couleur CSS). |
data-uid | L'identifiant du visiteur, quand votre site le connaît (utilisateur connecté). Il devient user.id, et chaque identifiant a son propre fil sur un même navigateur. 120 caractères au plus. Il reste déclaratif : sans jeton signé, l'agent ne lit ni n'écrit de fiche mémoire pour ce visiteur (voir dalevoz.identify et l'identité signée). |
data-contexte | Un objet JSON passé à dalevoz.contexte() au chargement, sans une ligne de JavaScript. |
data-segment | Le segment de l'agent à appliquer (voir la route segments), 60 caractères au plus. |
data-vars | Un objet JSON rangé dans le contexte de la conversation à sa création (context.vars). |
data-session | Un identifiant de conversation (UUID) à reprendre, par exemple celui rendu par site/lire. |
data-reset | off retire le bouton « nouvelle conversation » de l'en-tête. |
data-close | off retire la croix de fermeture (utile dans une WebView d'application). |
data-host | L'adresse de l'API, si elle diffère de celle du script. |
data-attribution | Rattache une commande de votre boutique à la conversation qui l'a déclenchée. Absent par défaut : rien n'est ajouté. lien (recommandé) : les liens ouverts depuis le fil vers votre propre site reçoivent dv=<identifiant de conversation>, les autres paramètres (UTM) et l'ancre restent intacts, et aucun cookie n'est posé ; votre boutique garde la valeur dans sa session et la recopie sur la commande. on : le widget dépose le cookie dv_conv (30 jours, uniquement l'identifiant), à déclarer dans votre bandeau de consentement. |
data-canal et data-plein sont posés par la page d'intégration en iframe de DaleVoz ; vous n'avez pas à les écrire. data-greeting est lu mais sans effet : l'accueil se règle sur l'agent.
Les deux noms désignent le même objet. Chaque méthode s'appelle directement (dalevoz.open()) ou par la fonction (DaleVoz("open")), la forme du bout de code à file d'attente ci-dessous. Les noms français d'origine restent valables.
| Fonction | Ce qu'elle fait |
|---|---|
identify(objet) · identifier | Dit qui est le visiteur. userId devient user.id (200 caractères au plus) et chaque personne retrouve SON fil sur un même navigateur ; name devient user.name ; token (le jeton signé par votre serveur, ou une fonction qui le rend) part dans l'en-tête x-dalevoz-identity et remplace user.id. Tout autre champ (email, offre…) passe par contexte(). À appeler avant l'ouverture ; appelé après, le fil de l'ancien visiteur est quitté. identify(null) revient au visiteur anonyme. |
open() · ouvrir() | Ouvre le panneau. |
close() · fermer() | Ferme le panneau (sans effet en mode embed). La conversation continue à la réouverture. |
toggle() · basculer() | Ouvre s'il est fermé, ferme s'il est ouvert. |
on(nom, fonction) · off(nom, fonction?) | Écoute un événement du widget (tableau suivant). off sans fonction retire tous les écouteurs de ce nom. Un on("ready") posé après le chargement est rappelé tout de suite. |
contexte(objet) | Déclare ce que la page sait du visiteur. Fusionne avec l'existant (une clé absente reste, null efface), et part avec le prochain message : rien ne transite tant qu'aucune conversation n'existe. Seules les variables déclarées sur l'agent (onglet « Contexte ») l'atteignent. Idempotent : appelable à chaque rendu. |
evenement(nom, donnees?) | Signale un instant (panier abandonné, étape franchie). L'agent ne réagit qu'aux événements déclarés sur lui, et c'est la configuration de l'agent qui décide s'il parle. Panneau fermé, sa réponse apparaît en bulle près du lanceur ; le panneau ne s'ouvre jamais tout seul. Muet si le visiteur a choisi « Ne m'interrompez plus » ou pendant un appel vocal. |
consentement() | Rend { requis, accepteLe } : le bandeau de consentement est-il allumé sur l'agent, et quand ce navigateur l'a-t-il accepté (date ISO ou null). |
<!-- Avant la balise du widget : les appels faits avant son chargement sont mis en file, puis rejoués. -->
<script>
window.DaleVoz = window.DaleVoz || function () {
(window.DaleVoz.q = window.DaleVoz.q || []).push(arguments);
};
// Utilisable tout de suite, même si le script n'est pas encore chargé :
DaleVoz("identify", { userId: "client-42", name: "Alexis" });
DaleVoz("on", "message", function (m) { console.log(m.role, m.text); });
</script>// Une fois chargé, window.DaleVoz et window.dalevoz sont le même objet.
dalevoz.open();
dalevoz.close();
dalevoz.toggle();
// Ce que la page sait du visiteur. Fusionné ; null efface une clé.
dalevoz.contexte({ offre: "pro", panier_total: 129.9, page: "commande" });
// Un instant qui peut faire réagir l'agent, s'il est déclaré sur l'agent.
dalevoz.evenement("panier_abandonne", { panier_total: 129.9 });
dalevoz.on("message", (m) => {
// m.role : "user" | "assistant" | "human_agent" ; m.text ; m.sessionId
if (m.role === "assistant") analytics.track("reponse_agent");
});
dalevoz.on("open", () => console.log("panneau ouvert"));Visiteur connecté. Sans jeton, l'identifiant reste déclaratif : la conversation marche, mais l'agent ne lit ni n'écrit la fiche mémoire de ce visiteur. Avec un jeton, c'est lui qui prouve qui parle ; une clé qui exige l'identité signée refuse alors tout visiteur non identifié. Préférez une FONCTION de jeton : un jeton vit 15 minutes au plus, une conversation davantage. Identité signée
// Utilisateur connecté sur VOTRE site. Le jeton est signé par VOTRE serveur (voir Identité signée).
dalevoz.identify({
userId: "client-42",
name: "Alexis Martin",
email: "alexis@exemple.fr", // passe par contexte() : déclarez la variable email sur l'agent
// Une fonction est rappelée avant chaque expiration du jeton (15 minutes au plus) :
token: () => fetch("/api/jeton-dalevoz").then((r) => r.text()),
});
// Déconnexion : retour au visiteur anonyme de ce navigateur.
dalevoz.identify(null);Chaque événement se reçoit par dalevoz.on(nom, fonction). Les cinq derniers partent aussi sur window en CustomEvent, avec le même contenu dans event.detail.
| on(…) | Sur window | Quand, et ce qu'il porte |
|---|---|---|
ready | dalevoz:ready | Le widget est monté. Sur window, émis à la fin du chargement du script, puis une seconde fois (depuis l'élément du widget, en remontant) quand son thème est appliqué : un écouteur window doit supporter deux appels. on("ready") n'est appelé qu'une fois. |
message | dalevoz:message | Un message du fil : { role, text, sessionId }, role valant user (le visiteur, texte écrit, dicté ou bouton cliqué), assistant (l'agent) ou human_agent (un conseiller, avec agentName). Les messages relus à la reprise d'un fil ne sont pas réémis. Les appels vocaux en temps réel ne passent pas par là. |
open · close | dalevoz:open · dalevoz:close | À l'ouverture et à la fermeture du panneau, seulement quand l'état change. |
consent | dalevoz:consent | Le visiteur vient d'accepter le bandeau : { acceptedAt }. |
identify | dalevoz:identify | Après identify() : { userId, signed }. |
| - | dalevoz:panneau | Historique : à chaque ouverture ou fermeture, avec event.detail.ouvert (boolean). |
Un outil HTTP est une action que l'agent déclenche lui-même : envoyer une demande de rappel à votre CRM, créer un ticket, réserver un créneau. Il se crée sur la page de l'agent, onglet « Actions », ou par une IA branchée en MCP. On lui donne un nom, une phrase qui dit QUAND s'en servir, l'adresse publique qui reçoit, la méthode (POST par défaut, PUT ou GET), les informations à collecter (une étoile les rend obligatoires : telephone*) et des en-têtes.
Les informations collectées partent en corps JSON, ou en paramètres d'adresse pour un GET. Une modification touche la version de travail : rien n'est en ligne avant la publication de l'agent.
Une clé d'API ne s'écrit jamais en clair dans un outil. Elle se range dans le coffre de l'espace : console, Réglages, onglet « ChatGPT et Claude », « Pour un développeur : coffre de secrets ». L'en-tête de l'outil la cite ensuite par son nom :
Authorization: Bearer {{secret:CRM_API_KEY}}
X-Api-Key: {{secret:AGENDA_CLE}}CLE_CRM). Valeur : 4 000 caractères au plus. 50 secrets par espace.{{env:TOOL_SECRET_NOM}} reste comprise : elle lit le secret NOM du même coffre, jamais une variable du serveur.DaleVoz appelle votre serveur (POST, JSON) quand quelque chose se passe dans une conversation, au lieu que vous veniez le demander. Un abonnement se crée dans la console, Réglages, onglet « ChatGPT et Claude », « Pour un développeur : webhooks » : une adresse en https, les événements à recevoir, tous les agents ou un seul. Son secret, whsec_…, n'est montré qu'une fois ; « Envoyer un test » y envoie un test.ping, et le journal garde 30 jours de livraisons.
| En-tête | Contenu |
|---|---|
Dale-Voz-Signature | t=<unix>,v1=<hex> : v1 est le HMAC-SHA256, en hexadécimal, de « <t>.<corps> » avec votre secret. |
Dale-Voz-Event | Le type de l'événement. |
Dale-Voz-Delivery | L'identifiant de la livraison, le même à chaque retente : gardez-le pour ne traiter un événement qu'une fois. |
Le corps, toujours la même enveloppe :
{
"id": "evt_…",
"type": "lead.qualifie",
"cree_le": "2026-09-24T10:12:04.000Z",
"espace": { "id": "…", "slug": "ma-boutique" },
"agent": { "id": "…", "slug": "accueil-boutique" },
"donnees": {
"lead_id": "…",
"conversation_id": "…",
"canal": "widget",
"nouveau": true,
"nom": "Camille Martin",
"email": "camille@exemple.fr",
"besoin": "Devis pour 40 personnes"
}
}Un champ vide est omis. Les conversations de « Tester », dans la console, n'émettent rien.
| Événement | Quand | Champs de donnees |
|---|---|---|
conversation.demarree | Une conversation commence. | conversation_id, canal, langue, contact_id, nom |
conversation.terminee | Une conversation est close et classée. | conversation_id, canal, terminee_le, issue, resume, sujet, satisfaction, messages_visiteur, messages_agent, duree_s, humain_intervenu |
transfert.demande | Le visiteur demande un humain. | conversation_id, canal, contact_id, nom, raison |
lead.qualifie | Un contact est créé ou complété (nouveau: true ou false). | lead_id, conversation_id, canal, nouveau, nom, email, telephone, besoin, secteur, preference_contact |
rdv.pris | Un rendez-vous est pris. | rdv_id, conversation_id, canal, debut, fin, duree_min, fuseau, nom, email, telephone, mode, sujet, invitation_envoyee |
satisfaction.recue | Le visiteur donne son avis. | conversation_id, canal, source, note, commentaire, resume |
test.ping | Un envoi d'essai, depuis la console. | message, abonnement_id |
mode vaut appel, ecrit ou visio ; note vaut satisfied, partial, need_detail ou not_resolved.
import express from "express";
import { createHmac, timingSafeEqual } from "node:crypto";
const TOLERANCE_S = 5 * 60;
function signatureValide(corpsBrut, entete, secret) {
const champs = {};
for (const morceau of (entete ?? "").split(",")) {
const i = morceau.indexOf("=");
if (i > 0) champs[morceau.slice(0, i).trim()] = morceau.slice(i + 1).trim();
}
const t = Number(champs.t);
if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > TOLERANCE_S) return false;
const attendue = createHmac("sha256", secret).update(`${t}.${corpsBrut}`).digest();
const recue = Buffer.from(champs.v1 ?? "", "hex");
return recue.length === attendue.length && timingSafeEqual(recue, attendue);
}
const app = express();
// Le corps BRUT : la signature porte sur les octets reçus, pas sur un JSON relu.
app.post("/webhooks/dale-voz", express.raw({ type: "application/json" }), (req, res) => {
const corps = req.body.toString("utf8");
if (!signatureValide(corps, req.get("Dale-Voz-Signature"), process.env.DALEVOZ_WEBHOOK_SECRET)) {
return res.status(400).end();
}
const livraison = req.get("Dale-Voz-Delivery");
if (dejaTraitee(livraison)) return res.status(200).end(); // une retente
const evenement = JSON.parse(corps);
traiter(evenement.type, evenement.donnees);
marquerTraitee(livraison);
res.status(200).end();
});import hashlib
import hmac
import os
import time
from flask import Flask, request
TOLERANCE_S = 5 * 60
app = Flask(__name__)
def signature_valide(corps: bytes, entete: str, secret: str) -> bool:
champs = dict(m.split("=", 1) for m in (entete or "").split(",") if "=" in m)
try:
t = int(champs.get("t", ""))
except ValueError:
return False
if abs(time.time() - t) > TOLERANCE_S:
return False
attendue = hmac.new(secret.encode(), f"{t}.".encode() + corps, hashlib.sha256).hexdigest()
return hmac.compare_digest(attendue, champs.get("v1", ""))
@app.post("/webhooks/dale-voz")
def webhook_dale_voz():
corps = request.get_data() # le corps brut, en octets
if not signature_valide(corps, request.headers.get("Dale-Voz-Signature", ""), os.environ["DALEVOZ_WEBHOOK_SECRET"]):
return "", 400
livraison = request.headers.get("Dale-Voz-Delivery")
if deja_traitee(livraison):
return "", 200
evenement = request.get_json()
traiter(evenement["type"], evenement["donnees"])
marquer_traitee(livraison)
return "", 200DaleVoz expose un serveur MCP distant. Branché sur Claude, ChatGPT, Claude Code ou Codex, il laisse l'IA créer et régler vos agents en conversation, avec VOS droits dans l'espace que vous autorisez. Aucune clé à coller : l'autorisation se fait par OAuth, sur un écran DaleVoz où vous choisissez l'espace.
https://dalevoz.ai/api/mcpLe pas à pas illustré, outil par outil, est dans la console : Brancher mon IA. Une autorisation ouvre un seul espace : une agence ajoute un branchement par client.
Claude (tous les abonnements, gratuit compris, avec un seul connecteur personnalisé en gratuit ; en Team ou Enterprise, c'est le propriétaire de l'organisation qui l'ajoute ; sur le web, l'application de bureau ou le téléphone) : le lien ci-dessous ouvre la fenêtre « Ajouter un connecteur personnalisé », nom et adresse déjà remplis. Laissez les options proposées et cliquez « Ajouter » en bas de la fenêtre, puis « Connecter » sur la page du connecteur, et autorisez votre espace sur la page DaleVoz qui s'ouvre. Sans le lien : claude.ai/customize/connectors, « Ajouter un connecteur personnalisé », collez l'adresse. Le lien : ajouter DaleVoz à Claude.
ChatGPT (abonnement Plus, Pro, Business ou Enterprise/Edu ; impossible en gratuit et avec Go ; en Business ou Enterprise, c'est l'administrateur de l'espace ChatGPT qui crée l'application). Uniquement sur chatgpt.com, dans le navigateur d'un ordinateur : les applications MCP n'existent ni dans l'application ni sur le téléphone.
Si ChatGPT affiche « Activez le mode développeur dans les paramètres de ChatGPT avant de créer une application MCP » : Paramètres, barre « Rechercher » en haut des paramètres, tapez « développeur », puis activez « Mode développeur ».
Claude Code : une commande, puis votre navigateur s'ouvre pour l'autorisation.
claude mcp add --transport http dale-voz https://dalevoz.ai/api/mcpCodex : la même logique, en deux commandes.
codex mcp add dale-voz --url https://dalevoz.ai/api/mcp
codex mcp login dale-voz2026-07-28, 2025-11-25, 2025-06-18.Pour commencer, collez ce message dans une nouvelle conversation : votre IA ouvre DaleVoz, vous dit où vous en êtes et vous guide pas à pas.
Je commence avec DaleVoz : lance dalevoz_demarrer, dis-moi en trois lignes où j'en suis, puis guide-moi pas à pas, une question à la fois, sans jargon.
dalevoz_documentation. Sans connecteur, tout tient en une adresse : https://dalevoz.ai/llms-full.txt?lang=fr.Le nombre d'outils visibles dépend de votre rôle et de votre offre. En libre service, votre IA voit l'essentiel (créer, nourrir, tester, publier, installer, lire les conversations) et ouvre le reste à la demande, sans rien rebrancher. Chaque écriture est inscrite au journal de l'agent, à votre nom.
Dans quel ordre s'en servir pour construire un agent qui tient : la méthode.
| Où | Limite |
|---|---|
| interact | Message : 8 000 caractères. Pièces jointes : 5 par message. |
| attachments | 15 000 000 caractères de base64 (environ 11 Mo) ; texte extrait coupé à 20 000 caractères. |
| messages | 50 éléments par appel. |
| history | Les 60 derniers éléments. |
| voice/dictee | 8 Mo, 120 secondes ; 20 appels par minute et par espace. |
| voice/lecture | 1 200 caractères lus ; 30 appels par minute et par espace. |
| site/lire | 6 lectures par heure et par adresse IP ; 8 pages lues. |
| site/pont-whatsapp | 3 envois par heure et par adresse IP ; un seul par conversation. |
| kb/sync-table | 2 000 lignes par envoi. |
| analytics | 90 jours au plus avec days. |
| conversations/export | 200 conversations par page. |
| agents | Prompt : 60 000 caractères ; maxTokens de 256 à 32 000. |
| segments | promptFragment et systemPrompt : 20 000 caractères chacun. |
| Identité signée | Jeton de 4 096 caractères, 15 minutes de vie au plus. |
| Coffre de secrets | 50 secrets par espace, 4 000 caractères par valeur. |
| /api/mcp | 240 appels par minute et par compte (une personne dans un espace, ou l'espace pour une clé sk_) ; 3 000 par adresse IP, dont 120 échecs d'authentification au plus (429, avec Retry-After: 60). |
Les limites ci-dessus par espace ou par adresse IP (dictée, lecture, lecture de site, pont WhatsApp, MCP) sont comptées en mémoire par instance de serveur : le plafond réellement atteint peut être un multiple de celui indiqué, ne vous appuyez pas dessus comme sur un quota exact.
Une clé pk_ vit dans le HTML de votre site : sans plafond, qui la recopie pourrait vider vos crédits. Chaque route publique compte donc ses appels en fenêtres fixes (minute, heure, jour), en base, pour la CLÉ (tous visiteurs confondus) et, derrière une clé pk_ seulement, pour le VISITEUR, reconnu à son adresse IP. Les seuils sont 10 à 20 fois au-dessus des pics mesurés chez nos clients : ils arrêtent un script, pas un site. Au-delà : 429, avec code limite, portee (cle ou visiteur), reessayerDansS et l'en-tête Retry-After ; error est une phrase pour le visiteur, dans sa langue, que le widget affiche une fois sans relancer. Un espace qui en a besoin (une conférence où toute la salle parle à l'agent depuis le même Wi-Fi) peut être relevé : écrivez-nous.
| Route | Par clé | Par visiteur (pk_) |
|---|---|---|
| interact | 300 par minute, 3 000 par heure, 20 000 par jour | 40 par minute, 600 par heure |
| attachments | 60 par minute, 600 par heure, 3 000 par jour | 10 par minute, 60 par heure |
| voice/dictee, voice/lecture | 600 par heure, 4 000 par jour (les deux ensemble) | 20 par minute, 200 par heure |
| site/lire | 60 par heure, 400 par jour | - |
| site/pont-whatsapp | 30 par heure, 200 par jour | - |
Ces plafonds s'ajoutent à ceux de l'offre de l'espace (conversations par mois, tours par conversation), qui ne répondent pas en 429 mais par un 200 dont le message le dit au visiteur (voir la route interact).
| Statut | Signification dans l'API |
|---|---|
| 200 | Succès. Aussi pour un plafond d'offre atteint sur interact (le message est dans les traces). |
| 400 | Corps illisible, champ invalide (issues), paramètre manquant, règle de forme non respectée. |
| 401 | Clé absente, inconnue ou révoquée ; jeton d'identité refusé ; mauvais type de clé sur les routes voice. |
| 403 | Clé pk_ sur une route réservée aux sk_ ; origine non autorisée ; lecture de site désactivée. |
| 404 | Agent, session ou ressource introuvable dans l'espace de la clé ; agent jamais publié. |
| 409 | Lot de base de connaissances synchronisé ailleurs ; site pas encore lu. |
| 413 | Audio trop lourd ou trop long. |
| 422 | Pièce jointe illisible ; site injoignable. |
| 423 | Agent en pause. |
| 429 | Trop d'appels (voir les deux tableaux ci-dessus). code vaut limite pour un plafond par clé ; Retry-After dit quand réessayer. |
| 500, 502, 503 | Erreur serveur, réponse du modèle interrompue, voix ou canal non configurés. |