Aller au contenu principal

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_page numérique hors de l'intervalle 1-100 est ramenée à la borne la plus proche : per_page=500 renvoie 100 éléments, sans erreur. Une valeur per_page illisible - 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 page entier supérieur ou égal à 1. page=0, une valeur négative, vide, non numérique, ou envoyée sous forme de tableau (page[]=1) renvoie 400 dans 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.

StatuterrorCauseCe que vous faites
400bad_requestcorps de requête absent ou incomplet, valeur de page invalide, page au-delà de la dernièrecorrigez la requête avant de la rejouer
401corps videtoken absent, invalide ou expiréredemandez un token sur /oauth/token et rejouez la requête
403forbiddenscope insuffisant, droits insuffisants sur la ressource, workspace non autorisé pour votre application ou inexistant, ou adresse IP hors de la liste autoriséevérifiez les scopes du token ; pour un accès workspace ou IP, adressez-vous à votre contact Scribee
404not_foundla ressource n'existe pas, ou appartient à un workspace auquel votre application n'a pas accèsvérifiez l'identifiant et le workspace interrogé
422unprocessable_entityla charge utile d'une écriture a échoué à la validationcorrigez 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.

codeCe qu'il signale
validation_failedune validation a refusé l'écriture ; details nomme les champs
operation_failedl'opération a été refusée sans cause plus précise
duplicate_recordun enregistrement portant cet identifiant existe déjà
value_too_longune valeur dépasse la longueur maximale de son champ
invalid_argumentune valeur soumise n'appartient pas aux valeurs admises
dependent_recordsdes 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.

Pages liées​