Conventions de l'API
Tous les endpoints de l'API Scribee partagent les mêmes conventions : une enveloppe de réponse unique, la pagination, le tri, les inclusions de ressources associées et des erreurs au format constant. Apprenez-les une fois sur cette page, elles s'appliquent à l'identique sur chaque ressource. Toutes les requêtes ci-dessous sont des lectures (GET) : vous pouvez les exécuter telles quelles sur votre compte de production, elles ne créent aucune donnée et n'envoient rien vers l'extérieur.
Chaque convention est illustrée sur la liste des factures, GET /api/v1/workspaces/{workspace_id}/invoices, avec le token obtenu dans Authentification et le workspace_id identifié dans Votre premier appel.
L'enveloppe de réponse
Toute réponse de liste contient un tableau data. Elle porte aussi un objet meta décrivant la pagination, sauf sur quatre sous-collections qui ne sont pas paginées et renvoient data seul : les justificatifs d'une facture (/api/v1/invoices/{id}/supporting_documents), les paiements d'une facture (/api/v1/invoices/{id}/payments), les justificatifs d'un tiers (/api/v1/parties/{id}/supporting_documents) et les invitations de mise à jour d'un tiers (/api/v1/workspaces/{workspace_id}/parties/{party_id}/update_invitations). Celles-ci renvoient la collection entière ; page et per_page y sont sans effet.
curl https://app.scribee.tech/api/v1/workspaces/YOUR_WORKSPACE_ID/invoices \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Réponse abrégée aux champs utiles ici - chaque facture est renvoyée dans sa sérialisation complète :
{
"data": [
{
"id": 12345,
"invoice_number": "INV-2024-00156",
"issue_date": "2024-12-15",
"direction": "sales",
"lifecycle_state": "deposited",
"lifecycle_status_code": "200",
"tax_inclusive_amount": 1770.0
}
],
"meta": {
"current_page": 1,
"per_page": 20,
"total_pages": 8,
"total_count": 156
}
}
Les endpoints de ressource unique (par exemple GET /api/v1/invoices/{id}) renvoient le même objet sous la clé data, sans meta.
Pagination
page (défaut : 1) et per_page (défaut : 20, maximum : 100) contrôlent la fenêtre renvoyée.
curl "https://app.scribee.tech/api/v1/workspaces/YOUR_WORKSPACE_ID/invoices?page=2&per_page=50" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
{
"data": ["..."],
"meta": {
"current_page": 2,
"per_page": 50,
"total_pages": 4,
"total_count": 156
}
}
Trois comportements à connaître :
- Une valeur
per_pagenumérique hors de l'intervalle 1-100 est ramenée à la borne la plus proche :per_page=500renvoie 100 éléments, sans erreur. Une valeurper_pageillisible - vide, non numérique, ou envoyée sous forme de tableau (per_page[]=10) - retombe sur la valeur par défaut, 20. - Envoyez toujours un
pageentier supérieur ou égal à 1.page=0, une valeur négative, vide, non numérique, ou envoyée sous forme de tableau (page[]=1) renvoie400dans l'enveloppe d'erreur habituelle :
{
"error": "bad_request",
"message": "Le numéro de page doit être un entier supérieur ou égal à 1"
}
- Demander une page au-delà de la dernière renvoie également
400, avec un message distinct, dès que la collection n'est pas vide :
{
"error": "bad_request",
"message": "Le numéro de page dépasse le nombre de pages disponibles"
}
Pour parcourir toute une collection, avancez page jusqu'à meta.total_pages inclus.
Tri
sort_by choisit le champ, sort_order la direction (asc ou desc, défaut : desc).
curl "https://app.scribee.tech/api/v1/workspaces/YOUR_WORKSPACE_ID/invoices?sort_by=due_date&sort_order=asc" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
{
"data": [
{ "id": 12290, "invoice_number": "INV-2024-00098", "due_date": "2025-01-05" },
{ "id": 12345, "invoice_number": "INV-2024-00156", "due_date": "2025-01-15" }
],
"meta": { "current_page": 1, "per_page": 20, "total_pages": 8, "total_count": 156 }
}
Champs acceptés sur la liste des factures : issue_date (défaut), due_date, invoice_number, tax_inclusive_amount, lifecycle_state, created_at. Une valeur inconnue de sort_by ou de sort_order ne provoque pas d'erreur : le tri retombe sur le défaut, issue_date décroissant.
Inclusions de ressources associées
Les listes renvoient chaque facture dans la même sérialisation complète que l'endpoint de ressource unique - parties, totaux, références et informations de cycle de vie compris. Seules les collections associées ne sont chargées qu'à la demande, via le paramètre include (valeurs séparées par des virgules).
curl "https://app.scribee.tech/api/v1/workspaces/YOUR_WORKSPACE_ID/invoices?include=lines" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
{
"data": [
{
"id": 12345,
"invoice_number": "INV-2024-00156",
"lines": [
{
"id": 501,
"line_id": "1",
"quantity": 2.0,
"line_extension_amount": 1475.0,
"item": { "name": "Prestation de conseil" }
}
]
}
],
"meta": { "current_page": 1, "per_page": 20, "total_pages": 8, "total_count": 156 }
}
Valeurs acceptées sur les factures : lines, payment_means, tax_subtotals, lifecycle_events, allowance_charges, item_notes, invoice_references, payments, early_payment_discounts. Une valeur inconnue est ignorée sans erreur. Chaque endpoint liste ses valeurs admises sur sa page de référence, par exemple Lister les factures.
early_payment_discounts dépend d'une option activée espace de travail par espace de travail. Quand elle ne l'est pas, l'inclusion reste acceptée mais renvoie toujours un tableau vide, et le champ early_payment_discounts_extracted_at de la facture vaut null : un tableau vide ne signifie donc pas à lui seul qu'aucun escompte n'existe. Demandez à votre contact Scribee si l'option est active sur votre workspace.
Les erreurs
Chaque erreur porte un statut HTTP et un corps JSON avec une clé error stable, destinée à vos traitements, et un message lisible, destiné à vos journaux. Basez votre logique sur le statut HTTP et sur error, jamais sur le texte de message. Les messages sont renvoyés en français.
| Statut | error | Cause | Ce que vous faites |
|---|---|---|---|
400 | bad_request | corps de requête absent ou incomplet, valeur de page invalide, page au-delà de la dernière | corrigez la requête avant de la rejouer |
401 | corps vide | token absent, invalide ou expiré | redemandez un token sur /oauth/token et rejouez la requête |
403 | forbidden | scope insuffisant, droits insuffisants sur la ressource, workspace non autorisé pour votre application ou inexistant, ou adresse IP hors de la liste autorisée | vérifiez les scopes du token ; pour un accès workspace ou IP, adressez-vous à votre contact Scribee |
404 | not_found | la ressource n'existe pas, ou appartient à un workspace auquel votre application n'a pas accès | vérifiez l'identifiant et le workspace interrogé |
422 | unprocessable_entity | la charge utile d'une écriture a échoué à la validation | corrigez les champs listés dans details |
400 : trois causes, un format
{
"error": "bad_request",
"message": "Le corps de la requête est manquant ou mal formé"
}
Trois causes partagent ce statut et la clé error, et le message les distingue. Le message ci-dessus signale un corps de requête absent, ou qui ne porte pas la clé englobante attendue par l'endpoint - {"customer": {...}} sur la création d'un client, par exemple. Les deux autres causes portent sur la pagination et sont détaillées plus haut : une valeur de page invalide, et une page demandée au-delà de la dernière. Dans les trois cas, corrigez la requête avant de la rejouer.
401 : pas de corps
La réponse 401 n'a pas de corps JSON ; le détail est porté par l'en-tête WWW-Authenticate. Redemandez un token comme décrit dans Authentification.
403 : cinq causes, un format
{
"error": "forbidden",
"message": "L'application n'a pas accès à cet espace de travail"
}
Deux message couvrent trois de ces causes. Le premier (ci-dessus) répond à la fois pour un workspace hors du périmètre de votre application et pour un workspace_id qui ne correspond à aucun workspace : la réponse est la même mot pour mot dans les deux cas, si bien qu'elle ne vous dit jamais si le workspace existe. Le second signale une adresse IP refusée par la liste du workspace (Cette adresse IP n'est pas autorisée pour cet espace de travail). Les deux causes restantes - scope insuffisant et droits insuffisants sur la ressource visée - partagent le même message générique, Vous n'êtes pas autorisé à effectuer cette action, et ne se distinguent donc pas l'une de l'autre par le texte. Aucun 403 n'expose de nom de classe interne ni de texte technique : les messages restent en français, comme partout ailleurs sur l'API. Basez votre logique sur le statut 403 et sur la clé error. Les scopes se corrigent en redemandant un token ; les accès workspace et IP sont configurés par Scribee, sur demande à votre contact.
Un refus de workspace ne se présente pas partout de la même façon, et c'est la forme de l'endpoint qui décide, jamais la cause du refus. Sur un endpoint qui porte le workspace dans son chemin, tout refus de workspace est un 403 : que le workspace soit hors du périmètre de votre application, qu'il n'existe pas, ou que sa liste d'IP ne couvre pas votre adresse source. Sur un endpoint qui désigne directement une ressource par son identifiant, il n'y a pas de workspace_id à refuser : les workspaces hors de votre périmètre, comme ceux dont la liste d'IP vous exclut, sont écartés avant la recherche, et vous recevez un 404. Un 404 inattendu depuis un nouveau serveur est donc d'abord une question d'adresse IP.
404 : l'existence n'est pas révélée
{
"error": "not_found",
"message": "La ressource demandée est introuvable"
}
Une ressource située dans un workspace auquel votre application n'a pas accès renvoie 404, pas 403 : l'API ne révèle pas l'existence de ressources hors de votre périmètre. Un 404 inattendu sur un identifiant connu signale une faute de frappe, un accès workspace manquant, une adresse IP hors de la liste autorisée du workspace, ou une ressource que votre offre ne couvre pas - une facture de vente reste invisible tant que l'entreprise n'a pas la capacité correspondante.
422 : le code machine et le détail par champ
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"direction": ["doit être 'sales' ou 'purchases'"]
}
}
code est un identifiant machine stable, en snake_case, jamais traduit et jamais reformulé. Il nomme la classe d'échec, là où message est un texte français destiné à vos journaux et details une carte des champs fautifs : c'est donc sur code que vous branchez votre logique quand le statut 422 seul ne suffit pas. error, message et details gardent le sens et le format qu'ils avaient.
code | Ce qu'il signale |
|---|---|
validation_failed | une validation a refusé l'écriture ; details nomme les champs |
operation_failed | l'opération a été refusée sans cause plus précise |
duplicate_record | un enregistrement portant cet identifiant existe déjà |
value_too_long | une valeur dépasse la longueur maximale de son champ |
invalid_argument | une valeur soumise n'appartient pas aux valeurs admises |
dependent_records | des enregistrements liés à la ressource bloquent l'opération |
Le catalogue peut s'enrichir de nouvelles valeurs sans préavis ; traitez un code que vous ne connaissez pas comme un échec générique plutôt que de rejeter la réponse.
details associe chaque champ fautif à la liste de ses erreurs quand l'échec est imputable à un champ. Quand il ne l'est pas - une règle qui porte sur la requête entière, un conflit avec une ressource liée - les messages sont regroupés sous la clé base. Certains échecs renvoient un message spécifique sans details : traitez details comme optionnel, et n'attendez pas une clé par champ soumis.
422 : une suppression bloquée par des enregistrements liés
Supprimer une entreprise, un établissement, un client ou un fournisseur encore référencé ailleurs est refusé par un 422 portant le code dependent_records. Quand l'API sait identifier la dépendance bloquante, elle la nomme dans le message et n'envoie pas de details :
{
"error": "unprocessable_entity",
"code": "dependent_records",
"message": "Impossible de supprimer une entreprise qui a des filiales"
}
Quand elle ne le sait pas, le refus vient de la base de données et prend la forme générique, avec le détail sous details.base :
{
"error": "unprocessable_entity",
"code": "dependent_records",
"message": "La validation a échoué",
"details": {
"base": ["Cette opération est en conflit avec d'autres enregistrements liés à cette ressource"]
}
}
Le code dependent_records est identique dans les deux cas : branchez-vous dessus plutôt que sur le texte. Détachez ou supprimez les enregistrements liés, puis rejouez la suppression. Certaines dépendances ne se traitent que depuis l'interface Scribee ; la page de chaque ressource précise lesquelles.