Knowledge
Le knowledge c'est la source de vérité de l'assistant IA pour tout ce qui n'est pas un produit : politique de livraison, fenêtre de retours, guides de tailles, histoire de la marque, FAQ. Poussez-le une fois et l'assistant ancre ses réponses sur ce que vous avez écrit, au lieu de deviner.
Quatre types d'entrées knowledge sont supportés :
snippet: réponse courte en texte brut pour une question donnée.article: contenu HTML plus long (un article de blog, un article de help-center).webpage: pointez Humind vers une URL publique et on fetch la page pour vous.file: uploadez un PDF, DOCX, Markdown ou document texte. Flow en deux étapes : crée la metadata, puis upload le fichier vers une URL pré-signée.
Les endpoints ici couvrent create, read, update, replace, archive et batch sur les entrées knowledge, y compris leurs tags, périodes d'activité et traductions embarquées.
L'objet Knowledge
| Champ | Type | Requis | Description |
|---|---|---|---|
external_id | string | Oui (à la création) | Votre identifiant unique pour cette entrée. Free-form, doit être unique par company. Utilisé comme clé d'upsert. |
humind_id | string | Renvoyé seulement | 24 caractères hex ObjectId qu'Humind assigne. Stable pendant toute la vie de l'entrée. |
type | 'snippet' | 'article' | 'webpage' | 'file' | Oui (à la création) | Discriminateur. Détermine lequel de content, content_html, url, ou original_filename + mime_type est requis. Voir les sections par type ci-dessous. |
title | string | Oui | Titre court utilisé pour l'affichage, la recherche et le contexte de grounding de l'assistant. |
tags | string[] | Non | Noms de tags. Le serveur les résout en ids et crée tout tag qui n'existe pas encore. Voir Tags. |
is_available_for_ai_agent | boolean | Non | Défaut true. Mettez false pour garder l'entrée dans votre librairie sans l'exposer à l'assistant. |
status | 'draft' | 'published' | 'archived' | 'queued' | 'failed' | Non | Défaut published à la création. queued/failed sont réservés aux fetches webpage : voir Le type webpage. |
active_from | ISO 8601 | Non | Date la plus tôt à laquelle l'assistant peut utiliser cette entrée. Voir Période d'activité. |
active_until | ISO 8601 | Non | Date la plus tard à laquelle l'assistant peut utiliser cette entrée. |
default_language | string | Non | Code ISO 639-1 de la langue des champs top-level. |
translations | object[] | Non | Overrides par locale. Voir Translations. |
created_at | ISO 8601 | Renvoyé seulement | Timestamp de création (UTC). |
updated_at | ISO 8601 | Renvoyé seulement | Timestamp de dernière modification (UTC). |
Champs spécifiques par type
Le discriminateur type décide quels champs body sont requis.
| Type | Requis | Optionnel | Notes |
|---|---|---|---|
snippet | content | - | Texte brut. Réponses courtes et focalisées. |
article | content_html | excerpt, slug | Body HTML. Sanitizé côté serveur, voir HTML content. excerpt et slug sont auto-dérivés s'ils sont omis. |
webpage | url | content_html | Si content_html est omis, Humind fetch l'URL en async. Voir Le type webpage. |
file | original_filename, mime_type | - | Le fichier lui-même est uploadé hors-bande via une URL pré-signée. Voir Le type file. |
| Champ | Type | Description |
|---|---|---|
content | string | Body texte brut pour type='snippet'. |
content_html | string | Body HTML pour type='article', ou snapshot de la page pour type='webpage'. |
excerpt | string | Optionnel pour type='article'. Résumé court affiché dans la liste knowledge du dashboard. Max 500 caractères. Quand il est omis, l'API dérive un excerpt de 160 caractères depuis content_html ; les articles publiés en ont toujours un. Fournissez-le explicitement pour une accroche soignée. |
slug | string | Optionnel pour type='article'. Slug URL-safe, en minuscules, séparé par des tirets. Max 255 caractères. Dérivé du title quand il est omis (ASCII en minuscules, les non-alphanumériques remplacés par des tirets). Même politique d'auto-dérivation qu'excerpt. |
url | string | URL HTTPS pour type='webpage'. Doit être joignable publiquement. Pour les clés live, l'API rejette http://, la syntaxe userinfo@host (https://example.com@attacker.com), localhost et tout *.localhost, l'host 0.0.0.0, les ranges IPv4 RFC 1918 / loopback / link-local / CGN, le loopback IPv6 (::1) et IPv6 ULA / link-local, ainsi que les suffixes internes Kubernetes (*.svc.cluster.local, *.cluster.local, *.internal) — tout renvoie 422 invalid_url. Les clés test (hmd_test_*) skip ces checks pour que les marchands puissent développer en local avec des tunnels ou http://localhost. |
original_filename | string | Nom d'affichage pour type='file'. L'API strip les composants de répertoire (/ et \), les caractères de contrôle, et les segments . / .. standalone avant le stockage — seul le basename est conservé. La longueur finale doit faire 1–255 caractères après sanitization. |
mime_type | string | MIME type pour type='file'. Whitelisté, voir Le type file. |
file_size | integer | Renvoyé seulement. Octets. null tant que l'upload est pending. |
file_url | string | Renvoyé seulement. Chemin relatif opaque ; ne pas parser. null tant que l'upload est pending. |
uploaded_at | ISO 8601 | Renvoyé seulement. Quand le fichier a fini de s'uploader. null jusqu'à ce moment-là. |
Champs cachés
Le dashboard peut afficher des champs que l'API publique ne renvoie pas ; traitez-les comme internes. Vous pouvez ignorer en sécurité tout ce que vous voyez dans le dashboard qui n'est pas documenté ici.
HTML content
Le champ content_html : utilisé par les types article et webpage : accepte du HTML. Humind sanitize le HTML côté serveur avant de le stocker, avec une allowlist fixe :
- Tags préservés :
p, a, br, hr, em, strong, b, i, u, ul, ol, li, h1–h6,blockquote, pre, code, table, thead, tbody, tr, th, td, img, span, div. - Tags strippés :
script, iframe, style, object, embed, form, input, et tout autre tag non listé ci-dessus. - Attributs strippés : event handlers (
onclick,onerror, …),style, et toute URLjavascript:. - Attributs préservés :
hrefsur<a>(uniquementhttps,http,mailtoet URIsdata:valides),src,alt,width,heightsur<img>, plus un set limité de valeursclass.
Le HTML est sanitizé en entrée
Ne comptez pas sur l'API pour préserver votre HTML brut. Les tags comme <script> ou les attributs comme onclick sont strippés silencieusement, votre markup stocké peut être un sous-ensemble strict de ce que vous avez envoyé. Relisez l'entrée après une écriture si vous voulez voir ce qui a été retenu. La même règle s'applique aux snapshots webpage qu'Humind récupère pour vous.
Create or upsert
POST /knowledge crée une nouvelle entrée, ou met à jour une existante si l'external_id existe déjà pour votre company. Les champs body requis dépendent du type.
Scope requis : knowledge:write
type='snippet'
Request
curl -X POST https://api.thehumind.com/public/v1/knowledge \
-H "Authorization: Bearer hmd_live_..." \
-H "Idempotency-Key: 3f1a8d92-7c4b-4e6f-b2a1-d5e9c8f7a3b4" \
-H "Content-Type: application/json" \
-d '{
"external_id": "kb-shipping-faq",
"type": "snippet",
"title": "Shipping delays",
"content": "Standard delivery takes 3-5 business days. Express delivery takes 1-2 business days. Free shipping on orders over 50 EUR.",
"tags": ["shipping", "faq"],
"is_available_for_ai_agent": true,
"status": "published",
"default_language": "en"
}'Response 201 Created ou 200 OK
{
"external_id": "kb-shipping-faq",
"humind_id": "65f1ab9c8e7d4a2b1c3d4e5f",
"type": "snippet",
"title": "Shipping delays",
"content": "Standard delivery takes 3-5 business days. Express delivery takes 1-2 business days. Free shipping on orders over 50 EUR.",
"tags": ["shipping", "faq"],
"is_available_for_ai_agent": true,
"status": "published",
"default_language": "en",
"created_at": "2026-04-25T14:30:00Z",
"updated_at": "2026-04-25T14:30:00Z"
}type='article'
Request
curl -X POST https://api.thehumind.com/public/v1/knowledge \
-H "Authorization: Bearer hmd_live_..." \
-H "Idempotency-Key: 4a5b6c7d-8e9f-4a3b-2c1d-0e9f8a7b6c5d" \
-H "Content-Type: application/json" \
-d '{
"external_id": "kb-sizing-guide-shoes",
"type": "article",
"title": "How to pick the right shoe size",
"content_html": "<p>If you're between two sizes, we recommend going up.</p><p>Our shoes use European sizing. See the chart below to convert from US or UK.</p>",
"tags": ["sizing", "shoes"],
"default_language": "en"
}'Response 201 Created ou 200 OK
{
"external_id": "kb-sizing-guide-shoes",
"humind_id": "65f1cd9e8e7d4a2b1c3d4e60",
"type": "article",
"title": "How to pick the right shoe size",
"content_html": "<p>If you're between two sizes, we recommend going up.</p><p>Our shoes use European sizing. See the chart below to convert from US or UK.</p>",
"tags": ["sizing", "shoes"],
"is_available_for_ai_agent": true,
"status": "published",
"default_language": "en",
"created_at": "2026-04-25T14:30:00Z",
"updated_at": "2026-04-25T14:30:00Z"
}type='webpage'
Vous pouvez soit fournir url et content_html (publication immédiate), soit juste url et laisser Humind fetcher la page pour vous (async).
Request, URL seule, fetch async
curl -X POST https://api.thehumind.com/public/v1/knowledge \
-H "Authorization: Bearer hmd_live_..." \
-H "Idempotency-Key: 7a8b9c0d-1e2f-4a3b-4c5d-6e7f8a9b0c1d" \
-H "Content-Type: application/json" \
-d '{
"external_id": "kb-shipping-policy-page",
"type": "webpage",
"title": "Shipping policy",
"url": "https://merchant.com/shipping-policy",
"tags": ["shipping", "policy"],
"default_language": "en"
}'Response 202 Accepted (fetch en file)
{
"external_id": "kb-shipping-policy-page",
"humind_id": "65f1ef0e8e7d4a2b1c3d4e61",
"type": "webpage",
"title": "Shipping policy",
"url": "https://merchant.com/shipping-policy",
"tags": ["shipping", "policy"],
"is_available_for_ai_agent": true,
"status": "queued",
"default_language": "en",
"created_at": "2026-04-25T14:30:00Z",
"updated_at": "2026-04-25T14:30:00Z"
}L'entrée existe immédiatement avec status: "queued". Humind fetch l'URL en arrière-plan et bascule status à published (ou failed) une fois le fetch terminé. Tant que ce n'est pas fait, l'entrée n'est pas utilisée par l'assistant. Voir Le type webpage pour le polling.
Request, URL avec content_html (publication immédiate)
curl -X POST https://api.thehumind.com/public/v1/knowledge \
-H "Authorization: Bearer hmd_live_..." \
-H "Idempotency-Key: 8b9c0d1e-2f3a-4b4c-5d6e-7f8a9b0c1d2e" \
-H "Content-Type: application/json" \
-d '{
"external_id": "kb-returns-policy-page",
"type": "webpage",
"title": "Returns policy",
"url": "https://merchant.com/returns",
"content_html": "<p>You can return any unworn item within 30 days of delivery.</p>",
"tags": ["returns", "policy"],
"default_language": "en"
}'Response 201 Created
Renvoyée avec status: "published" immédiatement, aucun fetch n'est exécuté puisque vous avez fourni le snapshot.
200 vs 201 vs 202
201 Created: nouvelle entrée, prête à l'usage.200 OK: entrée existante mise à jour en place.202 Accepted: entrée webpage créée, fetch en file. Pollez l'entrée pour voir la transition destatus.
The webpage type
Le type webpage est le chemin de moindre résistance pour du contenu que vous publiez déjà sur votre site. Vous donnez une URL à Humind ; on fetch la page, on enlève la chrome de navigation et on snapshot le body.
Cycle de vie
status | Signification |
|---|---|
queued | Humind n'a pas encore fetché l'URL. L'entrée existe mais n'est pas utilisée par l'assistant. |
published | Fetch réussi. content_html est peuplé et l'assistant ancre ses réponses dessus. |
failed | Fetch échoué (404, bloqué par robots.txt, erreur serveur, timeout, etc.). L'entrée est conservée pour que vous voyiez l'échec ; l'assistant ne l'utilise pas. |
Pour re-déclencher un fetch après un statut failed : corrigez le problème de votre côté, puis PATCH l'entrée avec la même url (ou une nouvelle). Le statut repasse à queued.
Polling
Pas de webhook pour la complétion de fetch. Pollez l'entrée par external_id ou humind_id :
curl https://api.thehumind.com/public/v1/knowledge/api:kb-shipping-policy-page \
-H "Authorization: Bearer hmd_live_..."La plupart des pages publient dans la minute. Si status est toujours queued après quelques minutes, vérifiez que votre URL est publiquement joignable.
Snapshot statique vs fetch live
Le content_html que Humind stocke est un snapshot point-in-time. Il ne se rafraîchit pas automatiquement quand votre page change. Pour récupérer les éditions sur votre site, PATCH l'entrée, envoyer la même url (sans content_html) re-met en file un fetch frais.
List knowledge
GET /knowledge renvoie les entrées qui appartiennent à la company à laquelle la clé API est rattachée.
Scope requis : knowledge:read
| Query parameter | Type | Description |
|---|---|---|
cursor | string | Cursor opaque renvoyé comme next_cursor à la page précédente. Omettez-le au premier appel. |
limit | integer | Items par page (1–100). Défaut 50. |
type | 'snippet' | 'article' | 'webpage' | 'file' | Filtre sur un seul type. |
Request
curl "https://api.thehumind.com/public/v1/knowledge?type=snippet&limit=50" \
-H "Authorization: Bearer hmd_live_..."Response 200 OK
{
"data": [
{
"external_id": "kb-shipping-faq",
"humind_id": "65f1ab9c8e7d4a2b1c3d4e5f",
"type": "snippet",
"title": "Shipping delays",
"tags": ["shipping", "faq"],
"status": "published",
"is_available_for_ai_agent": true,
"created_at": "2026-04-25T14:30:00Z",
"updated_at": "2026-04-25T14:30:00Z"
}
],
"next_cursor": "eyJpZCI6IjY1ZjFjZDllOGU3ZDRhMmIxYzNkNGU2MCJ9"
}Quand next_cursor vaut null ou est absent, vous êtes à la dernière page.
Retrieve a knowledge entry
GET /knowledge/{id} renvoie une seule entrée. Le {id} accepte l'une ou l'autre forme documentée dans Identifiants.
Scope requis : knowledge:read
Request
curl https://api.thehumind.com/public/v1/knowledge/api:kb-shipping-faq \
-H "Authorization: Bearer hmd_live_..."Response 200 OK
Même forme que la réponse de création.
Replace a knowledge entry
PUT /knowledge/{id} fait un remplacement complet : le body de la requête devient l'entrée entière. Les champs que vous n'incluez pas sont reset à leurs défauts (ou unset là où c'est applicable).
Scope requis : knowledge:write
Request
curl -X PUT https://api.thehumind.com/public/v1/knowledge/api:kb-shipping-faq \
-H "Authorization: Bearer hmd_live_..." \
-H "Idempotency-Key: 7a2b3c4d-5e6f-4a8b-9c0d-1e2f3a4b5c6d" \
-H "Content-Type: application/json" \
-d '{
"external_id": "kb-shipping-faq",
"type": "snippet",
"title": "Shipping delays (updated)",
"content": "Standard delivery now takes 2-4 business days. Express delivery is 1 business day. Free shipping on orders over 50 EUR.",
"tags": ["shipping", "faq"],
"default_language": "en"
}'Response 200 OK
Renvoie l'entrée complète, même forme que la réponse de création.
Replace est destructif
PUT enlève tout champ, tag ou traduction non présent dans le body. Pour bumper un seul champ, utilisez plutôt PATCH.
Update a knowledge entry
PATCH /knowledge/{id} fait un update partiel : seuls les champs présents dans le body sont modifiés. Le reste reste comme avant.
Scope requis : knowledge:write
Request, refresher le content d'un snippet
curl -X PATCH https://api.thehumind.com/public/v1/knowledge/api:kb-shipping-faq \
-H "Authorization: Bearer hmd_live_..." \
-H "Idempotency-Key: 9b8a7c6d-5e4f-4a3b-2c1d-0e9f8a7b6c5d" \
-H "Content-Type: application/json" \
-d '{
"content": "Standard delivery takes 2-4 business days. Express is 1 business day."
}'Request, re-fetcher une webpage
curl -X PATCH https://api.thehumind.com/public/v1/knowledge/api:kb-shipping-policy-page \
-H "Authorization: Bearer hmd_live_..." \
-H "Idempotency-Key: ab3c4d5e-6f78-4a9b-8c0d-1e2f3a4b5c6d" \
-H "Content-Type: application/json" \
-d '{
"url": "https://merchant.com/shipping-policy"
}'PATCH-er une entrée webpage avec une url re-met en file un fetch et bascule status à queued.
Response 200 OK
Renvoie l'entrée complète mise à jour.
Les changements de type ne sont pas autorisés
Vous ne pouvez pas switcher un snippet en article (ou tout autre changement cross-type) en PATCH-ant type. Supprimez l'entrée et créez-en une nouvelle avec le bon type.
Archiver (ou supprimer) une entrée knowledge
DELETE /knowledge/{id} fait un soft delete par défaut : le status de l'entrée passe à archived et l'assistant arrête de l'utiliser. Le record reste en base, donc vous pouvez le restaurer plus tard en PATCH-ant le statut à published.
Passez ?force=true pour faire un hard delete : le document est supprimé physiquement de la base après la cascade d'archivage. Il n'y a pas d'un-delete ; à n'utiliser que quand l'entrée doit vraiment disparaître (ex. nettoyage d'une base de test, entrée créée par erreur).
Scope requis : knowledge:write
Query parameters
| Nom | Type | Requis | Description |
|---|---|---|---|
force | boolean | Non | true (ou 1) pour supprimer définitivement l'entrée. Défaut false (soft delete). |
Request — soft delete (par défaut)
curl -X DELETE https://api.thehumind.com/public/v1/knowledge/api:return-policy \
-H "Authorization: Bearer hmd_live_..." \
-H "Idempotency-Key: 1a2b3c4d-5e6f-4789-9abc-def012345678"Request — hard delete
curl -X DELETE 'https://api.thehumind.com/public/v1/knowledge/api:return-policy?force=true' \
-H "Authorization: Bearer hmd_live_..." \
-H "Idempotency-Key: 1a2b3c4d-5e6f-4789-9abc-def012345678"Response 204 No Content
Pas de body.
Soft vs. hard delete
Le soft delete est recommandé pour les opérations marchandes normales : l'entrée disparaît immédiatement du retrieval chat, vous pouvez désarchiver en un PATCH, et les caches downstream (vector index, dashboards) se nettoient via la cascade d'archivage.
Le hard delete (?force=true) sert au nettoyage de la base de connaissances — typiquement une entrée de test qu'on veut totalement enlevée. Le soft delete déclenche d'abord la routine d'archivage ; ?force=true supprime ensuite définitivement la ressource. Les conversations chat passées qui référençaient cette entrée gardent leur snapshot, donc l'historique reste cohérent même quand la source disparaît.
Tags
tags est un array de noms free-form. Le serveur résout chaque nom en id de tag, créant le tag s'il n'existe pas déjà pour votre company :
{
"tags": ["shipping", "faq", "europe"]
}Vous ne pré-créez pas de tags via un endpoint séparé, la première fois que vous référencez un nom de tag, il est créé. L'assistant utilise les tags comme un signal de soft-grouping quand il récupère du knowledge pertinent pour une question.
Gardez les noms de tags stables
Renommer un tag équivaut à en créer un nouveau et à orpheliner l'ancien. Si vous changez régulièrement de taxonomie, taggez plutôt par concepts stables (shipping) que par concepts transitoires (q1-2026-promo).
Si un nom de tag échoue à la résolution (rare), l'API renvoie tag_resolution_failed avec le nom fautif dans details.tag.
Active period
Mettez active_from et/ou active_until pour scoper quand l'assistant peut faire remonter une entrée. Le record reste dans votre librairie toute l'année ; la fenêtre de date n'affecte que si l'assistant ancre sur elle.
| Cas d'usage | Setup recommandé |
|---|---|
| Promo saisonnière (FAQ Black Friday) | active_from = "2026-11-25T00:00:00Z", active_until = "2026-12-02T23:59:59Z" |
| Lancement future-daté | active_from seul, laissez active_until non défini. |
| Politique en sunset | active_until seul, l'assistant arrête de l'utiliser après le cutoff. |
Hors fenêtre, l'entrée est traitée comme si is_available_for_ai_agent valait false. Le statut (published, archived, …) n'est pas changé.
Translations
translations est une liste d'overrides par locale, keyée par language :
{
"default_language": "en",
"title": "Shipping delays",
"content": "Standard delivery takes 3-5 business days.",
"translations": [
{
"language": "fr",
"title": "Délais de livraison",
"content": "La livraison standard prend 3-5 jours ouvrés."
}
]
}Champs par locale que vous pouvez override :
| Champ | Type | S'applique à |
|---|---|---|
title | string | Tous les types |
content | string | snippet |
content_html | string | article, webpage |
Les locales non listées dans translations retombent sur la valeur top-level (en default_language).
Vous pouvez aussi patcher une locale à la fois sans renvoyer l'entrée complète via la sub-resource Translations dédiée.
Translations sub-resource
Pour les entrées knowledge qui existent déjà, vous pouvez patcher une seule locale sans renvoyer le payload complet. Utilisez le champ translations embarqué sur POST / PUT / PATCH pour le seed initial, puis pilotez les updates suivants via ces endpoints.
{lang} est un tag court BCP 47, en, fr, de, pt-BR, zh-CN, etc. La regex de validation est ^[a-z]{2}(-[A-Z]{2})?$. Une valeur invalide renvoie 422 avec code: invalid_lang_format.
La forme du body dépend du type de l'entrée. Le serveur valide contre le schéma correspondant ; envoyer content sur un article (ou content_html sur un snippet) renvoie 422.
Set or update a translation
PUT /knowledge/{id}/translations/{lang} upserte la traduction pour une seule locale. Les champs que vous omettez sont laissés intouchés sur un update ; à la première écriture, ils défaulent à unset.
Scope requis : knowledge:write
Body, type='snippet'
| Champ | Type | Description |
|---|---|---|
title | string | Titre d'affichage localisé. |
content | string | Body texte brut localisé. |
Body, type='article' ou type='webpage'
| Champ | Type | Description |
|---|---|---|
title | string | Titre d'affichage localisé. |
content_html | string | Body HTML localisé. Sanitizé, voir HTML content. |
Tous les champs sont optionnels individuellement, mais le body doit contenir au moins un. Un body vide renvoie 400 validation_failed.
Request, snippet
curl -X PUT https://api.thehumind.com/public/v1/knowledge/api:kb-shipping-faq/translations/fr \
-H "Authorization: Bearer hmd_live_..." \
-H "Idempotency-Key: 5f6a7b8c-9d0e-4f1a-2b3c-4d5e6f7a8b9c" \
-H "Content-Type: application/json" \
-d '{
"title": "Délais de livraison",
"content": "La livraison standard prend 3-5 jours ouvrés."
}'Request, article ou webpage
curl -X PUT https://api.thehumind.com/public/v1/knowledge/api:kb-sizing-guide-shoes/translations/fr \
-H "Authorization: Bearer hmd_live_..." \
-H "Idempotency-Key: 6a7b8c9d-0e1f-4a2b-3c4d-5e6f7a8b9c0d" \
-H "Content-Type: application/json" \
-d '{
"title": "Comment choisir la bonne pointure",
"content_html": "<p>Si vous hésitez entre deux tailles, on recommande de prendre la plus grande.</p>"
}'Response 200 OK
Renvoie l'entrée knowledge complète, même forme que la réponse de création, pour que vous voyiez toutes les locales actuellement stockées, pas juste celle que vous avez écrite.
Remove a translation
DELETE /knowledge/{id}/translations/{lang} enlève la traduction pour une seule locale. Idempotent : supprimer une locale qui n'existe pas est un no-op, pas une erreur.
Scope requis : knowledge:write
Request
curl -X DELETE https://api.thehumind.com/public/v1/knowledge/api:kb-shipping-faq/translations/fr \
-H "Authorization: Bearer hmd_live_..." \
-H "Idempotency-Key: 7b8c9d0e-1f2a-4b3c-4d5e-6f7a8b9c0d1e"Response 204 No Content
Pas de body.
Le type file
type='file' permet de pousser un PDF, document Word, Markdown, texte brut ou fichier HTML comme entrée knowledge. Comme le payload ne tient pas dans un body JSON, le workflow est divisé en trois étapes :
1. POST /knowledge ──► status='draft', file_url=null
{ type: 'file', original_filename, mime_type }
2. POST /knowledge/{id}/file-upload-url ──► { upload_url, expires_at }
3. PUT $UPLOAD_URL --data-binary @file.pdf ──► Azure Blob (no auth, just the SAS)
4. POST /knowledge/{id}/file-upload-completed ──► status='published',
file_size + uploaded_at populésLe premier appel enregistre la metadata seulement, aucun fichier n'est stocké encore. L'URL SAS générée à l'étape 2 est write-only et expire après 1 heure. L'étape 3 est un PUT direct vers Azure Blob storage ; vous ne touchez pas l'API Humind. L'étape 4 vérifie que le blob est bien arrivé et passe status à published pour que l'agent IA puisse l'utiliser.
Étape 1, créer l'entrée
curl -X POST https://api.thehumind.com/public/v1/knowledge \
-H "Authorization: Bearer hmd_live_..." \
-H "Idempotency-Key: 9c8b7a6d-5e4f-4a3b-2c1d-0e9f8a7b6c5d" \
-H "Content-Type: application/json" \
-d '{
"external_id": "kb-sizing-chart-pdf",
"type": "file",
"title": "Charte de tailles détaillée (PDF)",
"original_filename": "sizing-chart.pdf",
"mime_type": "application/pdf",
"tags": ["sizing"],
"default_language": "fr"
}'La réponse revient avec status: "draft", file_url: null, file_size: null, uploaded_at: null. L'entrée n'est pas utilisée par l'assistant tant que l'étape 4 n'est pas complétée.
Étape 2, obtenir une URL d'upload
curl -X POST https://api.thehumind.com/public/v1/knowledge/api:kb-sizing-chart-pdf/file-upload-url \
-H "Authorization: Bearer hmd_live_..." \
-H "Idempotency-Key: 0d9e8f7a-6b5c-4d3e-2f1a-9b8c7d6e5f4a"Response 200 OK
{
"upload_url": "https://<storage>.blob.core.windows.net/<container>/<opaque-path>/sizing-chart.pdf?sig=...&se=...",
"expires_at": "2026-04-25T15:30:00Z",
"blob_path": "<opaque-path>/sizing-chart.pdf"
}Appeler cet endpoint sur une entrée knowledge qui n'est pas type='file' renvoie 422 wrong_type.
Étape 3, PUT le fichier
Uploadez les bytes directement vers upload_url. Pas de header Authorization : le SAS dans la query string est l'auth.
curl -X PUT "$UPLOAD_URL" \
-H "x-ms-blob-type: BlockBlob" \
-H "Content-Type: application/pdf" \
--data-binary @sizing-chart.pdfAzure renvoie 201 Created en cas de succès. L'URL SAS n'a que les permissions Create + Write : elle ne peut pas read ou list autre chose.
Étape 4, finaliser
curl -X POST https://api.thehumind.com/public/v1/knowledge/api:kb-sizing-chart-pdf/file-upload-completed \
-H "Authorization: Bearer hmd_live_..." \
-H "Idempotency-Key: 1e0f9a8b-7c6d-4e5f-2a1b-0c9d8e7f6a5b"Response 200 OK
L'entrée mise à jour complète, avec status: "published", file_size populé et un uploaded_at frais. À partir de ce point, l'agent IA peut grounder ses réponses sur le document.
Si vous appelez cet endpoint avant que l'étape 3 n'ait fini (ou si l'upload a échoué silencieusement), vous obtenez :
{
"error": {
"code": "file_not_uploaded",
"message": "No file has been uploaded to the SAS URL yet. PUT your file before calling this endpoint."
}
}Réessayez simplement une fois le PUT complété. L'endpoint est idempotent, l'appeler à nouveau après un upload réussi re-publie l'entrée avec un uploaded_at frais.
MIME types autorisés
mime_type | Extension |
|---|---|
application/pdf | .pdf |
application/vnd.openxmlformats-officedocument.wordprocessingml.document | .docx |
application/msword | .doc (legacy) |
text/plain | .txt |
text/markdown | .md |
text/html | .html |
application/rtf | .rtf |
Tout autre type renvoie validation_failed à l'étape 1.
Limites
| Limite | Valeur |
|---|---|
Longueur de original_filename | 255 caractères |
| Taille de fichier | Pas de hard cap. On recommande de garder les fichiers individuels sous 50 MB pour que l'agent IA puisse les fetch dans le budget de la requête. |
| TTL de l'URL d'upload | 1 heure. Mintez une nouvelle URL si elle expire. |
OCR / extraction de texte
Les PDFs born-digital et les formats text-based marchent le mieux. Pour de l'OCR ou des PDFs scannés, contactez le support.
Batch upsert
POST /knowledge/batch accepte jusqu'à 500 entrées dans un seul appel et renvoie une réponse 207 Multi-Status avec une entrée par item. À utiliser pour le sync initial de votre base de connaissances ou n'importe quelle mise à jour bulk.
Scope requis : knowledge:write
| Contrainte | Valeur |
|---|---|
| Max items par batch | 500 |
| Max taille body | 5 MB |
| Comportement | Chaque item est traité indépendamment ; un échec ne bloque pas les autres. |
Request
curl -X POST https://api.thehumind.com/public/v1/knowledge/batch \
-H "Authorization: Bearer hmd_live_..." \
-H "Idempotency-Key: 4d3c2b1a-9f8e-4d7c-6b5a-4f3e2d1c0b9a" \
-H "Content-Type: application/json" \
-d '{
"items": [
{
"external_id": "kb-shipping-faq",
"type": "snippet",
"title": "Shipping delays",
"content": "Standard delivery takes 3-5 business days.",
"tags": ["shipping", "faq"],
"default_language": "en"
},
{
"external_id": "kb-returns-page",
"type": "webpage",
"title": "Returns policy",
"url": "https://merchant.com/returns",
"default_language": "en"
},
{
"external_id": "kb-bad",
"type": "snippet",
"title": "Missing content"
}
]
}'Response 207 Multi-Status
{
"results": [
{
"external_id": "kb-shipping-faq",
"status": "created",
"humind_id": "65f1ab9c8e7d4a2b1c3d4e5f"
},
{
"external_id": "kb-returns-page",
"status": "created",
"humind_id": "65f1cd9e8e7d4a2b1c3d4e60",
"queued_for_fetch": true
},
{
"external_id": "kb-bad",
"status": "failed",
"error": {
"code": "validation_failed",
"message": "Invalid knowledge payload.",
"details": {
"issues": [
{ "path": ["content"], "message": "Required", "code": "invalid_type" }
]
}
}
}
]
}| Champ | Type | Description |
|---|---|---|
results[i].external_id | string | L'external_id de l'item d'entrée, écho pour matching. |
results[i].status | string | Une valeur parmi created, updated, failed. |
results[i].humind_id | string | Présent en cas de succès, l'id Humind 24-hex assigné. |
results[i].queued_for_fetch | boolean | Optionnel, uniquement sur les items webpage quand le fetch d'URL a été mis en file. |
results[i].error | object | Présent en cas d'échec. Même forme qu'un objet d'erreur top-level. |
Doublons d'external_id au sein d'un batch
Si le même external_id apparaît plusieurs fois dans items, la première occurrence est traitée et les doublons suivants reviennent avec status: "failed" et le code duplicate_external_id_in_batch. Dédupliquez côté client avant l'envoi.
Wrapper ou tableau bare
Le body peut être un tableau JSON comme montré ci-dessus, ou wrappé : { "items": [ ... ] }. L'API normalise les deux vers la même réponse results.
Idempotency sur les batches
L'Idempotency-Key s'applique au batch entier. Un retry du même batch avec la même clé rejoue toute la réponse 207 : aucun item n'est traité deux fois. Générez un nouveau UUID pour un autre batch.
Erreurs courantes
Les endpoints knowledge peuvent renvoyer n'importe lequel des statuts HTTP standards, mais voici ceux que vous verrez le plus souvent :
| Statut | Code | Quand | Fix |
|---|---|---|---|
401 | missing_credentials, invalid_key_format, invalid_key, revoked | Le header d'auth manque, est malformé, ou la clé n'est plus active. | Voir Authentication. |
403 | insufficient_scope | La clé n'a pas knowledge:read (pour GET) ou knowledge:write (pour POST/PUT/PATCH/DELETE). | Créez une nouvelle clé avec le bon scope. |
404 | not_found | Le {id} ne correspond pas à une entrée qui appartient à votre company. | Vérifiez l'external_id ou le humind_id. Les lookups cross-tenant renvoient aussi 404. |
409 | idempotency_conflict | Même Idempotency-Key réutilisée avec un body différent. | Générez un UUID frais. |
400 | validation_failed | Le body a échoué le schéma Knowledge : le plus souvent un champ type-spécifique manquant (content pour snippet, content_html pour article, url pour webpage, original_filename/mime_type pour file). | Corrigez le champ et renvoyez. |
422 | wrong_type | /file-upload-url ou /file-upload-completed appelé sur une entrée knowledge qui n'est pas type='file'. | Utilisez une entrée file, ou enlevez l'appel. |
422 | file_not_uploaded | /file-upload-completed appelé avant d'avoir PUT les bytes vers upload_url. | Complétez l'étape 3 (le PUT) en premier, puis retentez l'étape 4. |
422 | tag_resolution_failed | Un nom de tag dans tags n'a pas pu être résolu ou créé. Rare. | Retentez ; si ça persiste, partagez le request_id avec le support. |
422 | invalid_lang_format | Le segment {lang} d'une URL de sub-resource translations ne match pas ^[a-z]{2}(-[A-Z]{2})?$. | Utilisez un tag court BCP 47 comme en, fr, pt-BR. |
200/202 (l'entrée affiche status='failed') | webpage_fetch_failed | Humind a tenté de fetcher l'URL d'une webpage et la page a renvoyé une erreur, a été bloquée par robots.txt, a timeout, ou n'a pas renvoyé du HTML. | Vérifiez que l'URL est joignable, puis PATCH l'entrée avec la même url (ou une corrigée) pour re-mettre en file. |
Pour aller plus loin
- Authentication : générer une clé
knowledge:write. - Conventions : identifiants, idempotency, dates.
- Products : pousser votre catalogue pour que l'assistant puisse recommander.
- Collections : grouper les produits pour des recommandations plus fines.