Référence API — Agents-as-API
Cette page est la référence API d’Abra. Elle s’adresse aux développeurs et intégrateurs qui veulent appeler un agent Abra depuis leur propre code — un script, un backend, ou un widget dans le navigateur. Elle contient donc du code (
curl, JavaScript, JSON).Base URL des exemples :
https://votre-abra— remplacez-la par l’origine de votre instance Abra. Toutes les routes sont préfixées par/api/v1.
Vue d’ensemble
Section intitulée « Vue d’ensemble »Chaque agent approuvé de votre organisation est exposé derrière une URL stable, authentifiée par une clé API, qui renvoie la réponse en temps réel (streaming SSE). C’est exactement le même moteur que le chat intégré à Abra : mêmes outils, mêmes documents (RAG), mêmes garde-fous et mêmes modèles. Vous ne réimplémentez rien — vous appelez.
Trois façons typiques d’intégrer :
- Script / tâche batch — un job qui interroge un agent en boucle (enrichissement de données, résumé de tickets…). Clé stockée côté serveur.
- Backend applicatif — votre API appelle l’agent Abra pour une fonctionnalité (un assistant dans votre produit). Clé stockée dans les secrets serveur, jamais renvoyée au client.
- Widget frontend — du JavaScript dans une page web appelle directement l’agent. Nécessite d’autoriser l’origine de la page dans le CORS de la clé (voir plus bas), et impose des précautions (la clé devient visible côté navigateur).
Le cycle d’un appel est toujours le même : vous POSTez un message à un agent identifié par son slug, et Abra vous renvoie un flux de tokens au fur et à mesure que l’agent réfléchit, appelle ses outils, et rédige sa réponse.
curl -N -X POST "https://votre-abra/api/v1/agents/support-rh/messages" \ -H "Authorization: Bearer abra_live_xxx" \ -H "Content-Type: application/json" \ -d '{"message":"Combien de jours de RTT en 2026 ?"}'
-N(--no-buffer) affiche le flux au fil de l’eau plutôt qu’en bloc.
Authentification
Section intitulée « Authentification »Chaque requête doit porter une clé API. Deux en-têtes sont acceptés,
au choix — le Bearer est prioritaire s’il est présent :
Authorization: Bearer abra_live_xxxxxxxxxxxxxxxxxxxxxxxxou
x-api-key: abra_live_xxxxxxxxxxxxxxxxxxxxxxxxLes clés commencent toujours par le préfixe abra_live_. Elles sont
affichées une seule fois, à la création : copiez le secret
immédiatement, il n’est plus jamais récupérable ensuite (seul un hash
est stocké côté serveur — Abra ne peut pas vous le réafficher).
Une clé est rattachée à un utilisateur principal de l’organisation.
Tous les appels effectués avec cette clé sont exécutés en son nom :
c’est ce principal qui détermine le périmètre RAG accessible, le budget
imputé, et l’attribution dans l’audit. Une clé sans principal est
refusée (403).
Créer et gérer une clé
Section intitulée « Créer et gérer une clé »Dans Abra, ouvrez la page Agents en API (menu Agents → onglet Agents en API / Setup). Vous y :
- générez une clé en choisissant : les agents qu’elle peut appeler (vide = tous les agents approuvés de l’org), les domaines autorisés pour le CORS, un plafond mensuel optionnel, et une limite de débit optionnelle ;
- copiez le secret
abra_live_…(une seule fois) ; - révoquez une clé à tout moment — la révocation est immédiate
(l’appel suivant renvoie
401).



Ne l’exposez jamais côté client public. Une clé dans du JavaScript de navigateur est lisible par n’importe quel visiteur. Réservez ce mode aux clés à périmètre restreint (agents limités + plafond mensuel + CORS précis). Pour un backend, gardez la clé dans vos secrets serveur.
Quelle organisation ?
Section intitulée « Quelle organisation ? »Vous n’avez jamais à préciser l’organisation dans l’URL : elle est
déterminée par la clé. Chaque clé appartient à une organisation, et un
agent y est identifié par la paire unique (organisation, slug).
Conséquence pratique : deux organisations différentes peuvent avoir un
agent avec le même slug (support, rh…) sans aucune ambiguïté — la clé
tranche. Vous manipulez donc toujours des slugs courts et lisibles,
propres à votre organisation.
Seuls les agents au statut approuvé sont appelables via l’API. Un
agent en brouillon (ou rejeté) renvoie 404, exactement comme un slug
inexistant — l’API ne révèle jamais l’existence d’un agent non publié.
Endpoints
Section intitulée « Endpoints »| Méthode | Chemin | Usage |
|---|---|---|
GET | /agents | Lister les agents que la clé peut appeler |
POST | /agents/{slug}/messages | One-shot — un tour, sans état à gérer côté client |
POST | /agents/{slug}/threads | Créer un thread (conversation avec état) |
POST | /agents/{slug}/threads/{id}/messages | Envoyer un message dans un thread |
GET | /agents/{slug}/threads/{id}/messages | Relire l’historique d’un thread |
OPTIONS | (toutes les routes ci-dessus) | Préflight CORS |
One-shot vs thread
Section intitulée « One-shot vs thread »- One-shot (
/messages) : le plus simple. Chaque appel est indépendant et crée sa propre conversation côté serveur (pour l’audit), mais aucun identifiant n’est renvoyé — vous n’avez rien à gérer. Pour un échange multi-tour, vous renvoyez vous-même l’historique complet dans le champmessages. - Thread (
/threads+/threads/{id}/messages) : Abra conserve l’historique côté serveur. Vous créez un thread une fois, récupérez sonthreadId, puis n’envoyez que le nouveau message à chaque tour — l’historique stocké est fusionné automatiquement.
GET /api/v1/agents
Section intitulée « GET /api/v1/agents »Renvoie la liste des agents approuvés que cette clé est autorisée à appeler (triés par nom) :
curl "https://votre-abra/api/v1/agents" \ -H "Authorization: Bearer abra_live_xxx"{ "data": [ { "id": "3f2a…", "slug": "support-rh", "name": "Assistant RH", "description": "Répond aux questions congés, paie, onboarding.", "icon": "🧑💼" } ]}POST /api/v1/agents/{slug}/messages
Section intitulée « POST /api/v1/agents/{slug}/messages »Envoie un message et renvoie la réponse en flux SSE (voir
Le flux SSE). Corps : message ou
messages (voir Corps de requête).
POST /api/v1/agents/{slug}/threads
Section intitulée « POST /api/v1/agents/{slug}/threads »Crée un thread pour cet agent. Corps optionnel : { "title": "…" }
(tronqué à 200 caractères). Réponse JSON (pas de flux) :
{ "data": { "threadId": "8c1e…" } }POST /api/v1/agents/{slug}/threads/{id}/messages
Section intitulée « POST /api/v1/agents/{slug}/threads/{id}/messages »Envoie un message dans un thread existant. Réponse en flux SSE.
Envoyez de préférence message seul : Abra recolle automatiquement
l’historique DB avant d’appeler l’agent. messages reste possible si vous
préférez fournir vous-même l’historique.
GET /api/v1/agents/{slug}/threads/{id}/messages
Section intitulée « GET /api/v1/agents/{slug}/threads/{id}/messages »Relit l’historique du thread, du plus ancien au plus récent. Paramètre
?limit= optionnel (1 à 200, défaut 200). Réponse JSON :
{ "data": [ { "id": "…", "role": "user", "parts": [{ "type": "text", "text": "Bonjour" }] }, { "id": "…", "role": "assistant", "parts": [{ "type": "text", "text": "Bonjour 👋 …" }] } ]}Corps de requête
Section intitulée « Corps de requête »Les endpoints de message acceptent un corps JSON :
| Champ | Type | Requis | Description |
|---|---|---|---|
message | string | oui* | Le message utilisateur (tour simple) |
messages | UIMessage[] | oui* | Historique complet, format Vercel AI SDK |
model | string | non | Forcer un modèle pour cet appel (voir Choix du modèle) |
* Fournissez soit message (une chaîne non vide), soit
messages (un tableau non vide). Si les deux sont absents ou vides →
400 avec le corps :
{ "error": "Body invalide : fournir `message` (string) ou `messages` (array)" }- Sur
/messages(one-shot),messagessert à passer tout l’historique d’un multi-tour que vous gérez vous-même. - Sur
/threads/{id}/messages, préférezmessageseul : Abra fusionne l’historique stocké.messagescourt-circuite ce merge et fournit l’historique tel quel.
Un objet UIMessage minimal (format du Vercel AI SDK) :
{ "id": "1", "role": "user", "parts": [{ "type": "text", "text": "Bonjour" }]}Le dernier message d’un tableau messages doit être un message
user, sinon 400.
Choix du modèle
Section intitulée « Choix du modèle »Par défaut, l’agent répond avec son modèle configuré dans Abra — vous
n’avez rien à préciser. Le champ model permet de surcharger ce
modèle pour un appel donné, mais uniquement vers un modèle de
l’allowlist de l’organisation et de l’allowlist propre à l’agent, le
cas échéant. Un modèle hors liste est refusé avec un 403 — il n’y a
pas de repli silencieux :
{ "error": "Le modèle \"gpt-4o\" n'est pas autorisé pour cet agent. …", "modelId": "gpt-4o", "code": "AGENT_TEMPLATE_MODEL_NOT_ALLOWED"}Demandez à votre administrateur Abra la liste des modèles autorisés.
Le flux SSE (streaming)
Section intitulée « Le flux SSE (streaming) »Les endpoints de message renvoient un flux Server-Sent Events
(Content-Type: text/event-stream), au format du Vercel AI SDK —
exactement le même flux que le chat intégré. Chaque ligne data: porte
un événement JSON, et le flux se termine par une ligne sentinelle
data: [DONE].
Les principaux type d’événement à connaître :
type | Signification |
|---|---|
text-delta | Un fragment de texte de la réponse (delta) — à concaténer |
tool-input-start | L’agent commence à appeler un outil (toolName, toolCallId) |
tool-output-available | Le résultat d’un outil est disponible (output) |
finish | Fin du tour |
Exemple de flux (simplifié) :
data: {"type":"text-delta","delta":"Bonjour"}data: {"type":"tool-input-start","toolName":"searchKnowledgeBase","toolCallId":"call_abc"}data: {"type":"tool-output-available","toolCallId":"call_abc","output":{"chunks":[]}}data: {"type":"text-delta","delta":" 👋"}data: {"type":"finish"}data: [DONE]Pour reconstituer le texte de la réponse, il suffit de concaténer les
delta de tous les événements text-delta. Les événements d’outils
sont informatifs (utiles pour afficher « l’agent consulte la base… »). Le
protocole émet d’autres type (démarrage/fin de bloc de texte, étapes,
etc.) : votre parseur doit ignorer sans erreur les types qu’il ne
connaît pas.
Lire le flux en JavaScript
Section intitulée « Lire le flux en JavaScript »const res = await fetch( "https://votre-abra/api/v1/agents/support-rh/messages", { method: "POST", headers: { "Authorization": "Bearer " + API_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ message: "Bonjour" }), },);
const reader = res.body.getReader();const decoder = new TextDecoder();let buffer = "";let answer = "";
while (true) { const { value, done } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n"); buffer = lines.pop() ?? ""; // garde la ligne incomplète
for (const line of lines) { if (!line.startsWith("data:")) continue; const payload = line.slice(5).trim(); if (payload === "[DONE]") break;
const evt = JSON.parse(payload); if (evt.type === "text-delta") { answer += evt.delta; // concatène le texte } else if (evt.type === "finish") { console.log("Réponse complète :", answer); } }}CORS — appel depuis un navigateur
Section intitulée « CORS — appel depuis un navigateur »Par défaut une clé est server-to-server : aucun en-tête CORS n’est renvoyé, donc un navigateur ne peut pas lire la réponse.
Pour appeler l’agent depuis le frontend d’un site (votre propre
JavaScript, votre design), ajoutez l’origine exacte de la page dans
« Domaines autorisés » de la clé (ex. https://app.mon-site.com). Abra
reflète alors cette origine — et uniquement celle-ci — dans
Access-Control-Allow-Origin. Jamais * (ce serait de toute façon
incompatible avec une clé Bearer).
Détails du comportement, tels qu’implémentés :
- Les en-têtes CORS sont posés sur la vraie réponse (POST/GET), flux SSE compris, uniquement si l’origine ∈ liste autorisée.
- Le préflight
OPTIONSrépond de façon permissive (le navigateur n’envoie pas la clé au préflight, donc Abra ne peut pas encore la valider). L’enforcement réel tient sur la requête effective : sansAccess-Control-Allow-Originsur celle-ci, le navigateur bloque la lecture. - Méthodes autorisées :
GET, POST, OPTIONS. En-têtes autorisés :Authorization, Content-Type, X-Api-Key.
Une origine non listée reçoit une réponse sans en-tête CORS : le navigateur bloque la lecture (la requête a pu partir, mais votre code ne verra pas le résultat).
Rate-limit par clé
Section intitulée « Rate-limit par clé »Chaque clé a une limite de débit par minute (fenêtre glissante). Le
défaut est 60 req/min ; il peut être ajusté par clé à la création. Au
dépassement, l’appel renvoie 429 Too Many Requests avec un en-tête
Retry-After (en secondes) et un corps indiquant la date de reset :
{ "error": "Rate limit exceeded", "resetAt": 1755084000000, "requestId": "…" }Ralentissez et réessayez après Retry-After.
Plafond mensuel
Section intitulée « Plafond mensuel »Une clé peut avoir un plafond de dépense mensuel. Au dépassement, les
appels renvoient 402 Payment Required :
{ "error": "Monthly cap exceeded", "requestId": "…" }Le compteur repart de zéro au début du mois UTC (reset paresseux,
appliqué au premier appel du nouveau mois), ou dès que l’administrateur
relève le plafond. Le budget LLM global de l’organisation peut lui aussi
renvoyer 402 (code: "LLM_BUDGET_EXHAUSTED" / "…_QUOTA_EXCEEDED")
indépendamment du plafond propre à la clé.
Feature désactivée
Section intitulée « Feature désactivée »Si l’API Agents n’est pas activée sur l’instance
(AGENTS_API_ENABLED=false, valeur par défaut), toute la surface
renvoie 404 de façon inerte. Contactez l’administrateur de l’instance
pour l’activer.
Traçabilité
Section intitulée « Traçabilité »Chaque appel est audité. À l’authentification, Abra écrit un événement
dans le journal de la clé (call, ou rate_limited / denied selon le
cas), et en fin de tour un événement usage avec les tokens consommés
et le coût — corrélés par conversation.
- Attribution. Toute conversation créée par une clé est rattachée à la
fois à la clé (
apiKeyId) et à son utilisateur principal (userId), avecsource = "api_key". C’est ce couple qui apparaît dans les tableaux de bord. - Request ID. Chaque réponse porte un en-tête
X-Request-Id(réutilisé depuis votreX-Request-Identrant s’il est présent, sinon généré). Ce même identifiant figure dans le corps des erreurs ("requestId": "…") et dans les logs serveur — copiez-le depuis les DevTools et donnez-le au support pour un diagnostic immédiat. - Où voir l’historique. Le dashboard Agents-as-API de votre organisation montre l’usage par clé (appels, tokens, coût, dernière utilisation), et l’onglet Conversations permet d’explorer le détail des échanges par clé et par agent.


Bonnes pratiques
Section intitulée « Bonnes pratiques »- Une clé par usage. Séparez backend, batch et widget frontend dans des clés distinctes. En cas de fuite, vous révoquez la clé concernée sans casser les autres intégrations.
- Périmètre minimal. Restreignez chaque clé aux agents dont elle a besoin, fixez un plafond mensuel et un débit raisonnables, et n’ouvrez le CORS qu’aux origines strictement nécessaires.
- Secrets côté serveur. Ne committez jamais une clé et ne l’injectez pas dans un bundle frontend public. Stockez-la dans un gestionnaire de secrets / variables d’environnement serveur.
- Rotation. Créez la nouvelle clé, déployez-la, puis révoquez l’ancienne (révocation immédiate). Cette recouverte évite toute coupure.
- Gérez les erreurs transitoires. Respectez
Retry-Aftersur429, et prévoyez un backoff. Traitez402comme un état « quota atteint », à remonter à l’exploitant plutôt qu’à réessayer en boucle. - Parseur SSE tolérant. Concaténez les
text-delta, ignorez lestypeinconnus, et arrêtez proprement sur[DONE]. - Conservez le
X-Request-Id. Loguez-le côté client pour corréler vos incidents avec les logs Abra.
Questions fréquentes
Section intitulée « Questions fréquentes »Dois-je préciser mon organisation dans l’URL ?
Non. La clé détermine l’organisation ; l’agent est résolu par
(organisation, slug).
Comment récupérer une clé perdue ? Impossible — seul un hash est stocké. Créez-en une nouvelle et révoquez l’ancienne.
Puis-je appeler un agent en brouillon ?
Non. Seuls les agents approuvés sont exposés ; sinon 404.
Le one-shot garde-t-il le contexte entre deux appels ?
Non. Utilisez un thread, ou renvoyez l’historique complet dans
messages.
Comment obtenir la réponse en une seule fois plutôt qu’en streaming ?
L’API renvoie toujours du SSE. Concaténez les text-delta jusqu’à
finish / [DONE] pour reconstituer le texte final côté client.
Mon fetch navigateur est bloqué (CORS).
Ajoutez l’origine exacte de la page dans « Domaines autorisés » de la
clé. Une origine non listée ne reçoit aucun en-tête CORS.
Récapitulatif des codes d’erreur
Section intitulée « Récapitulatif des codes d’erreur »| Code | Signification | Corps type |
|---|---|---|
400 | Corps invalide (message/messages manquant ou vide, dernier message non-user) | { "error": "Body invalide : …" } |
401 | Clé absente, invalide, révoquée ou expirée | { "error": "Invalid API key", "requestId": "…" } |
402 | Plafond mensuel de la clé (ou budget LLM de l’org) atteint | { "error": "Monthly cap exceeded", "requestId": "…" } |
403 | Clé sans principal, agent non autorisé pour la clé, ou modèle hors allowlist | { "error": "Agent non autorisé pour cette clé" } |
404 | Agent introuvable / non approuvé, thread introuvable, ou API désactivée | { "error": "Agent introuvable" } |
429 | Rate-limit dépassé (voir Retry-After) | { "error": "Rate limit exceeded", "resetAt": … } |
402/503 | Budget épuisé / provider ou clé API non configurés sur l’instance | { "error": "…", "code": "…" } |
Les erreurs issues du gauntlet d’authentification incluent toujours un
requestId. Toutes les erreurs sont au format { "error": "…" } (hors
flux SSE).
Exemples complets
Section intitulée « Exemples complets »One-shot (curl)
Section intitulée « One-shot (curl) »curl -N -X POST "https://votre-abra/api/v1/agents/support-rh/messages" \ -H "Authorization: Bearer abra_live_xxx" \ -H "Content-Type: application/json" \ -d '{"message":"Bonjour"}'Avec un modèle forcé (doit être dans l’allowlist) :
curl -N -X POST "https://votre-abra/api/v1/agents/support-rh/messages" \ -H "Authorization: Bearer abra_live_xxx" \ -H "Content-Type: application/json" \ -d '{"message":"Résume ce dossier","model":"claude-3-5-sonnet"}'Thread avec état (curl)
Section intitulée « Thread avec état (curl) »# 1) créer un threadcurl -X POST "https://votre-abra/api/v1/agents/support-rh/threads" \ -H "Authorization: Bearer abra_live_xxx" \ -H "Content-Type: application/json" \ -d '{"title":"Ticket #4821"}'# → {"data":{"threadId":"8c1e…"}}
# 2) envoyer des messages — l'historique est fusionné automatiquementcurl -N -X POST "https://votre-abra/api/v1/agents/support-rh/threads/8c1e…/messages" \ -H "Authorization: Bearer abra_live_xxx" \ -H "Content-Type: application/json" \ -d '{"message":"Et pour les RTT ?"}'
# 3) relire l'historiquecurl "https://votre-abra/api/v1/agents/support-rh/threads/8c1e…/messages?limit=50" \ -H "Authorization: Bearer abra_live_xxx"Multi-tour géré côté client (one-shot + messages)
Section intitulée « Multi-tour géré côté client (one-shot + messages) »{ "messages": [ { "id": "1", "role": "user", "parts": [{ "type": "text", "text": "Bonjour" }] }, { "id": "2", "role": "assistant", "parts": [{ "type": "text", "text": "Bonjour 👋" }] }, { "id": "3", "role": "user", "parts": [{ "type": "text", "text": "Et mes RTT ?" }] } ]}Depuis un navigateur (fetch JS)
Section intitulée « Depuis un navigateur (fetch JS) »Voir l’exemple complet de lecture du flux dans Le flux SSE. Prérequis : l’origine de la page doit figurer dans « Domaines autorisés » de la clé (voir CORS).
Lister les agents (curl)
Section intitulée « Lister les agents (curl) »curl "https://votre-abra/api/v1/agents" \ -H "Authorization: Bearer abra_live_xxx"