Superfasttt

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ête Authorization: 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 une v2 publié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"]
}
  • format vaut markdown quand un OCR a lu le document (titres et tableaux conservés), text pour le lecteur intégré.
  • pages donne le texte de chaque page quand le document est lu page par page (PDF, OCR) ; sinon pages et page_count valent null.
  • Envoyez un seul fichier, dans le champ file, avec un en-tête Content-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).
StatuterrorCause
400malformed_multipartCorps multipart invalide, plus d'une partie, ou partie autre que file
411length_requiredEn-tête Content-Length absent
413file_too_largeFichier de plus de 50 Mo
413too_many_pagesPDF de plus de 500 pages
413parsed_content_too_largeTexte lu de plus de 5 millions de caractères
415unsupported_file_typeType non accepté
422missing_fileAucun fichier dans la requête
422empty_file / unreadable_fileFichier vide, chiffré, corrompu ou sans texte lisible
429budget_exceededBudget IA de l'instance épuisé
502parser_unavailableLe 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).
  • sources réunit les passages de documents cités (origin: knowledge_base) et les pages web consultées par les outils de l'agent (origin: tool, avec url).
  • 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épond 404 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_reached vaut true quand 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).
StatuterrorCause
403module_not_licensedLe module Agents n'est pas actif sur l'espace client
403model_disabled / model_access_deniedLe modèle de l'agent est désactivé sur l'espace client, ou réservé à d'autres comptes
403resource_not_allowedLa clé est restreinte à certaines ressources : elle ne peut pas lancer un agent
404agent_not_foundAgent inconnu, supprimé, ou non visible par la clé
404conversation_not_foundConversation inconnue, ou ouverte par une autre clé
409approval_requiredL'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)
422invalid_requestCorps JSON invalide : question manquante ou vide, champ inconnu
422final_approval_unsupported_channel…L'agent exige une validation humaine de sa réponse, impossible sur ce canal
429budget_exceededBudget IA ou plafond de tokens de l'instance atteint (code précise lequel)
429llm_upstream_rate_limitedLe fournisseur du modèle limite les appels : patientez quelques minutes
502llm_upstream_error / llm_upstream_authLe fournisseur du modèle a échoué ou refusé la clé de l'instance
503grounding_infra_unavailableLes documents de l'agent n'ont pas pu être consultés : réessayez
504llm_upstream_timeoutLe 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_feed et external_id, que vous choisissez. Le renvoyer le remplace (updated) ; le renvoyer à l'identique — même texte, même titre, mêmes metadata — ne fait rien (unchanged, rien n'est réindexé). id reste le même.
  • source_feed nomme 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 son content.
  • 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.
StatuterrorCause
403knowledge_base_read_onlyLe compte qui a créé la clé peut lire la base, pas la modifier
404knowledge_base_not_foundBase inconnue, interne à l'instance, ou que la clé ne peut pas modifier
409indexing_conflictLe même document est en cours d'indexation par un autre appel : réessayez
413content_too_largeTexte plus long que ce que les réglages de la base acceptent
422invalid_requestCorps JSON invalide : champ manquant, vide ou inconnu, texte blanc, source_feed mal formé
422reserved_metadata_keyUne clé de metadata est réservée par l'instance (title, tenant_id, text…)
422unindexable_contentRien d'indexable dans le texte, ou metadata refusées par le schéma de la base
422ai_egress_blockedLa politique de données de l'instance interdit le modèle d'indexation de la base
429budget_exceededBudget 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
    }
  ]
}
ChampDescription
queryLa question, en mots (1 à 2 000 caractères)
limitNombre 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).

StatuterrorCause
400invalid_requestLa recherche a refusé la requête telle que construite
404knowledge_base_not_foundBase inconnue, interne à l'instance, ou que la clé ne peut pas lire
409unsearchable_knowledge_baseBase indexée avec plusieurs modèles, sans index actif pour choisir
422invalid_requestCorps JSON invalide : query manquante ou vide, limit hors bornes, champ inconnu
422unsearchable_knowledge_baseAucun modèle d'indexation n'est configuré pour cette base
422ai_egress_blockedLa politique de données de l'instance interdit le modèle d'indexation de la base
429budget_exceededBudget RAG de l'instance épuisé
503search_unavailableL'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" } }
StatuterrorCause
401invalid_api_keyClé absente, inconnue, révoquée ou expirée, ou jeton qui n'est pas une clé sk-
403insufficient_scopeLa clé n'a pas le scope requis
403option_not_licensedL'option API publique n'est pas active sur l'espace client
403ip_not_allowedAdresse IP hors de la liste autorisée de la clé
403tenant_suspended, tenant_portability_only…L'espace client n'accepte pas cet appel dans son état actuel
429rate_limit_exceededLimite par minute, ou quota journalier de la clé, dépassé (en-tête Retry-After)
503tenant_status_unavailableL'é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.

On this page