Catégories
Vos clients, vos fournisseurs et vos factures portent déjà une classification dans votre produit : lignes d'activité, portefeuilles, types de dépense. Les catégories transportent cette taxonomie dans Scribee : vous créez vos libellés au niveau du workspace ou d'une entreprise, puis vous les rattachez à vos fiches et à vos factures par category_id. Une catégorie reste une donnée interne : elle n'est émise dans aucun format réglementaire généré (Factur-X, UBL, CII) et elle est distincte de tax_category_id, le code de catégorie de TVA EN16931 porté par les lignes et sous-totaux de facture (Émettre une facture). Chaque endpoint de cette page ne touche que les données de votre workspace et n'envoie rien vers l'extérieur - ni PPF (Portail Public de Facturation), ni Peppol.
Ce que Scribee fait pour vous
- Garde votre taxonomie hors des documents réglementaires : les fichiers Factur-X, UBL et CII générés par Scribee ne portent jamais votre
category_id. - Refuse une assignation hors périmètre : une catégorie d'un autre workspace ou d'une autre entreprise est rejetée avec un
422au moment de l'écriture. - Applique l'unicité du nom entre catégories actives d'un même périmètre, sans tenir compte de la casse ; archiver une catégorie libère son nom.
- Préserve vos rattachements à l'archivage : les fiches et factures existantes gardent leur
category_id. - Protège vos abonnements webhook et votre classement à l'import : une catégorie ciblée par un endpoint webhook, ou visée par une déclaration de classement à l'import, n'est pas archivable tant que ce rattachement existe.
Deux périmètres : workspace ou entreprise
Une catégorie porte un company_id optionnel. Sans company_id, c'est un modèle de workspace, assignable aux fiches et factures de chaque entreprise du workspace (Entreprises et établissements). Avec un company_id, elle n'est assignable qu'aux fiches et factures de cette entreprise. Dans les réponses, tenant_id reprend le workspace_id du chemin.
Les identifiants de catégorie forment un espace de noms unique sur tous les workspaces liés à votre application : GET, PATCH et DELETE /api/v1/categories/{id} n'ont pas de workspace_id dans le chemin et atteignent la catégorie quel que soit le workspace qui la porte, dès lors que ce workspace est lié à votre application. Seuls la liste et la création sont adressés par workspace.
Sur la liste, scope=tenant filtre les modèles de workspace, scope=company les catégories d'entreprise, et company_id cible une entreprise donnée.
Étape 1 : créer une catégorie
Cet appel crée une catégorie dans votre workspace de production - il n'y a pas de sandbox. Il n'envoie rien vers l'extérieur et tout est réversible : le nom et la couleur se modifient (étape 4), l'archivage la retire des listes (étape 5). Vous pouvez donc l'exécuter tel quel en guise de répétition. Un token read write est requis.
curl -X POST https://app.scribee.tech/api/v1/workspaces/YOUR_WORKSPACE_ID/categories \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"category": {
"name": "Grands comptes",
"color": "#3B82F6"
}
}'
Réponse 201 :
{
"data": {
"id": 12,
"name": "Grands comptes",
"color": "#3B82F6",
"tenant_id": 3,
"company_id": null,
"archived_at": null,
"created_at": "2026-07-31T11:15:00+02:00",
"updated_at": "2026-07-31T11:15:00+02:00"
}
}
Les horodatages sont sérialisés en ISO 8601 avec le décalage du fuseau Europe/Paris (+02:00 en heure d'été, +01:00 en heure d'hiver). Aucun champ n'est renvoyé en UTC avec un suffixe Z.
name est obligatoire, 255 caractères au maximum. color est optionnelle, au format # suivi de 6 caractères hexadécimaux. Ajoutez company_id dans l'objet category pour créer une catégorie d'entreprise ; omettez-le pour un modèle de workspace.
Étape 2 : lister et filtrer
GET .../categories retourne les catégories actives du workspace, triées par name croissant. Le scope read suffit.
curl "https://app.scribee.tech/api/v1/workspaces/YOUR_WORKSPACE_ID/categories?scope=tenant" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
{
"data": [
{
"id": 12,
"name": "Grands comptes",
"color": "#3B82F6",
"tenant_id": 3,
"company_id": null,
"archived_at": null,
"created_at": "2026-07-31T11:15:00+02:00",
"updated_at": "2026-07-31T11:15:00+02:00"
}
],
"meta": {
"current_page": 1,
"per_page": 20,
"total_pages": 1,
"total_count": 1
}
}
sort_by accepte name, created_at, updated_at et archived_at ; sort_order accepte asc et desc, et vaut asc par défaut. Une valeur non reconnue sur l'un ou l'autre est ignorée sans erreur et le tri par défaut s'applique.
scope, company_id et include_archived sont sensibles à la casse, mais ne se comportent pas de la même façon. scope n'accepte que tenant et company ; toute autre valeur est ignorée, ne filtre rien et renvoie la liste complète. include_archived n'ajoute les catégories archivées que sur la chaîne exacte true. company_id, lui, n'est jamais ignoré : toute valeur non vide est appliquée telle quelle comme filtre. Un identifiant d'entreprise hors de ce workspace, ou qui contredit scope, renvoie donc une liste vide en 200, pas la liste complète et pas une erreur.
La pagination suit les conventions communes (Conventions de l'API) : per_page vaut 20 par défaut et est ramené silencieusement dans l'intervalle 1-100, donc per_page=500 renvoie 100 éléments sans avertissement. Une catégorie seule se lit sur GET /api/v1/categories/{id} - chemin direct, sans workspace_id (Votre premier appel) ; une catégorie archivée y répond aussi.
Étape 3 : assigner une catégorie
L'assignation passe par les endpoints de la ressource porteuse, pas par ceux des catégories : category_id est accepté à la création et à la mise à jour des clients et des fournisseurs (Clients et fournisseurs) ainsi que des factures (Émettre une facture). Cet appel modifie une fiche de votre annuaire et n'envoie rien vers l'extérieur ; pour revenir en arrière, réassignez un autre category_id.
curl -X PATCH https://app.scribee.tech/api/v1/customers/42 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"customer": {
"category_id": 12
}
}'
Réponse 200, abrégée aux champs utiles ici :
{
"data": {
"id": 42,
"type": "customer",
"company_id": 7,
"category_id": 12,
"name": "Acme Corp"
}
}
En lecture, chaque fiche client ou fournisseur et chaque facture renvoie son category_id. Les listes de fiches et de factures n'exposent pas de filtre par catégorie : le rattachement se lit ressource par ressource.
Sur une facture importée depuis un ERP connecté, category_id peut être renseigné sans que vous l'ayez envoyé. Ce classement ne vaut pas pour tous les ERP connectés : il repose sur un code de provenance que la facture doit porter à l'import, et un seul connecteur le transmet aujourd'hui - demandez à Scribee si celui de votre client est concerné avant d'en dépendre. Là où il s'applique, votre client déclare depuis l'interface Scribee quelle catégorie appliquer selon la provenance de la facture dans son ERP, et la déclaration s'applique aux factures importées après elle. Les factures déjà importées ne sont pas reclassées : enregistrer la déclaration ne les reprend pas, et la synchronisation courante non plus - il faut un rejeu explicite, à demander à Scribee. Ce classement ne passe jamais par-dessus une catégorie déjà rattachée - un category_id que vous envoyez, comme une catégorie choisie dans l'interface, l'emporte toujours - et il ne concerne jamais une facture créée par l'API partenaire. L'absence de déclaration n'est pas une erreur : la facture arrive simplement sans catégorie, sans message et sans erreur d'import.
Une facture d'achat restée sans catégorie reprend celle de son fournisseur : le category_id de la fiche fournisseur à laquelle son vendeur est rattaché. Ce rattachement se fait à la création quand vous envoyez le party_id du fournisseur sur la partie seller, et, pour une facture reçue (Peppol, dépôt de fichier, e-mail), quand Scribee reconnaît le vendeur dans l'annuaire de l'entreprise. Une facture d'achat créée par l'API sans party_id n'est pas rapprochée de l'annuaire et ne reçoit donc pas cette catégorie. La reprise ne fait que combler un vide : un category_id que vous envoyez, une catégorie choisie dans l'interface ou posée par le classement à l'import l'emporte toujours, puis vient la catégorie que votre client associe, depuis l'interface Scribee, à l'adresse Peppol sur laquelle la facture est reçue ; celle du fournisseur passe en dernier. Elle n'est reprise que si elle est active et assignable à l'entreprise de la facture ; sinon la facture reste sans catégorie. Les factures de vente ne sont pas concernées.
Étape 4 : renommer ou changer la couleur
La mise à jour porte sur name et color ; le périmètre (company_id) est fixé à la création - un company_id envoyé ici est ignoré sans erreur. Un token read write est requis.
curl -X PATCH https://app.scribee.tech/api/v1/categories/12 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"category": {
"name": "Comptes stratégiques"
}
}'
Réponse 200 avec la catégorie mise à jour :
{
"data": {
"id": 12,
"name": "Comptes stratégiques",
"color": "#3B82F6",
"tenant_id": 3,
"company_id": null,
"archived_at": null,
"created_at": "2026-07-31T11:15:00+02:00",
"updated_at": "2026-07-31T14:02:00+02:00"
}
}
Étape 5 : archiver
DELETE est un archivage, pas une suppression : il pose l'horodatage archived_at et la catégorie sort des listes par défaut, mais l'enregistrement est conservé. Les fiches et factures déjà rattachées gardent leur category_id, et la catégorie reste lisible par son id ou avec include_archived=true. Un token read write est requis.
curl -X DELETE https://app.scribee.tech/api/v1/categories/12 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Réponse 204, sans corps. L'appel est idempotent : archiver une catégorie déjà archivée répond aussi 204. L'archivage ne bloque pas l'écriture : un category_id archivé reste accepté sur les fiches et les factures. La réactivation d'une catégorie archivée se fait depuis l'interface Scribee.
Première exception : un endpoint webhook ciblé sur la catégorie bloque l'archivage. L'appel répond alors 422 avec le code dependent_records (voir plus bas) et la catégorie reste active. Détachez ces endpoints - category_id à null - ou supprimez-les, puis rejouez le DELETE (Webhooks). Le blocage compte tous les endpoints du workspace, mais vos écritures ne portent que sur ceux de votre application : un endpoint enregistré par une autre application ou géré par le workspace se détache depuis l'interface Scribee, ou par l'application qui le possède.
Seconde exception : une catégorie visée par une déclaration de classement à l'import (étape 3) bloque l'archivage de la même façon, avec le même 422 et le même code dependent_records, seul le message diffère (voir plus bas). Ce classement n'est pas exposé par l'API : retirez la catégorie de ce paramétrage, ou faites-le pointer vers une autre catégorie, depuis l'interface Scribee, puis rejouez le DELETE.
Ce qui se passe ensuite
- Rien ne part vers le PPF ni vers le réseau Peppol : créer, assigner ou archiver une catégorie ne touche aucun document réglementaire, ni aucune facture déjà émise.
- Le nom d'une catégorie archivée redevient disponible : l'unicité ne compare que les catégories actives d'un même périmètre. Vous pouvez donc recréer une catégorie active du même nom.
- Vos lectures renvoient
category_idpartout où il s'applique - fiches clients et fournisseurs, factures - sans paramètre supplémentaire. - Une catégorie peut aussi cibler un endpoint webhook, qui ne reçoit alors que les événements des factures qu'elle porte (Webhooks). C'est, avec le classement à l'import, l'un des deux rattachements qui retiennent l'archivage.
Erreurs et cas limites
Les endpoints de catégories regroupent toutes les erreurs de validation sous details.base. Un refus d'archivage fait exception : il porte un code et pas de details, parce qu'il vise la catégorie entière et non un champ.
400 : objet category vide
Un corps sans objet category, ou avec un objet category vide ({"category": {}}), n'atteint jamais la validation du modèle : la lecture des paramètres échoue avant. La réponse est un 400 dans l'enveloppe d'erreur habituelle, avec error à bad_request et le message "Le corps de la requête est manquant ou mal formé" :
{
"error": "bad_request",
"message": "Le corps de la requête est manquant ou mal formé"
}
Envoyez au moins name.
400 : page hors limites
Sur GET .../categories, un page supérieur au nombre de pages disponibles :
{
"error": "bad_request",
"message": "Le numéro de page dépasse le nombre de pages disponibles"
}
Le champ meta.total_pages de la réponse précédente donne la borne.
422 : nom manquant
name présent mais vide à la création :
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": ["Nom doit être rempli(e)"]
}
}
Fournissez un name et rejouez l'appel.
422 : nom déjà pris
Une catégorie active du même périmètre porte déjà ce nom - la comparaison ignore la casse :
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": ["Nom a déjà été pris"]
}
}
Réutilisez la catégorie existante (étape 2) ou choisissez un autre nom. Un modèle de workspace et une catégorie d'entreprise peuvent porter le même nom : le périmètre fait partie de la règle d'unicité.
422 : couleur invalide
color hors du format # + 6 caractères hexadécimaux :
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": ["Couleur n'est pas valide"]
}
}
422 : category_id refusé à l'assignation
Sur les endpoints de clients et de fournisseurs, tout category_id que la fiche ne peut pas légitimement porter est refusé sous la clé category :
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"category": ["doit exister"]
}
}
category porte le message "doit exister" dans les trois cas : une catégorie qui n'existe pas, une catégorie d'un autre workspace, et une catégorie d'une autre entreprise de votre workspace. Les trois sont volontairement indiscernables : une réponse distincte permettrait de sonder l'existence de catégories hors de votre périmètre, y compris celles qu'un membre rattaché à une seule entreprise ne voit déjà pas dans son sélecteur de catégories. C'est la normalisation déjà appliquée à accounting_ledger_id sur ces mêmes endpoints (Clients et fournisseurs).
La même règle de périmètre s'applique aux factures. Vérifiez le company_id de la catégorie contre celui de la fiche ou de la facture cible.
422 : endpoints webhook rattachés
DELETE /api/v1/categories/{id} sur une catégorie qu'un endpoint webhook cible :
{
"error": "unprocessable_entity",
"code": "dependent_records",
"message": "Des endpoints webhook sont encore rattachés à \"Grands comptes\". Détachez-les ou supprimez-les avant de l'archiver."
}
Le message nomme la catégorie et ce corps ne porte pas de details : branchez sur code. Listez les endpoints webhook du workspace, remettez leur category_id à null ou supprimez-les, puis rejouez le DELETE (Webhooks). La liste couvre le workspace entier alors que PATCH et DELETE ne portent que sur les endpoints de votre application : ceux d'une autre application ou gérés par le workspace répondent 403 et se détachent depuis l'interface Scribee. Les fiches et factures rattachées, elles, ne bloquent jamais l'archivage.
422 : classement à l'import rattaché
DELETE /api/v1/categories/{id} sur une catégorie visée par une déclaration de classement à l'import :
{
"error": "unprocessable_entity",
"code": "dependent_records",
"message": "Des correspondances d'import sont encore rattachées à \"Grands comptes\". Supprimez-les ou faites-les pointer vers une autre catégorie avant de l'archiver."
}
Même forme que le refus précédent - le message nomme la catégorie, le corps ne porte pas de details, branchez sur code. Ce paramétrage ne se lit ni ne se modifie par l'API : il se corrige depuis l'interface Scribee, dans les réglages d'intégration de l'entreprise concernée.
404 Not Found
Sur GET, PATCH et DELETE /api/v1/categories/{id}, trois situations donnent la même réponse : la catégorie n'existe pas, elle appartient à un workspace non lié à votre application, ou elle appartient à un workspace lié dont la liste d'IP autorisées exclut l'adresse d'où part l'appel. Ces chemins ne portent pas de workspace_id, donc un refus de périmètre y est indiscernable d'un identifiant inconnu. À la création, un company_id qui ne désigne pas une entreprise du workspace produit la même réponse :
{
"error": "not_found",
"message": "La ressource demandée est introuvable"
}
Vérifiez l'identifiant, le rattachement du workspace et l'adresse IP d'appel (Votre premier appel).
403 Forbidden
Trois causes, que seul le message distingue :
- Scope insuffisant. La lecture demande un token
read; la création, la mise à jour et l'archivage demandent un tokenread write. Il n'existe que ces deux jeux de scopes. Message :Vous n'êtes pas autorisé à effectuer cette action. - Le
workspace_iddu chemin désigne un workspace non lié à votre application. Message :L'application n'a pas accès à cet espace de travail. - Le workspace du chemin restreint les adresses IP et l'appel ne vient pas d'une adresse autorisée. Message :
Cette adresse IP n'est pas autorisée pour cet espace de travail.
Les deux dernières ne se produisent que sur les chemins qui portent un workspace_id, c'est-à-dire la liste et la création. Sur /api/v1/categories/{id}, le même refus se présente en 404.
{
"error": "forbidden",
"message": "Vous n'êtes pas autorisé à effectuer cette action"
}
Redemandez un token avec les scopes voulus (Authentification).
Pages liées
- Clients et fournisseurs - les fiches qui portent
category_id - Émettre une facture -
category_idà la création de facture, ettax_category_idavec lequel ne pas le confondre - Webhooks - cibler un endpoint sur une catégorie, et l'archivage que ce ciblage bloque
- Référence API : lister les catégories
- Référence API : créer une catégorie
- Référence API : archiver une catégorie