API publique v1
Le contrat versionné pour les applications tierces — clé d'API scopée, préfixe /api/public/v1 — whoami, lecture de documents, question à un agent et ajout de documents dans une base.
L'API publique v1 est le contrat stable que Superfasttt offre aux applications
tierces (CRM, ERP, outils métier) : un préfixe versionné, /api/public/v1/, une
authentification par clé d'API scopée, des réponses dont la forme ne change pas
sans une nouvelle version.
Elle exige l'option API publique sur votre espace client. Une clé d'API est créée par un administrateur de l'espace (Paramètres → Clés API), avec uniquement les scopes nécessaires à l'intégration.
Principes
- Une clé = un espace client. L'espace client d'une requête est toujours celui de la clé, jamais un paramètre, un en-tête ou un champ du corps. Une application qui travaille avec plusieurs instances détient une clé par instance et route elle-même chaque appel vers la bonne instance.
- Serveur à serveur. Seules les clés
sk-...sont acceptées, dans l'en-têteAuthorization: Bearer sk-.... Un jeton de session utilisateur est refusé (401). - Versionnée. Rien n'est renommé ni retiré dans
/api/public/v1/: on y ajoute des routes et des champs optionnels. Une rupture passe par unev2publiée à côté, avec le préavis décrit dans Versioning.
GET /api/public/v1/whoami
Scope requis : public:whoami.
Rend l'espace client (l'instance) auquel appartient la clé, ainsi que l'identité publique de la clé. C'est la sonde à appeler au démarrage de votre intégration, ou après un changement de configuration, pour vérifier que l'URL et la clé désignent bien l'instance attendue.
curl -s https://<votre-instance>/api/public/v1/whoami \
-H "Authorization: Bearer sk-xxxxxxxxxxxxxxxxxxxxxxxx"{
"api_version": "v1",
"tenant": { "id": "acme", "name": "Acme", "slug": "acme", "domain": null },
"key": {
"id": "3f1e0f0c-8d5c-4d8e-9b8e-2a1c3b4d5e6f",
"prefix": "sk-1a2b",
"name": "CRM",
"scopes": ["public:whoami"],
"expires_at": null
},
"environment": "production"
}- Le secret de la clé n'est jamais renvoyé.
- L'appel ne compte pas comme une utilisation de la clé : son compteur d'utilisation, sa date de dernière utilisation et son quota journalier restent inchangés. La limite par minute s'applique, et l'appel est tracé dans le journal de la clé.
- Comparez
tenant.idà l'identifiant attendu pour cette instance : une différence signale une URL ou une clé inversée.
POST /api/public/v1/documents/parse
Scope requis : documents:parse.
Lit un document et rend son texte, sans rien stocker sur l'instance (ni document, ni index). Le fichier est lu par la même chaîne que l'import de documents de l'instance : le lecteur intégré, ou un service d'OCR si l'instance en a configuré un et que sa politique de données l'autorise.
curl -s https://<votre-instance>/api/public/v1/documents/parse \
-H "Authorization: Bearer sk-xxxxxxxxxxxxxxxxxxxxxxxx" \
-F "file=@bail.pdf"{
"api_version": "v1",
"filename": "bail.pdf",
"parsing_mode": "mistral:ocr",
"format": "markdown",
"content": "# Bail commercial\n\n| Surface | Loyer |\n\n## Annexe",
"characters": 49,
"page_count": 2,
"pages": ["# Bail commercial\n\n| Surface | Loyer |", "## Annexe"]
}formatvautmarkdownquand un OCR a lu le document (titres et tableaux conservés),textpour le lecteur intégré.pagesdonne le texte de chaque page quand le document est lu page par page (PDF, OCR) ; sinonpagesetpage_countvalentnull.- Envoyez un seul fichier, dans le champ
file, avec un en-têteContent-Length(les envois « chunked » sont refusés). - Types acceptés : pdf, docx, xlsx, txt, md, mdx, html, csv ; 50 Mo et 500 pages au plus ; le texte rendu est limité à 5 millions de caractères.
- L'appel compte comme une utilisation de la clé ; le coût d'un OCR externe est
imputé au budget IA de l'instance. La réponse n'est jamais mise en cache
(
Cache-Control: no-store).
| Statut | error | Cause |
|---|---|---|
400 | malformed_multipart | Corps multipart invalide, plus d'une partie, ou partie autre que file |
411 | length_required | En-tête Content-Length absent |
413 | file_too_large | Fichier de plus de 50 Mo |
413 | too_many_pages | PDF de plus de 500 pages |
413 | parsed_content_too_large | Texte lu de plus de 5 millions de caractères |
415 | unsupported_file_type | Type non accepté |
422 | missing_file | Aucun fichier dans la requête |
422 | empty_file / unreadable_file | Fichier vide, chiffré, corrompu ou sans texte lisible |
429 | budget_exceeded | Budget IA de l'instance épuisé |
502 | parser_unavailable | Le service de lecture a échoué : réessayez plus tard |
Les refus communs à toute l'API publique (clé, scope, option, IP, limites, état de l'espace client) sont décrits dans Erreurs.
POST /api/public/v1/agents/{agent_id}/ask
Scope requis : agents:ask.
Pose une question à un agent de l'instance et rend sa réponse, avec les sources sur lesquelles elle s'appuie. L'agent tourne exactement comme dans les applications de l'instance (mêmes droits, mêmes outils, même modèle, même budget) ; la réponse n'est pas diffusée en continu.
curl -s https://<votre-instance>/api/public/v1/agents/<agent_id>/ask \
-H "Authorization: Bearer sk-xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"question": "Quel est le loyer annuel du bail de la rue de Rivoli ?"}'{
"api_version": "v1",
"agent_id": "8b1f…",
"conversation_id": "c42e…",
"answer": "Le loyer annuel est de 120 000 € HT.",
"sources": [
{
"origin": "knowledge_base",
"source_id": "d71a…#3",
"document_id": "d71a…",
"title": "Bail Rivoli.pdf",
"excerpt": "Loyer annuel : 120 000 € HT",
"score": 0.82,
"url": null
}
],
"iteration_budget_reached": false
}- L'agent doit être visible par le compte qui a créé la clé (son propriétaire,
un partage, un agent public de l'espace) ; sinon il n'existe pas pour la clé
(
404 agent_not_found). sourcesréunit les passages de documents cités (origin: knowledge_base) et les pages web consultées par les outils de l'agent (origin: tool, avecurl).- Conversation : la réponse porte un
conversation_id; renvoyez-le avec la question suivante pour rester dans le même fil. Seule la clé qui a ouvert une conversation peut la poursuivre : toute autre conversation, y compris celles du compte qui a créé la clé, répond404 conversation_not_found. - Mémoire personnelle : l'appel ne lit ni n'alimente la mémoire du compte qui a créé la clé.
iteration_budget_reachedvauttruequand l'agent a épuisé son nombre d'étapes : la réponse peut être incomplète.- L'appel compte comme une utilisation de la clé ; il consomme le budget IA de
l'instance et suit la limite par minute des appels IA. La réponse n'est jamais
mise en cache (
Cache-Control: no-store).
| Statut | error | Cause |
|---|---|---|
403 | module_not_licensed | Le module Agents n'est pas actif sur l'espace client |
403 | model_disabled / model_access_denied | Le modèle de l'agent est désactivé sur l'espace client, ou réservé à d'autres comptes |
403 | resource_not_allowed | La clé est restreinte à certaines ressources : elle ne peut pas lancer un agent |
404 | agent_not_found | Agent inconnu, supprimé, ou non visible par la clé |
404 | conversation_not_found | Conversation inconnue, ou ouverte par une autre clé |
409 | approval_required | L'agent s'est arrêté sur une étape qui demande une validation humaine, possible seulement dans l'application Agents (conversation_id fourni quand la question poursuivait une conversation ; une première question suspendue n'est pas conservée) |
422 | invalid_request | Corps JSON invalide : question manquante ou vide, champ inconnu |
422 | final_approval_unsupported_channel… | L'agent exige une validation humaine de sa réponse, impossible sur ce canal |
429 | budget_exceeded | Budget IA ou plafond de tokens de l'instance atteint (code précise lequel) |
429 | llm_upstream_rate_limited | Le fournisseur du modèle limite les appels : patientez quelques minutes |
502 | llm_upstream_error / llm_upstream_auth | Le fournisseur du modèle a échoué ou refusé la clé de l'instance |
503 | grounding_infra_unavailable | Les documents de l'agent n'ont pas pu être consultés : réessayez |
504 | llm_upstream_timeout | Le fournisseur du modèle n'a pas répondu à temps |
POST /api/public/v1/knowledge-bases/{kb_id}/documents
Scope requis : knowledge_bases:ingest.
Ajoute un document (en markdown) à une base de connaissances de l'instance, où ses agents et ses recherches le trouvent, ou le remplace s'il y est déjà.
curl -s https://<votre-instance>/api/public/v1/knowledge-bases/<kb_id>/documents \
-H "Authorization: Bearer sk-xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"external_id": "bail-42",
"source_feed": "crm-baux",
"title": "Bail Rivoli",
"content_markdown": "# Bail Rivoli\n\nLoyer annuel : 120 000 € HT",
"metadata": { "ville": "Paris" }
}'{
"api_version": "v1",
"knowledge_base_id": "5e0c…",
"id": "a41b…",
"external_id": "bail-42",
"source_feed": "crm-baux",
"action": "created"
}- Idempotent. Un document est identifié dans la base par
source_feedetexternal_id, que vous choisissez. Le renvoyer le remplace (updated) ; le renvoyer à l'identique — même texte, même titre, mêmesmetadata— ne fait rien (unchanged, rien n'est réindexé).idreste le même. source_feednomme votre flux (minuscules, chiffres,_,.,-) : choisissez-le une fois et gardez-le, il ne dépend pas de la clé (une clé renouvelée retrouve ses documents).- La base doit être modifiable par le compte qui a créé la clé (il en est
propriétaire, ou elle lui est partagée en édition). Si la clé est restreinte à
certaines ressources, la base doit en faire partie. Sinon elle n'existe pas pour la
clé (
404). - Pour un fichier, lisez-le d'abord avec
POST /api/public/v1/documents/parse(ci-dessus) et envoyez soncontent. metadata: des champs simples (texte, nombre, booléen), 50 au plus, enregistrés avec chaque passage du document.- Le texte est indexé pendant l'appel ; l'appel compte comme une utilisation de la clé et son indexation est comptée dans le budget RAG de l'instance.
| Statut | error | Cause |
|---|---|---|
403 | knowledge_base_read_only | Le compte qui a créé la clé peut lire la base, pas la modifier |
404 | knowledge_base_not_found | Base inconnue, interne à l'instance, ou que la clé ne peut pas modifier |
409 | indexing_conflict | Le même document est en cours d'indexation par un autre appel : réessayez |
413 | content_too_large | Texte plus long que ce que les réglages de la base acceptent |
422 | invalid_request | Corps JSON invalide : champ manquant, vide ou inconnu, texte blanc, source_feed mal formé |
422 | reserved_metadata_key | Une clé de metadata est réservée par l'instance (title, tenant_id, text…) |
422 | unindexable_content | Rien d'indexable dans le texte, ou metadata refusées par le schéma de la base |
422 | ai_egress_blocked | La politique de données de l'instance interdit le modèle d'indexation de la base |
429 | budget_exceeded | Budget RAG de l'instance épuisé |
POST /api/public/v1/knowledge-bases/{kb_id}/search
Scope requis : knowledge_bases:search.
Rend les passages d'une base de connaissances les plus proches d'une question, le plus
pertinent d'abord, chacun avec ce qui le cite : le document, sa page et sa section quand on
les connaît. Ce sont les passages qu'un agent de l'instance citerait : document_id est le
même que dans les sources de POST /api/public/v1/agents/{agent_id}/ask. Les réglages de
recherche de la base s'appliquent (mode, nombre de passages, reclassement, seuil).
curl -s https://<votre-instance>/api/public/v1/knowledge-bases/<kb_id>/search \
-H "Authorization: Bearer sk-xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "query": "Quel est le loyer annuel ?", "limit": 5 }'{
"api_version": "v1",
"knowledge_base_id": "5e0c…",
"hits": [
{
"document_id": "bail-rivoli.pdf",
"chunk_index": 3,
"title": "Bail Rivoli",
"excerpt": "Le loyer annuel est fixé à 120 000 € HT…",
"page": 4,
"section": "Conditions financières",
"score": 0.82
}
]
}| Champ | Description |
|---|---|
query | La question, en mots (1 à 2 000 caractères) |
limit | Nombre de passages au plus (1 à 50) ; absent, c'est le réglage de la base |
La base doit être lisible par le compte qui a créé la clé et, si la clé est restreinte à
certaines ressources, en faire partie ; sinon elle n'existe pas pour la clé (404).
| Statut | error | Cause |
|---|---|---|
400 | invalid_request | La recherche a refusé la requête telle que construite |
404 | knowledge_base_not_found | Base inconnue, interne à l'instance, ou que la clé ne peut pas lire |
409 | unsearchable_knowledge_base | Base indexée avec plusieurs modèles, sans index actif pour choisir |
422 | invalid_request | Corps JSON invalide : query manquante ou vide, limit hors bornes, champ inconnu |
422 | unsearchable_knowledge_base | Aucun modèle d'indexation n'est configuré pour cette base |
422 | ai_egress_blocked | La politique de données de l'instance interdit le modèle d'indexation de la base |
429 | budget_exceeded | Budget RAG de l'instance épuisé |
503 | search_unavailable | L'index de recherche est momentanément indisponible : réessayez (en-tête Retry-After) |
Erreurs
Tout refus d'authentification ou d'autorisation porte un code stable :
{ "detail": { "error": "insufficient_scope", "message": "...", "required_scope": "public:whoami" } }| Statut | error | Cause |
|---|---|---|
401 | invalid_api_key | Clé absente, inconnue, révoquée ou expirée, ou jeton qui n'est pas une clé sk- |
403 | insufficient_scope | La clé n'a pas le scope requis |
403 | option_not_licensed | L'option API publique n'est pas active sur l'espace client |
403 | ip_not_allowed | Adresse IP hors de la liste autorisée de la clé |
403 | tenant_suspended, tenant_portability_only… | L'espace client n'accepte pas cet appel dans son état actuel |
429 | rate_limit_exceeded | Limite par minute, ou quota journalier de la clé, dépassé (en-tête Retry-After) |
503 | tenant_status_unavailable | L'état de l'espace client ne peut pas être vérifié : réessayez (en-tête Retry-After) |
Les erreurs 404/405 de routage, 422 de validation et 500 suivent le
format général.

