ERHADocumentationOuvrir la console

API

Les clés d'API de l'espace et les routes pour appeler ses profils LLM, au format OpenAI.

Chaque espace ERHA expose une API HTTP pour appeler ses profils LLM depuis n'importe quel système. Le format est celui des chat completions d'OpenAI : le champ model désigne un profil de l'espace, jamais un fournisseur ni un modèle précis. Tout client compatible OpenAI fonctionne tel quel.

Clés d'API

Les clés se créent dans le module API de l'espace, réservé aux administrateurs. Chaque clé a un nom, et sa propre consommation, mesurée à l'appel et détaillée sur la page de la clé. Le secret, préfixé erha_sk_, n'est affiché qu'une seule fois, à la création. Une clé révoquée est refusée immédiatement.

Restrictions par clé

Chaque clé peut porter deux listes, modifiables depuis le module API (bouton « Restreindre »). Une liste vide ne restreint rien ; les deux s'appliquent ensemble.

ListeCe qui est vérifiéCe que ça protège
Adresses IPL'adresse du client (IPv4, IPv6 ou plage CIDR).Une clé qui fuit ne sert à rien hors des serveurs listés. C'est la restriction à poser sur toute intégration serveur à serveur.
DomainesL'hôte de l'en-tête Origin (Referer à défaut), *. pour les sous-domaines.Une clé utilisée dans une page web ne peut pas être rejouée depuis un autre site : le navigateur fixe lui-même cet en-tête.

La restriction par domaine ne protège pas contre un script, curl ou Postman : hors navigateur, l'appelant écrit l'en-tête Origin qu'il veut. Une clé destinée à un serveur se restreint par adresse IP.

Un appel refusé répond 403 avec le type permission_error et nomme l'adresse ou le domaine refusé, pour que l'intégrateur sache quoi ajouter à la liste.

Dossiers de consommation

Un appel peut nommer un dossier : un module ou une fonctionnalité de votre produit. Sa consommation est alors comptée à part, sur la même clé. Une seule clé suffit pour savoir ce que consomme chaque partie de votre produit.

Le dossier se donne par l'en-tête X-Erha-Folder ou par le paramètre de requête folder.

curl https://api.erha.ai/v1/chat/completions \
  -H "Authorization: Bearer erha_sk_..." \
  -H "Content-Type: application/json" \
  -H "X-Erha-Folder: facturation" \
  -d '{ "model": "reecriture", "messages": [{ "role": "user", "content": "Bonjour" }] }'
curl "https://api.erha.ai/v1/chat/completions?folder=facturation" ...

Avec un SDK OpenAI, l'en-tête est le plus simple : il se pose une fois sur le client (voir SDK compatibles OpenAI).

RègleDétail
FormatLettres minuscules, chiffres, -, _ et ., 64 caractères au plus. Le nom commence et finit par une lettre ou un chiffre. Exemples : facturation, crm.relances, export_pdf.
MajusculesRamenées en minuscules : Facturation et facturation sont le même dossier.
En-tête et paramètreSi les deux sont donnés, ils doivent nommer le même dossier.
Sans dossierL'appel est compté sur la clé, dans la ligne « Sans dossier ».

La consommation de chaque dossier s'affiche dans le module API, dans le détail de la clé : appels, tokens et coût du mois. Un clic sur un dossier filtre l'historique des appels. Le dossier ne change pas la facture : l'appel est facturé sur la clé, comme sans dossier.

Règles de dossier par clé

Chaque clé peut porter deux réglages, modifiables depuis le module API (menu de la clé, « Dossiers »).

RéglageEffet
Dossiers autorisésLes seuls dossiers acceptés. Un appel qui en nomme un autre est refusé. Une liste vide accepte tout dossier bien formé. 50 dossiers au plus.
Dossier obligatoireUn appel qui ne nomme aucun dossier est refusé.

Un appel refusé répond 400 avec le type invalid_request_error. Le message dit quelle règle bloque et, pour un dossier hors liste, donne les dossiers autorisés. Rien n'est consommé.

La liste des dossiers autorisés n'est pas une protection : qui détient la clé peut nommer n'importe quel dossier de la liste. Elle garde des noms stables, pour qu'une faute de frappe soit refusée au lieu d'ouvrir une nouvelle ligne dans le suivi. Pour séparer des droits, par exemple un module qui ne doit pas consommer au nom d'un autre, créez une clé par module.

Authentification

Toutes les requêtes portent la clé dans l'en-tête Authorization.

Authorization: Bearer erha_sk_...

URL de base

Les routes sont servies par l'API, sous /v1 :

https://api.erha.ai/v1

L'adresse console.erha.ai/api/v1 répond encore, relayée vers l'API, pour les intégrations déjà en place. Les nouvelles intégrations utilisent l'adresse de l'API.

Routes

MéthodeRouteRôle
GET/v1/modelsLes profils que la clé peut appeler.
POST/v1/chat/completionsUn appel LLM sur un profil, au format chat completions.

Lister les modèles

GET /v1/models renvoie les profils que la clé peut appeler. Ce sont les valeurs acceptées dans le champ model de POST /v1/chat/completions.

curl https://api.erha.ai/v1/models \
  -H "Authorization: Bearer erha_sk_..."
{
  "object": "list",
  "data": [
    { "id": "reecriture", "object": "model", "owned_by": "erha" },
    { "id": "synthese", "object": "model", "owned_by": "erha" }
  ]
}

Chat completions

POST /v1/chat/completions exécute un appel LLM sur un profil de l'espace. L'appel est mesuré et facturé sur la clé utilisée, et sur le dossier qu'il nomme (voir Dossiers de consommation).

Un appel dispose de 280 secondes. Au-delà, l'API répond 504 et rien n'est facturé : réessayez, ou réduisez la taille de la requête.

ChampTypeDescription
model (requis)stringUn profil de l'espace (voir GET /v1/models).
messages (requis)arrayTableau non vide de messages {role, content}. Les rôles system, user, assistant et tool sont acceptés ; les messages system sont concaténés en instruction système. Le content d'un message user accepte aussi un tableau de parties (voir Pièces jointes).
streambooleanRenvoie un flux SSE au format OpenAI. Le contenu arrive en un seul chunk : c'est le protocole qui est supporté, pas la progressivité.
tools, tool_choiceVoir Tools.
curl https://api.erha.ai/v1/chat/completions \
  -H "Authorization: Bearer erha_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "reecriture",
    "messages": [
      { "role": "system", "content": "Réponds en français." },
      { "role": "user", "content": "Bonjour" }
    ]
  }'

La réponse suit le format chat.completion, avec l'usage en tokens de l'appel.

{
  "id": "erha-5f0c…",
  "object": "chat.completion",
  "created": 1755500000,
  "model": "reecriture",
  "choices": [
    {
      "index": 0,
      "message": { "role": "assistant", "content": "Bonjour !" },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 18,
    "completion_tokens": 5,
    "total_tokens": 23
  }
}

Pièces jointes

À la place d'une chaîne, le champ content d'un message user accepte un tableau de parties : c'est ainsi qu'on joint une image ou un document. Les autres rôles restent en texte.

TypeChampDescription
texttextTexte simple.
image_urlimage_url.urlImage à analyser.
filefile.file_dataDocument à analyser. Un PDF passe ici, jamais en image_url.

Formats acceptés : image/jpeg, image/png, image/gif et image/webp pour les images, application/pdf pour les documents. Tout autre type est refusé en 400, en nommant la partie fautive.

Les pièces jointes voyagent en base64 dans une URL data:. Contrairement à l'API OpenAI, une URL distante (http ou https) est refusée : l'API ne va jamais chercher un fichier sur un hôte désigné par le prompt.

curl https://api.erha.ai/v1/chat/completions \
  -H "Authorization: Bearer erha_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "synthese",
    "messages": [
      {
        "role": "user",
        "content": [
          {
            "type": "file",
            "file": { "file_data": "data:application/pdf;base64,JVBERi0xLjQK..." }
          },
          { "type": "text", "text": "Résume ce document." }
        ]
      }
    ]
  }'
{
  "type": "image_url",
  "image_url": { "url": "data:image/png;base64,iVBORw0KGgo..." }
}

Les documents demandent un profil adossé à Anthropic. Sur un profil OpenAI-compatible, une image passe, mais un document est refusé explicitement plutôt qu'envoyé sans être lu.

Tools

Le format OpenAI est accepté tel quel : tools, tool_choice, et le rôle tool pour renvoyer un résultat. Quand le modèle demande un appel, la réponse porte tool_calls et finish_reason vaut tool_calls.

{
  "model": "reecriture",
  "messages": [{ "role": "user", "content": "Quel temps fait-il à Paris ?" }],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_weather",
        "parameters": {
          "type": "object",
          "properties": { "city": { "type": "string" } },
          "required": ["city"]
        }
      }
    }
  ]
}

Renvoyez le résultat dans un message de rôle tool portant le tool_call_id reçu, puis rappelez la route avec l'historique complet.

{
  "role": "tool",
  "tool_call_id": "call_abc123",
  "content": "18 °C, ensoleillé"
}

Indisponible sur les profils adossés à Anthropic : l'appel est refusé plutôt que servi sans ses tools. Utilisez un profil OpenAI-compatible.

Erreurs

Les erreurs suivent le format OpenAI : un objet error avec un message et un type.

{
  "error": {
    "message": "Invalid or revoked API key.",
    "type": "authentication_error"
  }
}
CodeTypeSignification
400invalid_request_errorRequête invalide : corps non JSON, model ou messages manquant, partie de contenu refusée (type inconnu, media type non supporté, URL distante), ou dossier refusé (mal formé, absent alors que la clé l'exige, hors de la liste de la clé).
401authentication_errorClé absente, mal formée, invalide ou révoquée.
402billing_errorL'espace ne peut pas être facturé : aucun moyen de paiement, ou compte de facturation résilié. Rien n'est consommé ni mesuré.
403permission_errorClé restreinte : l'adresse IP ou le domaine de l'appel n'est pas dans ses listes.
404invalid_request_errorProfil inconnu pour l'espace.
429rate_limit_errorPlus de 300 appels par minute sur la clé.
502api_errorErreur en aval pendant l'exécution de l'appel.
503gateway_unavailableLa passerelle est momentanément indisponible. Rien n'a été consommé : réessayez.
504timeout_errorLe modèle n'a pas répondu en 280 secondes. Rien n'est facturé : réessayez, ou réduisez la taille de la requête.

SDK compatibles OpenAI

Tout client compatible OpenAI fonctionne : pointez baseURL vers l'API et utilisez un profil comme modèle. defaultHeaders (default_headers en Python) est facultatif : il range toute la consommation du client dans un dossier (voir Dossiers de consommation).

import OpenAI from "openai"

const client = new OpenAI({
  baseURL: "https://api.erha.ai/v1",
  apiKey: "erha_sk_...",
  defaultHeaders: { "X-Erha-Folder": "facturation" },
})

const completion = await client.chat.completions.create({
  model: "reecriture",
  messages: [{ role: "user", content: "Bonjour" }],
})
from openai import OpenAI

client = OpenAI(
    base_url="https://api.erha.ai/v1",
    api_key="erha_sk_...",
    default_headers={"X-Erha-Folder": "facturation"},
)

completion = client.chat.completions.create(
    model="reecriture",
    messages=[{"role": "user", "content": "Bonjour"}],
)
Une question sur ERHA ?Dans la console, Ask ERHA répond sur le produit, ses pages et la facturation.Ouvrir la console