Aller au contenu

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.


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.

Fenêtre de terminal
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.


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_xxxxxxxxxxxxxxxxxxxxxxxx

ou

x-api-key: abra_live_xxxxxxxxxxxxxxxxxxxxxxxx

Les 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).

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

À CAPTURER : page « Agents en API » — bouton de création de clé et liste des clés existantes

À CAPTURER : formulaire de création — choix des agents autorisés, domaines CORS, plafond mensuel, débit/min

À CAPTURER : écran « copiez votre clé maintenant » — le secret abra_live_… affiché une seule fois

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.


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


MéthodeCheminUsage
GET/agentsLister les agents que la clé peut appeler
POST/agents/{slug}/messagesOne-shot — un tour, sans état à gérer côté client
POST/agents/{slug}/threadsCréer un thread (conversation avec état)
POST/agents/{slug}/threads/{id}/messagesEnvoyer un message dans un thread
GET/agents/{slug}/threads/{id}/messagesRelire l’historique d’un thread
OPTIONS(toutes les routes ci-dessus)Préflight CORS
  • 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 champ messages.
  • Thread (/threads + /threads/{id}/messages) : Abra conserve l’historique côté serveur. Vous créez un thread une fois, récupérez son threadId, puis n’envoyez que le nouveau message à chaque tour — l’historique stocké est fusionné automatiquement.

Renvoie la liste des agents approuvés que cette clé est autorisée à appeler (triés par nom) :

Fenêtre de terminal
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": "🧑‍💼"
}
]
}

Envoie un message et renvoie la réponse en flux SSE (voir Le flux SSE). Corps : message ou messages (voir Corps de requête).

Crée un thread pour cet agent. Corps optionnel : { "title": "…" } (tronqué à 200 caractères). Réponse JSON (pas de flux) :

{ "data": { "threadId": "8c1e…" } }

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.

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 👋 …" }] }
]
}

Les endpoints de message acceptent un corps JSON :

ChampTypeRequisDescription
messagestringoui*Le message utilisateur (tour simple)
messagesUIMessage[]oui*Historique complet, format Vercel AI SDK
modelstringnonForcer 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), messages sert à passer tout l’historique d’un multi-tour que vous gérez vous-même.
  • Sur /threads/{id}/messages, préférez message seul : Abra fusionne l’historique stocké. messages court-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.

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.


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 :

typeSignification
text-deltaUn fragment de texte de la réponse (delta) — à concaténer
tool-input-startL’agent commence à appeler un outil (toolName, toolCallId)
tool-output-availableLe résultat d’un outil est disponible (output)
finishFin 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.

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);
}
}
}

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 OPTIONS ré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 : sans Access-Control-Allow-Origin sur 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).


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.

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

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.


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), avec source = "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 votre X-Request-Id entrant 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.

À CAPTURER : dashboard Agents-as-API — usage par clé (appels, tokens, coût, lastUsedAt)

À CAPTURER : onglet Conversations — explorateur maître/détail des échanges par clé et par agent


  • 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-After sur 429, et prévoyez un backoff. Traitez 402 comme un état « quota atteint », à remonter à l’exploitant plutôt qu’à réessayer en boucle.
  • Parseur SSE tolérant. Concaténez les text-delta, ignorez les type inconnus, 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.

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.

CodeSignificationCorps type
400Corps invalide (message/messages manquant ou vide, dernier message non-user){ "error": "Body invalide : …" }
401Clé absente, invalide, révoquée ou expirée{ "error": "Invalid API key", "requestId": "…" }
402Plafond mensuel de la clé (ou budget LLM de l’org) atteint{ "error": "Monthly cap exceeded", "requestId": "…" }
403Clé sans principal, agent non autorisé pour la clé, ou modèle hors allowlist{ "error": "Agent non autorisé pour cette clé" }
404Agent introuvable / non approuvé, thread introuvable, ou API désactivée{ "error": "Agent introuvable" }
429Rate-limit dépassé (voir Retry-After){ "error": "Rate limit exceeded", "resetAt": … }
402/503Budget é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).


Fenêtre de terminal
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) :

Fenêtre de terminal
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"}'
Fenêtre de terminal
# 1) créer un thread
curl -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é automatiquement
curl -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'historique
curl "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 ?" }] }
]
}

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

Fenêtre de terminal
curl "https://votre-abra/api/v1/agents" \
-H "Authorization: Bearer abra_live_xxx"