Documentation API

Toute la puissance de Qubly sans le widget : upload de documents, interrogation RAG en streaming, gestion de vos bases et dossiers. API REST + Server-Sent Events, JSON partout. Chaque appel documenté est testable en direct sur cette page : connectez une clé dans le Playground, puis utilisez les boutons « Tester ».

BASE https://api.qubly.io
Documentation complète hors ligne : Markdown JSON

Démarrage

Chaque appel part de la console : créez un compte gratuit sur app.qubly.io, votre base « Base principale » et votre première clé API sont prêtes d'office.

  1. Créez le compte → la clé principale s'affiche (une seule fois)
  2. Uploadez un premier document (console ou API)
  3. Interrogez-le via POST /v1/chat ou le widget

Authentification

Toutes les routes passent par l'en-tête x-api-key. Les clés ont la forme rag_<préfixe>_<secret> ; seul leur SHA-256 est stocké, elles sont révocables instantanément.

curl https://api.qubly.io/v1/knowledge-bases \
  -H "x-api-key: rag_votre_cle"
Une clé ne voit que les données de son tenant : chaque requête est isolée côté base métier et vecteurs, même en cas de compromission.

Périmètre des clés

PérimètreCréationAccès
FULL (défaut){ "scope": "FULL" }Tout, y compris la gestion (clés, bases, compte, facturation)
CHAT{ "scope": "CHAT" }Chat, recherche, upload, lecture et dossiers — jamais la gestion (403 forbidden_scope)
Une clé exposée publiquement (widget, page statique) doit être CHAT : même volée, elle ne peut ni supprimer vos données ni gérer votre compte.

Chat — RAG en streaming

POST /v1/chat

Pipeline complet : vectorisation de la question → recherche dans la base → réponse générée uniquement à partir des extraits retrouvés, avec sources. La réponse arrive en Server-Sent Events. L'assistant connaît l'arborescence de dossiers de la base : demandez-lui « où se trouve le document X ».

Corps de la requête

ChampTypeDescription
knowledgeBaseIdstringUUID de la base à interroger
messagesarrayHistorique ; le dernier message user est la question
topKint (défaut 5)Nombre d'extraits injectés dans le contexte

Événements SSE reçus

TypePayloadQuand
sourcesdocuments[]Avant la réponse : documents (avec dossier) et scores utilisés
deltacontentFragment de réponse (markdown)
donemessageIdFin du flux
errorcode, messageErreur en cours de flux
curl -N https://api.qubly.io/v1/chat \
  -H "x-api-key: rag_votre_cle" \
  -H "Content-Type: application/json" \
  -d '{
    "knowledgeBaseId": "90dfe604-…",
    "messages": [{ "role": "user", "content": "Quelles sont vos conditions de résiliation ?" }]
  }'

data: {"type":"sources","documents":[{"id":"…","name":"CGV-2026.pdf","folder":"Factures / 2026","score":0.87}]}
data: {"type":"delta","content":"Vos contrats sont résiliables"}
data: {"type":"delta","content":" à tout moment après le 12ᵉ mois…"}
data: {"type":"done","messageId":"…"}
Le flux continue d'écrire même après un code 200 : les erreurs de pipeline arrivent en événement error (le flux est déjà ouvert). Consommez avec text/event-stream et fermez la connexion à done.
POST /v1/search

Le cœur du RAG sans la génération : vectorise la question et renvoie les extraits les plus proches avec leur score. Idéal pour l'autocomplétion, la recherche interne, ou pour bâtir votre propre couche LLM — réponse JSON immédiate, aucun token de génération consommé.

ChampTypeDescription
knowledgeBaseIdstringUUID de la base à interroger
querystring (2–2000)La question / requête en langage naturel
topKint (défaut 6, max 50)Nombre d'extraits renvoyés
curl https://api.qubly.io/v1/search \
  -H "x-api-key: rag_votre_cle" \
  -H "Content-Type: application/json" \
  -d '{ "knowledgeBaseId": "90dfe604-…", "query": "conditions de résiliation", "topK": 3 }'

→ 200 { "results": [
  { "documentId": "…", "documentName": "CGV-2026.pdf", "chunkIndex": 4,
    "score": 0.87, "content": "Les contrats sont résiliables à tout moment…" } ] }
Burst : 30 recherches/min par tenant (429 + Retry-After).

Upload de documents

POST /v1/knowledge-bases/:kbId/documents

Multipart, champ file — 25 Mo max. Un second champ optionnel folderId range le document dans un dossier dès l'upload. Formats acceptés :

FamilleFormatsTraitement
Bureautique.pdf .docx .xlsxParsing natif ; OCR automatique pour les PDF scannés
Texte & données.txt .md .csv .json .xml .htmlStructure conservée (titres, lignes, cellules)
Images.png .jpg .webp .gif .bmp .tiff .heicOCR Mistral (HEIC converti automatiquement)
Audio.mp3 .wav .m4a .ogg .flacTranscription Voxtral
Vidéo.mp4 .webm .mov .mkv .aviPiste audio extraite (ffmpeg) puis transcription

L'ingestion est asynchrone : la réponse est immédiate (202, statut PENDING), le document est ensuite parsé, découpé, vectorisé et indexé par le worker.

curl https://api.qubly.io/v1/knowledge-bases/90dfe604-…/documents \
  -H "x-api-key: rag_votre_cle" \
  -F "file=@CGV-2026.pdf;type=application/pdf"
→ 202 { "document": { "id": "…", "status": "PENDING", … } }
POST /v1/knowledge-bases/:kbId/import-url

Importe une page web : le worker la scrape (rendu HTML nettoyé, tableaux et titres conservés) et indexe son contenu. Seules les URL publiques http/https sont acceptées (garde anti-SSRF : les adresses internes sont refusées).

curl https://api.qubly.io/v1/knowledge-bases/90dfe604-…/import-url \
  -H "x-api-key: rag_votre_cle" -H "Content-Type: application/json" \
  -d '{"url": "https://exemple.fr/article"}'
→ 202 { "document": { "format": "URL", "status": "PENDING", … } }

Pour suivre l'avancement :

GET /v1/knowledge-bases/:kbId/documents/:documentId
StatutSens
PENDINGEn file d'ingestion
INDEXEDVectorisé et interrogeable (avec chunkCount)
FAILEDÉchec (champ error explicatif)

Bases de connaissances

GET /v1/knowledge-bases

Liste des bases du tenant.

POST /v1/knowledge-bases
{ "name": "Support technique" }
PATCH /v1/knowledge-bases/:kbId

Renomme la base ou active le rangement automatique : les nouveaux documents sont classés dans l'arborescence par l'IA après indexation.

{ "name": "Support technique", "autoClassify": true }
DELETE /v1/knowledge-bases/:kbId

Supprime la base : purge de tous ses vecteurs puis cascade sur les documents.

Documents

GET /v1/knowledge-bases/:kbId/documents?limit=50&offset=0

Liste paginée (limit ≤ 100). Filtre optionnel ?folderId= (UUID d'un dossier, ou root pour les documents non rangés). Réponse : { documents, total, limit, offset }.

DELETE /v1/knowledge-bases/:kbId/documents/:documentId

Supprime le document et purge ses vecteurs immédiatement.

Dossiers et rangement automatique

Chaque base possède une arborescence de dossiers (5 niveaux max). Les documents non rangés restent « à la racine ». L'IA peut classer automatiquement les documents (nom + extrait du contenu), et le chat connaît l'arborescence : il peut dire où se trouve un document.

GET /v1/knowledge-bases/:kbId/folders

Arborescence complète avec compteurs de documents par dossier, plus le nombre de documents non rangés.

→ { "tree": [{ "id": "…", "name": "Factures", "documentCount": 12,
       "children": [{ "id": "…", "name": "2026", "documentCount": 5, … }] }],
     "rootDocumentCount": 3 }
POST /v1/knowledge-bases/:kbId/folders  { "name": "Factures", "parentId": null }
PATCH /v1/folders/:folderId  { "name": "…", "parentId": "…" }

Crée, renomme ou déplace un dossier (les cycles sont refusés).

DELETE /v1/folders/:folderId

Supprime le dossier : ses documents et sous-dossiers remontent d'un niveau (aucun document n'est supprimé).

PATCH /v1/knowledge-bases/:kbId/documents/:documentId  { "folderId": "…" | null }

Déplace un document (null = retour à la racine).

POST /v1/knowledge-bases/:kbId/classify  { "all": false }

Rangement automatique : l'IA classe les documents non rangés (ou toute la base avec {"all":true}) par lots, en réutilisant les dossiers existants. Réponse : { "summary": { "classified", "foldersCreated", "skipped" } }.

Clés API

GET /v1/api-keys
POST /v1/api-keys  { "name": "Site vitrine", "scope": "CHAT" }
DELETE /v1/api-keys/:keyId

10 clés actives max par tenant. La clé créée s'affiche une seule fois (seul le hash est conservé). Créez une clé dédiée par intégration — le widget, par exemple, doit avoir la sienne (périmètre CHAT), révocable indépendamment. Ces routes exigent une clé FULL ou la session console.

Usage & quotas

GET /v1/usage

Consommation courante et limites du plan (bases, documents, chunks, chats du jour). Au-delà : 402 quota_exceeded. Burst : 10 chats/min et 10 uploads/min par tenant (429 + en-tête Retry-After).

Widget

Pas une ligne d'intégration à écrire — un <script> sur votre site :

<script
  src="https://widget.qubly.io/widget.js"
  data-api-url="https://api.qubly.io"
  data-api-key="rag_cle_dediee_widget"
  data-kb-id="90dfe604-…"
  data-title="Assistant Qubly"
  data-greeting="Bonjour — comment puis-je vous aider ?"
  data-placeholder="Posez votre question…"
  data-color="#4f46e5"       <!-- couleur d'accent (défaut zinc) -->
  data-position="right"      <!-- left | right -->
  async
></script>

22 Ko (9,7 gzip), rendu markdown, aucune dépendance. La conversation du visiteur est conservée dans son navigateur (localStorage, par base de connaissances) : il retrouve son historique en revenant sur la page, et peut l'effacer d'un clic. Testez-le sur la page de démo.

Playground — vos clés, vos appels

Cliquez sur « Utiliser ma session Qubly » (si vous êtes connecté à la console) : tous les boutons « Tester » de cette page fonctionnent immédiatement via votre session. Sans session, collez une clé API (périmètre CHAT suffit pour lecture/chat/upload) — elle reste dans votre navigateur (localStorage, jamais envoyée ailleurs qu'à api.qubly.io).

Codes d'erreur

HTTPerrorCause
400bad_requestCorps invalide (détails dans details)
401unauthorizedClé absente, invalide ou révoquée
402quota_exceededQuota du plan atteint
403forbidden_scopeRoute de gestion appelée avec une clé CHAT
404not_foundRessource inconnue ou hors de votre tenant
413payload_too_largeFichier > 25 Mo
415unsupported_media_typeFormat non supporté (bureautique, texte, données, image, audio, vidéo)
429rate_limitedBurst dépassé — respecter Retry-After
501billing_not_configuredFonctionnalité non activée sur l'instance

Une question, un cas d'usage non couvert ? La console affiche vos identifiants et l'état de vos bases en temps réel.