Le cycle de vie d'une facture
La réforme de la facturation électronique ne se limite pas à transmettre des factures : elle normalise leur suivi. Chaque facture, de vente comme d'achat, avance sur un référentiel de statuts défini par l'AIFE (Agence pour l'informatique financière de l'État), du dépôt à l'encaissement. Cette page est la référence du cycle de vie pour tout le guide : les statuts observables, les transitions que votre système peut déclencher selon le sens de la facture, et les codes motifs exigés pour un litige ou un refus. Émettre une facture de vente et Recevoir les factures fournisseurs s'appuient sur elle sans la répéter.
Ce que Scribee fait pour vous
- Historisation : chaque changement de statut est enregistré comme événement de cycle de vie, avec son horodatage, son code et son motif, consultable via
include=lifecycle_eventspar ordre chronologique. - Encaissement automatique : l'enregistrement d'un paiement qui solde la facture déclenche l'encaissement (212) sans appel de votre part, et sa modification ou sa suppression le retire (voir Enregistrer les paiements).
- Transitions calculées : le champ
lifecycle_available_transitionsliste, sur chaque facture, les évènements que votre prochain appel peut déclencher depuis l'état courant. - TVA en euros au dépôt : sur une facture libellée dans une autre devise que l'
EUR, le dépôt (deposit) fixe lui-même la devise de comptabilisation de la TVA àEURet y convertit le total de TVA, au taux de référence de la date d'émission. Vous n'avez quecurrency_codeà envoyer ; sans taux publié pour cette date, le dépôt est refusé et la facture reste au brouillon (voir Émettre une facture de vente).make_available, la transition des factures d'achat, n'est pas concernée.
Vos appels de transition et les paiements que vous enregistrez sont les deux leviers dont vous disposez, mais ils ne sont pas les seules sources d'écriture. Scribee applique aussi les statuts qui lui parviennent par les flux réglementaires entrants d'une autre plateforme - 202 Reçue, 213 Rejetée et 220 Annulée en font partie - et les enregistre dans l'historique comme n'importe quelle autre transition. Une facture peut donc changer d'état sans que vous ayez appelé quoi que ce soit.
Sur une facture de vente, ce sont aussi les statuts de votre client qui arrivent ainsi : 203 Mise à disposition, 204 Prise en charge, 205 Approuvée, 206 Approuvée partiellement, 207 En litige, 208 Suspendue, 210 Refusée et 211 Paiement transmis. Les statuts 202 et 203 sont facultatifs et la plateforme de votre client peut ne pas les transmettre : Scribee applique donc chacun de ces statuts dès 200 Déposée ou 202 Reçue, sans attendre les étapes omises. Un statut antérieur qui arrive en retard - un 202 après un 204, par exemple - est inscrit dans l'historique avec applied à false et ne fait jamais reculer la facture. Des statuts reçus et précédemment inscrits sans effet peuvent aussi être réappliqués : chaque statut qui fait alors transitionner la facture est annoncé une seconde fois, avec applied à true (Webhooks).
Les statuts
Le champ lifecycle_state porte la valeur API, lifecycle_status_code le code AIFE correspondant.
| Code | lifecycle_state | Libellé | Signification |
|---|---|---|---|
000 | draft | Brouillon | Document en construction : modifiable par PATCH. Pour une facture créée directement dans Scribee, non supprimable dès lors qu'elle porte un numéro définitif, c'est-à-dire un numéro présent qui ne commence pas par DRAFT- ; une facture importée par fichier échappe à cette condition (voir Revenir en brouillon). |
200 | deposited | Déposée | La facture de vente est validée et entre dans le cycle de vie. Une facture auto-facturée reçue par le réseau Peppol, enregistrée en vente, n'y passe pas : elle entre directement en 203. |
203 | available | Mise à disposition | La facture d'achat, ou la facture auto-facturée reçue par le réseau Peppol, est entrée dans le cycle de vie et attend votre traitement. |
204 | taken_in_charge | Prise en charge | Vous avez commencé le traitement de la facture d'achat. |
205 | approved | Approuvée | Vous acceptez la facture d'achat. |
206 | approved_partially | Approuvée partiellement | Vous n'acceptez qu'une partie de la facture d'achat, motif normalisé à l'appui. |
207 | disputed | En litige | Vous contestez tout ou partie de la facture d'achat, motif normalisé à l'appui. |
208 | suspended | Suspendue | Vous suspendez le traitement de la facture d'achat dans l'attente d'un complément, motif normalisé et commentaire à l'appui ; le traitement reprend ensuite par l'une des autres décisions. |
210 | refused | Refusée | Vous refusez la facture d'achat, motif normalisé à l'appui ; le refus est définitif, sauf réouverture depuis l'interface d'un refus resté dans Scribee (voir Rouvrir une facture d'achat refusée). |
211 | payment_sent | Paiement transmis | Le paiement est émis, en attente d'encaissement. |
212 | collected | Encaissée | Le paiement est reçu ; la facture est soldée. |
Le référentiel AIFE compte d'autres codes (202 Reçue, 213 Rejetée, 220 Annulée notamment). Aucun appel de cette API ne les produit directement, mais le traitement des flux réglementaires entrants les applique et les inscrit dans l'historique. Un autre écart mérite d'être connu : modifier ou supprimer un paiement qui soldait la facture peut ramener celle-ci de 212 Encaissée à 211 Paiement transmis, sans appel de transition. Traitez lifecycle_state comme une valeur ouverte - prévoyez un cas par défaut pour un code inconnu plutôt que de supposer la liste close, et ne supposez pas l'état monotone.
Une autre plateforme peut aussi vous adresser les statuts 214 Visée, 224 Demande de Paiement Direct, 225 Affacturée, 227 Changement de Compte à Payer et 228 Non Affacturée. Ils sont purement informatifs : ils apparaissent dans l'historique avec applied à false, leur champ state vaut respectivement endorsed, direct_payment_requested, factored, payee_account_changed et unfactored, et ils ne changent jamais lifecycle_state. Le statut 226 n'est jamais enregistré. Aucun appel de cette API ne produit ces statuts.
Le graphe des transitions
Le diagramme montre le parcours type ; le tableau qui suit liste chaque transition individuellement.
Les statuts intermédiaires sont facultatifs : collect (212) accepte n'importe quel statut actif postérieur au dépôt comme point de départ. Le contrat, transition par transition :
| Évènement | De | Vers | Déclencheur | reason_code |
|---|---|---|---|---|
deposit | 000 | 200 | Vous, facture de vente | - |
make_available | 000 ou 202 | 203 | Vous, facture d'achat ; automatique pour une facture auto-facturée reçue par le réseau Peppol | - |
take_in_charge | 203 ou 208 | 204 | Vous, facture d'achat | - |
approve | 203, 204, 207 ou 208 | 205 | Vous, facture d'achat | - |
approve_partially | 203, 204, 207 ou 208 | 206 | Vous, facture d'achat | obligatoire |
dispute | 203, 204 ou 208 | 207 | Vous, facture d'achat | obligatoire |
suspend | 203, 204 ou 207 | 208 | Vous, facture d'achat | obligatoire, avec reason |
refuse | 203, 204, 207 ou 208 | 210 | Vous, facture d'achat | obligatoire, avec reason |
send_payment | 203, 204, 205 ou 206 | 211 | Vous, facture d'achat | - |
collect | 200, 202, 203, 204, 205, 206, 207, 208 ou 211 | 212 | Vous, facture de vente ; automatique quand un paiement solde la facture | - |
uncollect | 212 | 211 | Automatique, quand un paiement modifié ou supprimé ne solde plus la facture | - |
revert_to_draft | 200, ou 203 pour un achat | 000 | Vous, sous conditions (voir plus bas) | - |
Le paiement transmis (211) n'exige ni prise en charge ni approbation préalable. Sur un compte où le circuit d'approbation des factures d'achat est activé, en revanche, send_payment ne part que de 205 : la facture doit être approuvée avant que son paiement soit déclaré.
Sur une facture de vente, make_available, take_in_charge, approve, approve_partially, dispute, suspend, refuse et send_payment partent aussi de 200 et de 202 : ce sont les statuts de votre client, et ces transitions-là ne viennent que des flux entrants (voir Ce que Scribee fait pour vous).
Qui déclenche quoi
Les évènements que votre système peut déclencher dépendent du sens de la facture (direction) :
- Vente (
direction: "sales") :deposit,collect,revert_to_draft. Sur une facture auto-facturée reçue par le réseau Peppol, entrée en203, seulcollectest ouvert :depositpart de000, etrevert_to_draftdepuis203est réservé aux factures d'achat. - Achat (
direction: "purchases") :make_available,take_in_charge,approve,approve_partially,suspend,dispute,refuse,send_payment,revert_to_draft. L'encaissement (212) d'une facture d'achat s'obtient en enregistrant les paiements, pas par l'endpoint de transition. uncollectne se déclenche jamais par l'endpoint de transition : il est automatique, piloté par vos paiements. L'envoyer renvoie422avec le message d'évènement inconnu.receive,rejectetcancelexistent dans le moteur d'états mais aucun appel de l'API ne peut les déclencher, quel que soit le sens de la facture. Vous obtenez422:La transition receive ne peut pas être déclenchée manuellement.quand l'état courant permettrait la transition, etImpossible de passer de ...sinon.
Un évènement du mauvais sens suit la même règle. collect sur une facture d'achat déposée renvoie La transition collect ne peut pas être déclenchée manuellement. ; approve sur une facture de vente déposée renvoie de même La transition approve ne peut pas être déclenchée manuellement., car l'état permet la transition au statut de votre client, jamais à votre appel. Sur une facture d'achat déposée, en revanche, approve renvoie Impossible de passer de deposited à approve. L'état actuel ne permet pas cette transition. : l'état interdit déjà la transition avant que la règle de sens ne soit évaluée.
Vous n'avez pas à recalculer ces règles : lifecycle_available_transitions liste exactement les évènements que votre prochain appel peut déclencher depuis l'état courant, guards et règles de sens inclus.
lifecycle_state ne se modifie pas par PATCH
Le champ lifecycle_state est en lecture seule sur PATCH /api/v1/invoices/{id}. L'envoyer avec une valeur fait échouer tout l'appel en 422, avec le code invalid_argument : aucune modification n'est appliquée, pas même celle des autres champs de la charge utile.
{
"error": "unprocessable_entity",
"code": "invalid_argument",
"message": "lifecycle_state ne peut pas être modifié sur ce point d'entrée. Utilisez PATCH /api/v1/invoices/{id}/transition pour faire évoluer la facture dans son cycle de vie."
}
Le refus porte sur la présence d'une valeur : lifecycle_state: null ou une chaîne vide traverse l'endpoint sans erreur et sans effet. Pour faire avancer la facture, retirez lifecycle_state de votre charge utile de PATCH et appelez l'endpoint de transition, seul à porter reason_code et reason.
Le seul endroit où lifecycle_state est accepté en écriture reste la création : POST /api/v1/workspaces/{workspace_id}/invoices peut créer et déposer la facture dans le même appel (voir Émettre une facture de vente).
Les codes motifs
Quatre statuts exigent un motif normalisé (norme AFNOR XP Z12-012) : l'approbation partielle (206), le litige (207), la suspension (208) et le refus (210). Tous les autres évènements, le dépôt (200) compris, n'acceptent aucun code : y joindre un reason_code renvoie 422.
Le code NON_TRANSMISE (Non transmise) se lit malgré tout sur une facture au statut 200 : c'est Scribee qui le pose, quand l'acheteur est immatriculé (SIREN ou SIRET) et que l'annuaire national ne publie pour lui aucune adresse de facturation électronique active.
Deux codes exigent en plus un texte libre dans reason : AUTRE et REF_ERR. Pour tous les codes, reason reste accepté en complément, dans la limite de 250 caractères, et n'est jamais accepté sans reason_code.
Le refus (210) exige toujours un reason, quel que soit le reason_code retenu. La règle G7.25 demande un commentaire motivant le refus dans la balise MDT-126, et le code motif MDT-113 ne le remplace pas : le code dit quel contrôle est en cause, le texte dit pourquoi cette facture-ci est refusée. Un refuse sans reason renvoie 422 avec code: operation_failed, et la facture ne bouge pas. Le litige (207) n'est pas concerné : il se motive par son seul reason_code, sauf quand ce code est AUTRE ou REF_ERR. Ces deux codes ne changent pas non plus : ils exigeaient déjà un texte libre, et l'exigent toujours, quel que soit le statut.
La suspension (208) exige elle aussi un reason, pour la même règle G7.25 : un suspend sans reason renvoie 422, et la facture ne bouge pas. L'approbation partielle (206) se motive, comme le litige, par son seul reason_code, sauf quand ce code est AUTRE ou REF_ERR.
:::warning Changement de comportement
Jusqu'ici, un refuse accompagné du seul reason_code était accepté : la facture passait bien à 210, mais le compte rendu de refus dû à l'émetteur ne pouvait pas être construit faute de ce commentaire, et rien ne vous le signalait - la transition avait répondu 200. Le contrôle est désormais fait au moment de la transition, là où votre appel peut encore fournir le texte. Si votre intégration refuse des factures en n'envoyant que reason_code, elle reçoit maintenant 422 : ajoutez reason à sa charge utile.
:::
| Code | Libellé | 206 | 207 | 208 | 210 |
|---|---|---|---|---|---|
AUTRE | Autre (texte libre obligatoire) | x | x | ||
COORD_BANC_ERR | Coordonnées bancaires erronées | x | x | ||
TX_TVA_ERR | Taux de TVA erroné | x | x | ||
MONTANTTOTAL_ERR | Montant total erroné | x | x | ||
CALCUL_ERR | Erreur de calcul de la facture | x | x | ||
NON_CONFORME | Mention légale manquante | x | x | ||
DOUBLON | Facture en doublon | x | x | ||
DEST_INC | Destinataire inconnu | x | |||
DEST_ERR | Erreur de destinataire | x | x | ||
TRANSAC_INC | Transaction inconnue | x | x | ||
EMMET_INC | Émetteur inconnu | x | x | ||
CONTRAT_TERM | Contrat terminé | x | x | ||
DOUBLE_FACT | Données réglementaires F1 en doublon | x | x | ||
CMD_ERR | Numéro de commande ou d'engagement incorrect ou manquant | x | x | x | x |
ADR_ERR | Adresse de facturation électronique erronée | x | x | ||
SIRET_ERR | SIRET erroné ou absent | x | x | x | |
CODE_ROUTAGE_ERR | Code routage absent ou erroné | x | x | x | |
REF_CT_ABSENT | Référence contractuelle manquante | x | x | x | x |
REF_ERR | Référence incorrecte (texte libre obligatoire) | x | x | x | |
PU_ERR | Prix unitaire incorrect | x | x | ||
REM_ERR | Remise erronée | x | x | ||
QTE_ERR | Quantité facturée incorrecte | x | x | ||
ART_ERR | Article facturé incorrect | x | x | ||
MODPAI_ERR | Modalités de paiement incorrectes | x | x | ||
QUALITE_ERR | Qualité d'article livré incorrecte | x | x | ||
LIVR_INCOMP | Livraison incomplète ou non effectuée | x | x | ||
JUSTIF_ABS | Justificatif absent ou insuffisant | x |
Un code envoyé sur un statut où il n'est pas admis renvoie 422 : la validation est faite par statut cible, pas sur la liste globale.
L'action attendue d'un litige
Un litige (dispute, statut 207) peut dire, en plus de son motif, ce que vous attendez du fournisseur. Deux champs facultatifs le portent, sous invoice, à côté de reason_code et reason :
requested_action_code: l'action attendue sous forme codée (MDT-121), prise dans la liste fermée ci-dessous.requested_action: sa description en texte libre (MDT-122), dans la limite de 250 caractères.
Les deux sont indépendants : envoyez l'un, l'autre ou les deux. Aucun n'est déduit : un litige envoyé sans eux ne porte aucune action attendue, pas même NOA. Une valeur vide ou faite d'espaces est traitée comme absente.
| Code | Action attendue |
|---|---|
NOA | Aucune action requise |
PIN | Information complémentaire requise |
NIN | Créer une facture rectificative |
CNF | Créer un avoir total |
CNP | Créer un avoir partiel |
CNA | Rembourser le paiement de la facture |
OTH | Autre |
curl -X PATCH https://app.scribee.tech/api/v1/invoices/12345/transition \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"invoice": {"event": "dispute", "reason_code": "QTE_ERR", "requested_action_code": "CNP", "requested_action": "Avoir pour les 3 unités non livrées"}}'
Chacun de ces cas renvoie 422 avec code: operation_failed, et la facture ne bouge pas :
- l'un des deux champs envoyé avec un autre évènement que
dispute,approve_partially,suspendetrefusecompris : il est refusé, pas ignoré ; - un
requested_action_codehors de la liste ci-dessus ; - un
requested_actionnumérique plutôt que textuel, ou qui dépasse 250 caractères.
Les deux champs sont enregistrés avec l'évènement 207, mais include=lifecycle_events ne les restitue pas.
Le montant encaissé
L'encaissement (212) est la seule transition qui porte de l'argent, et la seule qui exige un champ en plus de event : collected_amount. Tous les autres évènements l'ignorent.
Ce montant est déclaré, jamais déduit. La règle P1.15 de l'annexe 7 demande la somme réellement reçue (montant encaissé, MDT-215), et non le montant TTC de la facture : déclarer le total à la place d'un encaissement partiel serait une sur-déclaration auprès du PPF. Scribee ne devine pas cette somme, c'est votre appel qui la nomme.
curl -X PATCH https://app.scribee.tech/api/v1/invoices/12345/transition \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"invoice": {"event": "collect", "collected_amount": 812.34}}'
{
"data": {
"id": 12345,
"lifecycle_state": "collected",
"lifecycle_status_code": "212",
"lifecycle_available_transitions": []
}
}
Trois règles encadrent la valeur :
- Omettre le champ renvoie
422, aveccode: operation_failed, et la facture ne bouge pas. Il n'existe pas de valeur par défaut : uncollectsans montant n'est jamais lu comme un encaissement du total. 0.00est accepté. C'est ainsi que se déclare une correction qui n'a déplacé aucune somme. Zéro est un montant déclaré, pas un montant absent : ne l'envoyez pas pour signifier que vous ignorez la somme reçue.- Une valeur non numérique et un montant négatif sont refusés, tous deux en
422aveccode: invalid_argument, et aucun encaissement n'est enregistré. Une valeur non numérique est refusée plutôt que lue comme zéro, pour que la facture n'atteigne jamais212en portant un chiffre que vous n'avez pas écrit. Un montant négatif est un décaissement (règle P1.17 de l'annexe 7) : son motif d'annulation (MDT-126) n'a pas encore d'élément d'émission défini, donc le compte rendu correspondant ne pourrait pas être produit.
Aucun plafond n'est appliqué : collected_amount n'est comparé ni au solde restant ni au montant TTC de la facture.
L'autre porte : enregistrer un paiement
collect reste réservé aux factures de vente (voir la section Qui déclenche quoi, plus haut). L'enregistrement d'un paiement mène au même encaissement, quel que soit le sens de la facture, et c'est alors Scribee qui fournit le montant encaissé : celui du paiement que vous venez d'enregistrer, jamais le cumul réglé.
- Un paiement qui solde la facture déclenche lui-même
collect: la facture passe à212Encaissée, et l'évènement porte le montant de ce paiement. - Un paiement partiel inscrit lui aussi un évènement
212portant son propre montant, mais ne solde pas la facture :lifecycle_statene bouge pas. Un évènement212dans l'historique ne suffit donc pas à conclure que la facture est encaissée - c'estlifecycle_statequi fait foi.
Le détail est dans Enregistrer les paiements.
Pas à pas
L'exemple suit une facture d'achat importée par fichier, mise à disposition (203). Commencez toujours par la lecture : elle n'a aucun effet de bord et vous donne la liste exacte des transitions possibles.
1. Lire le statut courant
GET /api/v1/invoices/{id} retourne l'état du cycle de vie et les transitions disponibles ; le scope read suffit.
curl https://app.scribee.tech/api/v1/invoices/12345 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Réponse abrégée aux champs du cycle de vie :
{
"data": {
"id": 12345,
"invoice_number": "FRN-2026-0042",
"direction": "purchases",
"lifecycle_state": "available",
"lifecycle_status_code": "203",
"lifecycle_available_transitions": ["take_in_charge", "approve", "dispute", "refuse", "send_payment", "revert_to_draft"]
}
}
2. Déclencher une transition
Cet appel écrit un événement de cycle de vie horodaté dans l'historique de la facture ; l'historique ne se supprime pas, et aucune transition ne ramène une facture approuvée en arrière. L'approbation (205) n'est transmise ni à votre fournisseur ni sur un réseau externe. Le scope write est requis.
curl -X PATCH https://app.scribee.tech/api/v1/invoices/12345/transition \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"invoice": {"event": "approve"}}'
{
"data": {
"id": 12345,
"lifecycle_state": "approved",
"lifecycle_status_code": "205",
"lifecycle_available_transitions": ["send_payment"]
}
}
3. Motiver un litige ou un refus
Un refus est définitif : aucune transition ne part du statut 210. Seul un utilisateur de Scribee peut, depuis l'interface, rouvrir une facture d'achat dont le refus n'a jamais quitté Scribee (voir Rouvrir une facture d'achat refusée). En cas de doute, préférez le litige (dispute), qui laisse ouvertes l'approbation et le refus. Les deux exigent un reason_code de la table ci-dessus, et le refus exige en plus le texte libre reason.
curl -X PATCH https://app.scribee.tech/api/v1/invoices/12345/transition \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"invoice": {"event": "refuse", "reason_code": "TX_TVA_ERR", "reason": "Taux de TVA à 20 % au lieu de 10 % sur la ligne 2"}}'
{
"data": {
"id": 12345,
"lifecycle_state": "refused",
"lifecycle_status_code": "210",
"lifecycle_available_transitions": []
}
}
4. Relire l'historique
include=lifecycle_events restitue la piste d'audit, par ordre chronologique croissant.
curl "https://app.scribee.tech/api/v1/invoices/12345?include=lifecycle_events" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Réponse abrégée :
{
"data": {
"id": 12345,
"lifecycle_state": "refused",
"lifecycle_events": [
{
"id": 881,
"status_code": "203",
"status_label": "Mise à disposition",
"state": "available",
"occurred_at": "2026-03-02T09:15:00+01:00",
"sender_role": null,
"sender_id": null,
"sender_scheme_id": null,
"reason": null,
"reason_code": null,
"terminal": false,
"rejected": false,
"applied": true,
"created_at": "2026-03-02T09:15:00+01:00"
},
{
"id": 902,
"status_code": "210",
"status_label": "Refusée",
"state": "refused",
"occurred_at": "2026-03-04T16:40:00+01:00",
"sender_role": null,
"sender_id": null,
"sender_scheme_id": null,
"reason": "Taux de TVA à 20 % au lieu de 10 % sur la ligne 2",
"reason_code": "TX_TVA_ERR",
"terminal": true,
"rejected": false,
"applied": true,
"created_at": "2026-03-04T16:40:00+01:00"
}
]
}
}
Trois champs sont toujours null sur les événements que vos appels produisent : sender_role, sender_id et sender_scheme_id. L'endpoint de transition ne transmet pas d'émetteur, et l'API n'expose aucun paramètre pour en fournir un. N'utilisez pas ces champs pour distinguer l'origine d'un événement. De même, applied vaut toujours true et rejected toujours false sur ces événements. terminal vaut true sur les statuts 210 et 212, sauf sur un 210 rouvert, où il vaut false.
Rouvrir une facture d'achat refusée
Pour l'API, le refus reste définitif : aucune transition ne part de refused, lifecycle_available_transitions y est vide et aucun évènement ne permet d'en sortir. Une seule exception existe, et elle ne passe pas par l'API : un utilisateur de Scribee peut, depuis l'interface, rouvrir une facture d'achat refusée par erreur, motif obligatoire à l'appui. La réouverture n'est possible que si le refus n'a jamais quitté Scribee, c'est-à-dire si toutes ces conditions sont réunies :
- la facture est une facture d'achat reçue, et non une autofacture établie dans Scribee ;
- elle n'a été reçue ni par le réseau (PPF ou Peppol), ni par Chorus Pro ;
- aucune transmission n'a été enregistrée pour son statut
210, même si elle n'est jamais partie : un envoi inscrit suffit à bloquer la réouverture.
Une facture refusée qui ne remplit pas l'une de ces conditions reste refusée, sans exception.
Ce que votre intégration observe après une réouverture :
lifecycle_staterepasse àavailableetlifecycle_status_codeà203;lifecycle_available_transitionsredevient celle d'une facture mise à disposition.- Aucun évènement de cycle de vie n'est créé : la réouverture n'est pas un statut AIFE et rien n'est transmis. L'évènement
210reste danslifecycle_events, avecappliedàtrue, mais sonterminalpasse àfalse. L'historique peut donc se terminer sur un210alors que la facture est àavailable: lisez l'état courant danslifecycle_state, jamais dans le dernier évènement. - Aucun webhook n'est émis. La livraison
invoice.lifecycle_event.createddu refus portaitterminalàtrue, et rien n'annonce la réouverture : relisezGET /api/v1/invoices/{id}pour connaître l'état courant. - Un nouveau refus après la réouverture crée un nouvel évènement
210, de nouveau terminal.
Revenir en brouillon
revert_to_draft ne concerne que les factures entrées dans Scribee par fichier - POST /api/v1/workspaces/{workspace_id}/invoices/upload, voir Importer des factures existantes - et jamais rattachées à un flux Peppol ou PPF. Une facture déposée revient de 200 en brouillon ; une facture d'achat mise à disposition revient de 203 en brouillon.
Une facture créée par POST /api/v1/workspaces/{workspace_id}/invoices n'est jamais concernée : revert_to_draft n'apparaît pas dans son lifecycle_available_transitions et l'envoyer renvoie 422. Une correction passe par un avoir, décrit dans Émettre une facture de vente.
De retour en brouillon, la facture est de nouveau modifiable par PATCH. DELETE /api/v1/invoices/{id} exige le scope destroy ou write : un token qui n'a ni l'un ni l'autre reçoit 403 forbidden. Le scope présent, la suppression reste refusée tant que la facture ne réunit pas toutes ces conditions : en brouillon (lifecycle_state: draft), jamais échangée sur un réseau externe (Peppol ou PPF), sans aucun événement de cycle de vie enregistré, absente de tout export comptable, et non issue de la conversion d'un devis. Une dernière condition ne s'applique qu'aux factures créées directement dans Scribee (formulaire, conversion de devis, ou POST /api/v1/workspaces/{workspace_id}/invoices) : leur numéro doit encore commencer par DRAFT-, faute de quoi la suppression est refusée. Une facture importée par fichier (POST /api/v1/workspaces/{workspace_id}/invoices/upload) échappe à cette dernière condition : son numéro d'origine, même définitif, ne bloque jamais la suppression. Un rapprochement d'achat confirmé, lui, bloque la suppression quel que soit l'état de la facture, brouillon ou non.
Chaque condition non remplie renvoie 422, dans une enveloppe {error, code, message} sans clé details, avec le code operation_failed :
{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Cette facture a consommé un numéro séquentiel et ne peut pas être supprimée. Passez par une annulation ou un avoir."
}
message | Cause |
|---|---|
| Cette facture ne peut pas être supprimée car elle est rapprochée d'un bon de commande ou d'un reçu confirmé. | rapprochement d'achat confirmé, quel que soit l'état de la facture |
| Cette facture a été échangée via un réseau externe (Peppol/PPF) et ne peut pas être supprimée. | brouillon rattaché à un flux Peppol ou PPF |
| Cette facture est déjà entrée dans le cycle de vie de facturation et ne peut pas être supprimée. | au moins un événement de cycle de vie déjà enregistré |
| Cette facture fait partie d'un export comptable et ne peut pas être supprimée. | écriture comptable déjà incluse dans un export |
| Cette facture est issue de la conversion d'un devis et ne peut pas être supprimée. | créée par conversion d'un devis |
| Cette facture a consommé un numéro séquentiel et ne peut pas être supprimée. Passez par une annulation ou un avoir. | créée directement dans Scribee, avec un numéro qui ne commence plus par DRAFT- |
| Seules les factures en brouillon peuvent être supprimées | la facture n'est plus en brouillon |
Dans de très rares cas de concurrence - une facture qui entre en dépôt au moment précis de sa suppression -, l'échec renvoie un message générique, « Échec de la suppression de la facture », sans rapport avec les conditions ci-dessus.
:::caution N'envoyez jamais un invoice_number commençant par DRAFT-
DRAFT- est le préfixe réservé au placeholder interne, et invoice_number n'est pas filtré à la création : une facture créée avec un numéro de cette forme est, elle, supprimable, et surtout son dépôt ne conserve pas ce numéro - il est remplacé par la séquence de l'entreprise, ou refusé quand aucun modèle de numérotation n'est configuré. Choisissez n'importe quel autre préfixe.
:::
Une facture qui échoue à l'une de ces conditions se corrige par PATCH tant qu'elle reste en brouillon, ou par un avoir une fois sortie du brouillon.
Le statut de paiement
Scribee dérive de chaque statut de cycle de vie un statut de paiement, affiché dans l'interface Scribee. Il n'est pas exposé par l'API. Côté API, appliquez la même dérivation à lifecycle_state :
| Statut de paiement | Statuts de cycle de vie |
|---|---|
| En attente | 000, 200, 202, 203, 204, 205, 207 |
| En cours | 211 |
| Payée | 212 |
| Non applicable | 210, 213, 220 |
Le verdict du PPF sur le dépôt
lifecycle_state ci-dessus est le suivi Scribee du cycle de vie commercial de la facture. Ce que l'administration fiscale a décidé du dépôt de cette facture est une tout autre chose, publiée par trois champs distincts, tous en lecture seule : deposit_outcome, deposit_outcome_at et deposit_receptions.
Ne confondez pas les deux. Une facture refusée par l'acheteur est lifecycle_state: "refused" (code AIFE 210) : le document a bien été déposé et acheminé, et c'est votre client qui le rejette pour une raison commerciale. Un deposit_outcome à "251" dit l'inverse : c'est le dépôt lui-même que l'administration n'a pas accepté, donc la facture n'est jamais parvenue à un acheteur. Les deux faits ont des listes de codes différentes, des causes différentes et des suites différentes. Une intégration qui les fusionne se trompera dans les deux sens.
deposit_outcome : le code du verdict
Chaîne de caractères ou null. Lorsque le Portail Public de Facturation (PPF) s'est prononcé, ce champ porte son verdict sous la forme du code de la réforme elle-même (Dossier de spécifications externes FE v3.2, section 3.6.8, Tableau 9), rapporté par l'interface FFE0604A :
| Valeur | Signification |
|---|---|
"250" | Déposée : la facture a été contrôlée conforme et prise en compte |
"251" | Rejetée : la facture a été contrôlée non conforme, elle n'est ni intégrée ni prise en compte |
null signifie que le PPF ne s'est pas encore prononcé sur ce dépôt. Ce n'est ni une erreur, ni une valeur par défaut, ni un troisième statut : lisez-le comme "pas encore de réponse" et relisez la facture plus tard.
Un cas où null est définitif, et où relire la facture plus tard ne changera rien. L'obligation d'émission de la réforme entre en vigueur par vagues, et la première est celle du 1er septembre 2026. Tant que la société émettrice n'est pas déclarée dans cette vague, Scribee ne dépose rien auprès du PPF pour ses factures de vente : aucun verdict n'est donc attendu, et deposit_outcome reste null indéfiniment plutôt que temporairement. C'est un réglage de société, dans Réglages entreprise > Facturation électronique, pas une propriété de la facture ni un champ de l'API.
Le miroir de ce cas, pour une société qui est dans la vague. Une facture de vente d'une société déclarée dans la vague doit être adressée à une adresse de facturation électronique du destinataire (schéma 0225) avant d'être déposée. Cette résolution ne vaut que pour les ventes à un acheteur porteur d'un SIREN et établi dans le territoire de TVA français, ou sans pays renseigné, les seules qui font l'objet d'un dépôt de facture : une vente à un autre acheteur - sans SIREN, ou porteur d'un SIREN mais établi dans un pays étranger ou dans l'outre-mer hors de ce territoire - se dépose sans qu'aucune adresse d'annuaire soit cherchée, et c'est l'e-reporting qui la porte (voir Déclarer les transactions). Aucune adresse n'est cherchée non plus lorsque le vendeur est établi en Guyane, à Mayotte, dans une collectivité d'outre-mer ou dans les Terres australes et antarctiques françaises : la facture n'est alors pas déposée auprès du PPF (voir L'outre-mer hors du territoire de TVA). Scribee résout l'adresse au moment du dépôt, sans appel réseau, dans cet ordre : la valeur portée par la facture (directory_routing_identifier de l'acheteur, y compris celle que la facture a recopiée de la fiche client quand l'acheteur de votre charge utile de création porte un party_id), puis l'adresse électronique de l'acheteur portée par la facture (endpoint_id) lorsque son endpoint_scheme_id est aife (0225) - celle de votre charge utile de création, ou le BT-49 d'une facture UBL, CII ou Factur-X importée -, puis la valeur de la fiche client, puis l'annuaire national. Une adresse électronique sous un autre schéma (siret, siren...) n'entre pas dans ce choix.
Trois issues, et deux sont un échec de votre appel :
- Aucune adresse active publiée pour un acheteur immatriculé : le dépôt aboutit. La facture passe à
200et Scribee y pose le motifNON_TRANSMISE(voir Les codes motifs). C'est le cas nominal d'un acheteur qui n'est pas encore équipé : la facture est bien déposée, elle n'est simplement pas remise à une plateforme destinataire. - Plusieurs adresses actives publiées, sans que Scribee puisse choisir : le dépôt est refusé en
422et rien n'est enregistré. - Une adresse enregistrée que l'annuaire ne permet pas d'exploiter : la valeur portée par la facture, à défaut son adresse électronique
aife(0225), ou à défaut encore celle de la fiche client, ne se lit pas comme une adresse de schéma0225. Scribee ne redescend pas au niveau suivant - ce serait réacheminer la facture vers une adresse que vous n'avez pas choisie - donc le dépôt est refusé en422et rien n'est enregistré non plus.
Dans ces deux refus, le corps de la réponse porte details avec la clé buyer.directory_routing_identifier : c'est le champ à renseigner, sur la fiche client ou dans la charge utile de création. Un endpoint_id sous endpoint_scheme_id aife (0225) sur l'acheteur de la charge utile désigne lui aussi l'adresse, mais seulement quand la facture ne porte aucun directory_routing_identifier : quand plusieurs adresses actives sont publiées, c'est alors lui qui fait le choix. Un acheteur désigné par party_id hérite du directory_routing_identifier de la fiche client, qui l'emporte sur cet endpoint_id ; pour choisir une autre adresse sur une telle facture, envoyez directory_routing_identifier dans l'entrée de l'acheteur, qui prime sur la valeur recopiée. Un troisième refus, beaucoup plus rare, relève de la concurrence : quand l'adresse de la fiche client change pendant le dépôt lui-même, la facture n'est pas déposée plutôt que d'être annoncée sur une adresse et enregistrée sur une autre ; il suffit de la redéposer. Une fois la facture déposée, l'adresse est figée sur la facture : une nouvelle tentative relit cette valeur, ne refait aucune recherche, et ne réachemine donc jamais silencieusement une facture déjà annoncée ailleurs.
Ces refus passent avant le contrôle sémantique, et cela se voit. L'adresse est résolue avant que la facture ne soit validée, parce que les octets contrôlés portent eux-mêmes cette adresse (BT-49). Une facture qui cumule les deux problèmes - destinataire non adressable et assertion sémantique fatale - remonte donc d'abord l'erreur d'adresse, et le verdict Schematron ne s'exprime pas. Ne concluez pas de son absence que la facture est sémantiquement valide : corrigez l'adresse, puis redéposez pour obtenir le verdict.
Un refus distinct, et qui ne se corrige pas au même endroit. Un acheteur établi dans le territoire de TVA français dont l'immatriculation est renseignée mais ne se lit ni comme un SIREN ni comme un SIRET fait refuser le dépôt en 422, avant même la résolution d'adresse. Le corps de la réponse porte details avec la clé buyer.legal_registration_id : c'est l'immatriculation qu'il faut corriger sur la fiche client, pas l'adresse. La ligne de partage est là : une immatriculation présente mais illisible est une donnée à corriger, tandis qu'un acheteur qui n'en porte aucune est un client sans immatriculation - sa facture se dépose, et la vente part en e-reporting (voir Déclarer les transactions).
Un refus de plus, sur une donnée que votre charge utile ne porte pas toujours. Une facture de vente dont le vendeur ou l'acheteur ne porte aucun pays fait refuser le dépôt en 422. Le pays de chaque partie est obligatoire à la facture électronique - BT-40 pour le vendeur, BT-55 pour l'acheteur - et Scribee n'en fabrique aucun : une partie enregistrée sans pays en reste dépourvue, et c'est le dépôt qui le réclame. Le corps de la réponse porte details avec la clé seller.address.country_code, buyer.address.country_code, ou les deux quand les deux parties sont muettes - le chemin exact sous lequel la lecture de la facture restitue ce champ. Le pays du vendeur se renseigne sur l'établissement siège de votre société, celui de l'acheteur dans l'adresse de la fiche client ou dans la charge utile de création. Ce refus suit le même périmètre que la résolution d'adresse ci-dessus : il ne vise que les ventes qui font l'objet d'un dépôt de facture, jamais une vente portée par l'e-reporting, dont le rapport n'exige aucun pays.
:::warning Changement de comportement
Jusqu'ici, une partie sans pays en recevait un d'office : Scribee écrivait FR, et la facture se déposait en affirmant un territoire que personne n'avait déclaré. Ce code n'est plus fabriqué. Une fiche client dont l'adresse ne porte pas de pays, comme un établissement siège sans pays, produit désormais une partie sans pays, et son dépôt est refusé plutôt qu'accepté sur une donnée inventée. Renseignez le pays sur les fiches concernées avant votre prochain dépôt.
:::
Le reste du cycle de vie est inchangé pour ces sociétés : la facture se crée, se modifie, se dépose (statut 200), s'exporte, part vers un logiciel comptable connecté et notifie vos webhooks exactement comme décrit plus haut. Seuls les envois réglementaires vers le PPF et Peppol attendent l'entrée dans la vague.
Leur dépôt n'est pas jugé non plus sur les règles de la facture électronique. Rien de ce qu'une société hors vague émet n'étant transmis, le contrôle Schematron décrit plus bas ne s'exerce pas sur ses ventes, pas plus que les refus de la voie B2C. Les autres contrôles du dépôt, eux, s'appliquent comme à tout le monde : erreurs d'import bloquantes, conformité du PDF que vous avez fourni, mentions légales obligatoires, modèle de numérotation, taux de change.
deposit_outcome_at : l'instant du verdict
Chaîne de caractères ou null. C'est l'instant où le PPF a horodaté ce verdict, republié tel qu'il l'a envoyé, au format YYYY-MM-DDTHH:MM:SS.
Il ne porte volontairement aucun indicateur de fuseau horaire. La réforme ne déclare pas de référence de temps pour ce champ : lui attacher un décalage reviendrait à publier une précision que le message n'a jamais transportée. Ne le parsez donc pas comme un instant UTC, à la différence de created_at, updated_at et du received_at des réceptions, qui sont des instants complets avec fuseau.
Ce champ vaut null exactement quand deposit_outcome vaut null.
deposit_receptions : les motifs de rejet, groupés par réception
Toujours un tableau, jamais null. Un tableau vide signifie qu'aucun motif de rejet n'est au dossier, ce qui est le cas normal d'une facture déposée comme d'une facture sans réponse.
Chaque entrée correspond à une réception FFE0604A qui a transporté des motifs de rejet pour le dépôt de cette facture, la plus ancienne en premier. Elle porte deux champs :
received_at: le moment où Scribee a reçu cette réception.motives: les motifs qu'elle transportait, dans l'ordre du document. Un rejet en porte au moins un.
Chaque motif porte à son tour :
-
code: le contrôle qui a échoué (MDT-113, section 3.6.9, Tableau 11). Trois valeurs, et la liste est close :Valeur Contrôle en échec REJ_SEMANContrôle sémantique ou de format REJ_UNIContrôle d'unicité : la donnée a déjà été transmise et traitée REJ_COHContrôle de cohérence des données REJ_PER, le contrôle de période, n'existe pas sur cette interface : il appartient à la liste de l'e-reporting (section 3.7.10, Tableau 6, voir L'e-reporting). Les deux listes se ressemblent et ne sont pas la même. Un dépôt de facture ne déclare aucune période sur laquelle un contrôle de période pourrait échouer. -
anomaly_source: chaîne de caractères ounull(MDT-126). Le texte libre du PPF disant où se situe l'anomalie, là oùcodedit quel contrôle a échoué. C'est la partie exploitable pour votre utilisateur : affichez-la. Elle porte jusqu'à 2 000 caractères et n'est jamais tronquée. Seul le code est obligatoire sur un rejet : ce champ vautnullquand le PPF n'a envoyé aucun texte.
deposit_outcome est le dernier verdict, pas l'historique
C'est le point à ne pas manquer, et la raison pour laquelle les motifs sont groupés par réception plutôt que listés à plat sur la facture. L'administration peut se prononcer plusieurs fois sur une même facture, et chaque réponse porte ses propres raisons. deposit_outcome porte le dernier verdict connu, et rien d'autre. deposit_receptions porte l'historique des reproches. Les deux ne se contredisent jamais : ils ne répondent pas à la même question, et un verdict plus récent n'efface pas les motifs d'une réception antérieure.
Déroulé d'une facture rejetée, corrigée, puis redéposée :
- Le PPF rejette le dépôt.
deposit_outcomevaut"251",deposit_outcome_atporte l'instant de ce rejet, etdeposit_receptionscontient une entrée : la réception qui a transporté les motifs. - La facture est corrigée puis redéposée, et le PPF l'accepte.
deposit_outcomepasse à"250"etdeposit_outcome_atporte l'instant de cette acceptation. Mais l'entrée du rejet précédent reste dansdeposit_receptions, avec sonreceived_atet ses motifs inchangés.
À l'étape 2, la facture ressemble à ceci :
{
"id": 4312,
"invoice_number": "FA-2026-0087",
"lifecycle_state": "deposited",
"lifecycle_status_code": "200",
"deposit_outcome": "250",
"deposit_outcome_at": "2026-03-11T14:05:00",
"deposit_receptions": [
{
"received_at": "2026-03-04T09:30:15Z",
"motives": [
{
"code": "REJ_COH",
"anomaly_source": "Ligne 3 : total incohérent"
},
{
"code": "REJ_SEMAN",
"anomaly_source": null
}
]
}
]
}
Une intégration qui aplatit deposit_receptions en une simple liste de motifs et les affiche comme "les raisons du rejet de cette facture" se trompera donc exactement au moment où le client a déjà corrigé le problème : elle annoncera un rejet sur une facture déposée.
La règle est simple. Pour savoir où en est le dépôt, lisez deposit_outcome. Pour savoir ce qui a été reproché et quand, lisez deposit_receptions en gardant chaque groupe de motifs attaché à son received_at. Un deposit_receptions non vide à côté d'un deposit_outcome à "250" n'est pas une incohérence : c'est la trace d'un rejet depuis corrigé.
Ce qui se passe ensuite
- Chaque transition pousse un événement
invoice.lifecycle_event.createdvers vos endpoints de webhook (Webhooks, catégorie Plateforme), y compris les encaissements et désencaissements déclenchés par vos paiements. Votre système n'a pas besoin d'interroger l'API en boucle pour suivre les transitions qu'il déclenche. - Deux statuts suivent une règle de transmission propre. Un
212Encaissée n'est déclaré au PPF que si la TVA de la facture est due à l'encaissement : facture d'acompte,tax_due_date_codeà72, ou, sans ce code, un cadre de facturation (BT-23) qui ne commence pas parB; sinon il ne part que vers la plateforme de l'autre partie. Un213Rejetée que Scribee pose à l'émission d'une facture de vente part vers le PPF seul, jamais vers la plateforme du destinataire. - Le dépôt n'attribue un numéro depuis la séquence de numérotation de l'entreprise que si la facture porte encore un numéro préfixé
DRAFT-, ce qui est le cas des factures créées depuis l'interface Scribee - et le serait aussi d'une facture API à laquelle vous auriez donné vous-même un numéro de cette forme. Une facture créée parPOST /api/v1/workspaces/{workspace_id}/invoicesconserve donc son numéro au dépôt dès lors qu'il n'a pas ce préfixe : celui que vous avez envoyé, ou le placeholderTEMP-...généré à la création si vous n'en avez envoyé aucun. Si vous voulez un numéro maîtrisé côté partenaire, envoyez-le à la création ou corrigez-le parPATCHavant le dépôt. - À l'entrée dans le cycle de vie (
depositoumake_availabledepuis le brouillon), Scribee régénère les fichiers de la facture : UBL, CII, Factur-X et PDF (voir Formats et téléchargements). Le PDF fait exception dans deux cas : quand vous avez fourni le PDF vous-même, et quand la facture a été importée sous forme de PDF ou d'image - le fichier d'origine est alors conservé tel quel. Le Factur-X fait exception lui aussi quand la facture a été déposée sous cette forme : le fichier déposé est restitué au lieu d'être régénéré. La génération est asynchrone : un téléchargement lancé immédiatement après la transition peut encore renvoyer le fichier précédent.
Erreurs et cas limites
Les échecs de transition renvoient 422 avec error: "unprocessable_entity", un code machine, un message en français et un objet details. Le code vaut invalid_argument quand l'évènement lui-même est inconnu ou manquant, operation_failed dans la plupart des autres cas, l'un des trois codes du contrôle Schematron décrits plus bas quand c'est ce contrôle qui refuse le dépôt, et l'un des deux codes du contrôle PDF/A-3 d'un Factur-X importé, décrits eux aussi plus bas, quand c'est ce contrôle-là. details est vide sauf pour le refus des mentions légales obligatoires et pour celui d'un Factur-X non conforme PDF/A-3, décrits plus bas. Basez vos traitements sur le statut HTTP et sur code, jamais sur le texte du message (Conventions de l'API).
{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Un motif normalisé est requis pour le statut En litige",
"details": {}
}
message | Cause | Ce que vous faites |
|---|---|---|
| Évènement de cycle de vie inconnu ou manquant. Fournissez un nom d'évènement valide parmi les transitions disponibles de la facture. | event absent, inconnu, ou uncollect | envoyez un évènement listé dans lifecycle_available_transitions |
| Impossible de passer de approved à deposit. L'état actuel ne permet pas cette transition. | la transition n'existe pas depuis l'état courant | relisez la facture et repartez de lifecycle_available_transitions |
| La facture comporte des erreurs d'import bloquantes. Corrigez-les avant de changer son statut. | deposit ou make_available sur un brouillon porteur d'une erreur d'import bloquante autre qu'un verdict de non-conformité Factur-X | PATCH la facture entière pour effacer les erreurs, puis rejouez la transition |
| Le PDF Factur-X joint n'est pas conforme. Remplacez-le par un fichier conforme avant de déposer la facture. | deposit d'une facture dont vous avez fourni le PDF, évalué non conforme | fournissez un PDF conforme par PATCH, puis redéposez |
| Des mentions légales obligatoires sont absentes de la facture (pénalités de retard, frais de recouvrement et escompte). Renseignez-les dans les paramètres de facturation de l'entreprise, puis réessayez. | deposit d'une facture de vente à laquelle il manque une des trois mentions légales | lisez details, qui nomme les attributs à renseigner |
| Cette facture a été créée alors que la société était membre d'un assujetti unique, mais elle ne le déclare pas en entier (SIREN du vendeur sous le schéma 0231, raison sociale, numéro de TVA et adresse de l'assujetti unique, note MEMBRE_ASSUJETTI_UNIQUE). Complétez l'assujetti unique dans les paramètres de la société, puis réenregistrez le brouillon avant de le déposer. | deposit d'une vente créée alors que la société déclarait un assujetti unique, qui n'en porte pas la déclaration complète | complétez les paramètres d'assujetti unique de la société, PATCH le brouillon, puis redéposez |
| La transition receive ne peut pas être déclenchée manuellement. | évènement réservé au moteur d'états, ou évènement du mauvais sens alors que l'état permettrait la transition | vérifiez direction et repartez de lifecycle_available_transitions |
| Un motif normalisé est requis pour le statut En litige | dispute ou refuse sans reason_code | ajoutez un code de la table des motifs |
| Un statut Refusée (210) exige un commentaire motivant le refus dans la balise MDT-126 (règle G7.25) ; le code motif MDT-113 ne le remplace pas, et aucun commentaire n'a été enregistré avec ce statut. | refuse sans reason | ajoutez le texte libre motivant le refus |
| Marquer une facture comme encaissée exige le montant encaissé. Transmettez collected_amount avec la transition, ou enregistrez le paiement sur la facture, ce qui déclare l'encaissement avec le montant réellement reçu. | collect sans collected_amount | ajoutez collected_amount, ou enregistrez un paiement |
| collected_amount doit être un nombre. Le montant réellement encaissé (MDT-215) est enregistré tel qu'il est transmis, jamais déduit : une valeur non numérique est donc refusée plutôt qu'interprétée comme zéro. | collected_amount non numérique | envoyez un nombre |
| Un montant encaissé négatif (décaissement) sous le code de statut 212 exige un motif d'annulation dans le champ commentaire (règle P1.17 de l'annexe 7, MDT-126), et ce champ n'a aucun élément d'émission défini à ce jour ; le CDAR n'est donc pas construit plutôt qu'émis sans son motif. | collected_amount négatif | envoyez un montant positif ou nul |
| Le motif REJ_SEMAN n'est pas autorisé pour le statut En litige | code hors de la liste du statut cible, ou reason_code envoyé sur un évènement qui n'en accepte aucun | choisissez un code admis, ou retirez le code |
| Un commentaire est requis pour le motif AUTRE | AUTRE ou REF_ERR sans reason | ajoutez le texte libre |
| Le motif doit comporter au maximum 250 caractères | reason trop long | raccourcissez le texte |
| Le motif doit être une valeur textuelle | reason envoyé comme nombre ou booléen | envoyez une chaîne |
| Un code motif est requis lorsqu'un commentaire est fourni | reason envoyé sans reason_code | ajoutez le code, ou retirez le texte |
| Impossible de déposer la facture : veuillez renseigner un numéro de facture unique. | dépôt d'une facture importée dont le numéro est resté au placeholder interne DRAFT- | renseignez le numéro d'origine de la facture par PATCH avant le dépôt |
| Impossible de déposer la facture : aucun modèle de numérotation configuré pour cette entreprise | dépôt d'une facture au placeholder DRAFT- issue de l'interface Scribee, sans modèle de numérotation | configurez le modèle de numérotation depuis l'interface Scribee |
| Impossible de déposer cette facture en USD : aucun taux de change n'est disponible pour le 12/03/2026. Une facture en devise étrangère doit porter son total de TVA en EUR, ce qui exige un taux publié. Attendez la synchronisation des taux, puis relancez le dépôt. | deposit d'une facture libellée dans une autre devise que l'EUR, sans taux publié pour sa date d'émission | redéposez une fois le taux disponible, ou corrigez issue_date si elle est erronée |
| Cette vente s'adresse à un client que la facture n'identifie pas : elle est déclarée en données de transaction et non transmise comme facture. Cette déclaration précise si l'opération porte sur des biens ou sur des services, or la facture ne porte aucun cadre de facturation et ses lignes n'en impliquent aucun. Renseignez le cadre de facturation sur la facture, puis redéposez-la. | deposit d'une vente à un acheteur non identifié, sans cadre de facturation | envoyez invoicing_process_id, puis redéposez |
| Cette vente s'adresse à un client que la facture n'identifie pas : elle est déclarée en données de transaction et non transmise comme facture. Son cadre de facturation M1 couvre à la fois des biens et des services, or cette déclaration compte les deux séparément, ligne à ligne. Émettez les biens et les services sur des factures distinctes, puis redéposez-les. | deposit d'une vente à un acheteur non identifié, sous un cadre de facturation mixte (M1, M2, M4) | scindez la facture en une facture de biens et une facture de services |
| Cette vente s'adresse à un client que la facture n'identifie pas : elle est déclarée en données de transaction et non transmise comme facture. Cette déclaration porte une vente relevant du régime de la marge dans une catégorie qui lui est propre, or la facture mêle des ventilations de TVA relevant du régime de la marge et des ventilations ordinaires - une répartition qui n'est pas encore prise en charge. Émettez les ventes relevant du régime de la marge sur une facture distincte, puis redéposez-les. | deposit d'une vente à un acheteur non identifié qui mêle une ventilation de TVA au régime de la marge et une autre ventilation de TVA | émettez les ventes au régime de la marge sur une facture distincte |
| Cette vente s'adresse à un client que la facture n'identifie pas : elle est déclarée en données de transaction et non transmise comme facture. Relevant du régime de la marge, elle y est déclarée sur sa marge hors TVA et la TVA due sur cette marge, deux montants que la facture n'indique pas : sa ventilation au régime de la marge porte le prix de vente au taux nul. Renseignez la base de marge de la vente pour chaque taux de TVA (marge hors TVA et TVA sur la marge, réelles ou estimées), puis redéposez-la. | deposit d'une vente à un acheteur non identifié qui porte une ventilation de TVA au régime de la marge, sans base de marge | fournissez les bases par un PATCH qui ne porte que margin_bases, puis redéposez |
| Cette vente s'adresse à un client que la facture n'identifie pas : elle est déclarée en données de transaction et non transmise comme facture. Une base de marge fournie pour cette vente relevant du régime de la marge n'est pas cohérente : son taux n'est pas un taux de TVA français admis, sa TVA sur la marge s'écarte de plus d'un centime de sa marge hors TVA taxée à ce taux, ou les marges TVA comprise dépassent le prix de vente de la facture. Corrigez la base, la TVA ou le taux de chaque base de marge pour que la marge ne dépasse pas le prix de vente, puis redéposez-la. | deposit d'une vente à un acheteur non identifié au régime de la marge dont une base de marge est incohérente | corrigez les bases par un PATCH qui ne porte que margin_bases, puis redéposez |
| Cette vente s'adresse à un client que la facture n'identifie pas : elle est déclarée en données de transaction et non transmise comme facture. Cette déclaration additionne les opérations de la journée, or la facture ne porte pas de montant total hors TVA ou pas de montant total de TVA. Complétez les totaux de la facture, puis redéposez-la. | deposit d'une vente à un acheteur non identifié, sans total hors TVA ou sans total de TVA | complétez les totaux, puis redéposez |
| Cette vente s'adresse à un client que la facture n'identifie pas : elle est déclarée en données de transaction et non transmise comme facture. Cette déclaration est faite en euros, or la facture est libellée en USD et ne porte aucune contre-valeur en euros de son montant de TVA. Renseignez le montant de TVA en euros, puis redéposez-la. | deposit d'une vente à un acheteur non identifié, libellée dans une autre devise que l'EUR et sans contre-valeur en euros du total de TVA | renseignez le total de TVA en euros, puis redéposez |
| Cette vente s'adresse à un client que la facture n'identifie pas : elle est déclarée en données de transaction et non transmise comme facture. Cette déclaration porte toujours une ventilation par taux de TVA, or la facture n'en porte aucune, y compris lorsque ses montants sont nuls. Ajoutez au moins une ligne de ventilation de TVA - au taux 0 et pour un montant nul s'il s'agit d'un ticket exonéré -, puis redéposez-la. | deposit d'une vente à un acheteur non identifié, sans aucune ligne de ventilation de TVA | ajoutez au moins une ligne de ventilation, puis redéposez |
| Cette vente s'adresse à un client que la facture n'identifie pas : elle est déclarée en données de transaction et non transmise comme facture. Cette déclaration porte les totaux de la journée en regard de leur ventilation par taux de TVA, or les totaux de la facture ne correspondent pas à la somme de sa ventilation de TVA. Corrigez la ventilation de TVA ou les totaux de la facture pour qu'ils concordent exactement, au centime près, puis redéposez-la. | deposit d'une vente à un acheteur non identifié dont les totaux ne correspondent pas à la somme de sa ventilation de TVA | alignez la ventilation et les totaux au centime près, puis redéposez |
| Cette facture ne peut pas être déposée au Portail Public de Facturation : toute sa ventilation de TVA relève de la catégorie O (hors champ de la TVA), que le portail rejette sur une facture électronique (règle G2.32). S'il s'agit de débours, marquez chaque ligne comme débours pour que la facture sorte de la réforme ; sinon, corrigez les catégories de TVA, puis redéposez-la. | deposit d'une vente déposée auprès du PPF (flux 1), à un acheteur qui n'est pas une entité publique, dont toutes les lignes de ventilation de TVA sont en catégorie O | marquez chaque ligne de débours avec disbursement, ou corrigez les catégories de TVA, puis redéposez |
Le message Le motif X n'est pas autorisé pour le statut ... interpole un libellé de statut qui n'existe pas pour revert_to_draft (000) : le texte contient alors une clé de traduction brute. Raison de plus pour ne jamais analyser le texte des messages.
Les refus de contrôle nomment leur cause
Certaines transitions sont déclarées depuis l'état courant mais restent bloquées par un contrôle métier. L'état n'est alors pas en cause, et le message ne dit pas Impossible de passer de ... : chacun de ces contrôles a son propre message. Trois visent toutes les factures, un ne vise que les ventes d'une société membre d'un assujetti unique, neuf ne visent que la voie B2C et un ne vise que le dépôt auprès du PPF.
deposit-Le PDF Factur-X joint n'est pas conforme. Remplacez-le par un fichier conforme avant de déposer la facture.Vous avez fourni le PDF de la facture et sa conformité Factur-X a été évaluée non conforme. Une conformité encore inconnue ne bloque pas le dépôt ; seul un verdict de non-conformité le fait. Ce verdict est aussi inscrit sur la facture comme erreur d'import bloquante, mais ce contrôle-ci est évalué avant celui des erreurs d'import : c'est donc ce message-ci que vous recevez.depositetmake_available-La facture comporte des erreurs d'import bloquantes. Corrigez-les avant de changer son statut.C'est le cas d'une facture créée sansinvoice_number,issue_date,type_codeoucurrency_code: Scribee remplit le champ manquant avec un placeholder et enregistre une erreur bloquante. UnPATCHsur le brouillon efface ces erreurs et débloque la transition.deposit-Des mentions légales obligatoires sont absentes de la facture (...). Renseignez-les dans les paramètres de facturation de l'entreprise, puis réessayez.Une des trois mentions légales obligatoires manque en note d'article. Avec le refus d'un Factur-X non conforme PDF/A-3 (voir plus bas), c'est l'un des deux refus de transition qui remplissentdetails; il est détaillé juste après.deposit-Cette facture a été créée alors que la société était membre d'un assujetti unique, mais elle ne le déclare pas en entier (SIREN du vendeur sous le schéma 0231, raison sociale, numéro de TVA et adresse de l'assujetti unique, note MEMBRE_ASSUJETTI_UNIQUE). Complétez l'assujetti unique dans les paramètres de la société, puis réenregistrez le brouillon avant de le déposer.La facture de vente a été créée par Scribee alors que la société déclarait un assujetti unique, mais elle ne porte pas les trois éléments de cette déclaration : le SIREN de l'assujetti unique sur le vendeur, la partietax_representativecomplète et la seule notetax_declarationMEMBRE_ASSUJETTI_UNIQUE. C'est le cas quand les paramètres de la société ne portaient que le SIREN à la création de la facture. Complétez-les dans l'interface Scribee, envoyez unPATCHsur le brouillon, puis redéposez (Émettre une facture de vente).
Sur deposit, ces quatre contrôles sont évalués dans cet ordre et le premier qui échoue donne le message : une facture qui cumule une erreur d'import bloquante et une mention légale manquante ne signale que l'erreur d'import, et il faut redéposer pour découvrir la suivante.
Neuf refus supplémentaires ne visent que les ventes d'une société déclarée dans la vague d'émission à un acheteur que la facture n'identifie pas : aucun SIREN, aucun legal_registration_id, aucun vat_identifier, ou aucune partie buyer du tout. Une telle vente n'est jamais transmise comme facture ; sauf si chaque ligne est marquée comme débours, elle est déclarée dans l'agrégat B2C quotidien (Déclarer des transactions), et son dépôt est donc contrôlé sur ce que cette déclaration exige. Hors vague, aucun de ces neuf refus ne peut l'atteindre - ni aucun contrôle Schematron, décrit plus bas. Les neuf messages commencent par Cette vente s'adresse à un client que la facture n'identifie pas : elle est déclarée en données de transaction et non transmise comme facture. puis nomment la cause ; ils figurent en entier dans le tableau ci-dessus et répondent code: "operation_failed" avec un details vide, comme les quatre contrôles précédents.
- Cadre de facturation absent - la catégorie de l'opération déclarée se déduit du seul cadre de facturation, et ni
invoicing_process_idni les lignes de la facture n'en fournissent un. Envoyezinvoicing_process_idsur la facture. - Cadre de facturation mixte -
goods_and_services_invoice(M1),paid_goods_and_services_invoice(M2) etgoods_and_services_final_invoice_after_retainer(M4) annoncent biens et services à la fois, que la déclaration compte séparément. Émettez les biens et les services sur des factures distinctes. - Vente au régime de la marge mêlée à d'autres ventilations - la déclaration compte une vente au régime de la marge dans une catégorie qui lui est propre. Une facture qui porte à la fois une ventilation de TVA de catégorie
Edont le motif d'exonération estVATEX-EU-F,VATEX-EU-I,VATEX-EU-JouVATEX-EU-D, et une autre ventilation de TVA - taxable,GouO-, est refusée, quelle que soit sa devise. Émettez les ventes au régime de la marge sur une facture distincte. - Vente au régime de la marge sans base de marge - la déclaration porte une telle vente sur sa marge hors TVA et la TVA due sur cette marge, deux montants que la facture ne porte pas. Une facture qui porte une ventilation de TVA de catégorie
Edont le motif d'exonération estVATEX-EU-F,VATEX-EU-I,VATEX-EU-JouVATEX-EU-Dest refusée, quelle que soit sa devise, faute de base de marge. Fournissez ces montants dansmargin_bases, par unPATCHqui ne porte que cette clé, puis redéposez la facture (Les ventes au régime de la marge). - Base de marge incohérente - les bases fournies sont déclarées telles quelles, et Scribee en contrôle la vraisemblance. Une vente au régime de la marge est refusée, quelle que soit sa devise, dès qu'une
margin_vat_amounts'écarte de plus d'un centime de samargin_base_amounttaxée à sonvat_rate, ou que les marges TVA comprise dépassent le prix de vente de la facture. Corrigez les bases par unPATCHqui ne porte quemargin_bases, puis redéposez la facture. - Totaux absents - la déclaration additionne les opérations de la journée : le total hors TVA et le total de TVA sont exigés tous les deux.
- Total de TVA hors euros - la déclaration est faite en euros. Une facture libellée dans une autre devise doit porter la contre-valeur en euros de son total de TVA.
- Ventilation de TVA absente - la déclaration porte toujours une ventilation par taux de TVA, y compris quand les montants sont nuls. Une facture qui n'en porte aucune ligne n'a rien à y inscrire : ajoutez-en au moins une, au taux 0 et pour un montant nul s'il s'agit d'un ticket exonéré.
- Ventilation et totaux discordants - la déclaration porte les totaux de la journée en regard de leur ventilation. Le total hors TVA et le total de TVA de la facture doivent donc égaler la somme de sa ventilation de TVA, exactement et au centime près, dans la devise de la facture.
Ces neuf refus sont évalués après les quatre contrôles ci-dessus, et dans l'ordre où ils sont listés : une vente qui cumule une erreur d'import bloquante et un cadre de facturation absent ne signale que l'erreur d'import.
Un dernier refus ne vise que les ventes d'une société déclarée dans la vague d'émission dont la facture est déposée auprès du PPF (flux 1) - un acheteur porteur d'un SIREN et établi dans le territoire de TVA français (Déclarer des transactions) : une facture dont toutes les lignes de ventilation de TVA sont en catégorie O (hors champ de la TVA). Le PPF rejette un flux 1 dont la ventilation de TVA est tout entière en O (règle G2.32) ; Scribee refuse donc le dépôt lui-même, après les quatre contrôles ci-dessus et avant tout contrôle Schematron. Le message, en entier dans le tableau ci-dessus, commence par Cette facture ne peut pas être déposée au Portail Public de Facturation et répond code: "operation_failed" avec un details vide. Une facture qui mêle une ventilation O à une ventilation taxable n'est pas concernée, pas plus qu'une facture dont chaque ligne est marquée comme débours : celle-ci sort de la réforme et n'est pas déposée auprès du PPF (Émettre une facture de vente). S'il s'agit de débours, marquez chaque ligne avec disbursement par un PATCH sur le brouillon ; sinon, corrigez les catégories de TVA de la facture ; puis redéposez-la. Ce refus ne vaut pas lorsque l'acheteur est une entité publique, que la règle G2.32 exclut : Scribee la reconnaît quand le SIREN de l'acheteur désigne, dans l'annuaire national, une unité légale de type public, en juge au dépôt et garde cette réponse pour toute la transmission de la facture. Une telle facture est déposée ; le contrôle du flux 1 ne lui oppose pas davantage G2.32 lorsque toute sa ventilation de TVA est en catégorie E avec un motif d'exonération de l'article 261 du CGI (VATEX-FR-CGI261-1, VATEX-FR-CGI261A...), ou qu'elle mêle des ventilations O et E.
Deux refus, en revanche, gardent bien le message générique Impossible de passer de ..., parce qu'aucun contrôle nommé n'est en cause :
make_availablesur une facture de senssalesqui n'est ni en200ni en202, un brouillon par exemple. Depuis200ou202, l'état permet la transition au statut de votre client, jamais à votre appel : vous obtenezLa transition make_available ne peut pas être déclenchée manuellement.revert_to_draftquand la facture n'est pas entrée dans Scribee par fichier, ou qu'elle est rattachée à un flux Peppol ou PPF.
Les mentions légales manquantes : details nomme les attributs à renseigner
Les trois mentions sont normalement héritées des réglages de disclaimer de l'entreprise lorsque vous omettez item_notes. Vous pouvez aussi les fournir vous-même comme notes d'article avec les codes payment_detail_remittance_information, payment_information et terms_of_payment. Envoyer votre propre tableau item_notes remplace intégralement les mentions héritées : un tableau explicite qui les omet bloque le dépôt. Le contrôle ne vise que les factures de vente dont Scribee est l'émetteur : une facture créée par POST /api/v1/workspaces/{workspace_id}/invoices en fait partie, une facture entrée par POST /api/v1/workspaces/{workspace_id}/invoices/upload ou par un autre canal externe non. Les factures d'achat ne sont jamais concernées.
Le refus liste les mentions manquantes dans le message, et les reprend champ par champ dans details, sous la clé de l'attribut à renseigner dans les paramètres de facturation de l'entreprise :
{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Des mentions légales obligatoires sont absentes de la facture (pénalités de retard, frais de recouvrement et escompte). Renseignez-les dans les paramètres de facturation de l'entreprise, puis réessayez.",
"details": {
"late_payment_disclaimer": ["Cette mention légale obligatoire est absente des paramètres de facturation de l'entreprise."],
"recovery_costs_disclaimer": ["Cette mention légale obligatoire est absente des paramètres de facturation de l'entreprise."],
"cash_discount_disclaimer": ["Cette mention légale obligatoire est absente des paramètres de facturation de l'entreprise."]
}
}
details ne porte que les mentions réellement manquantes : une seule clé quand une seule mention manque. Les trois clés possibles, dans l'ordre où elles apparaissent :
Clé de details | Mention citée dans le message |
|---|---|
late_payment_disclaimer | pénalités de retard |
recovery_costs_disclaimer | frais de recouvrement |
cash_discount_disclaimer | escompte |
Le message reprend les mêmes libellés dans le même ordre, entre parenthèses, séparés par des virgules et le dernier introduit par et.
Le dépôt demandé en un seul appel - POST /api/v1/workspaces/{workspace_id}/invoices avec lifecycle_state: "deposited" - est refusé par les mêmes contrôles, avec le même message et le même details. Une seule différence de forme : là où l'endpoint de transition envoie toujours details, quitte à ce qu'il soit vide, la création omet la clé quand elle n'a rien à y mettre. Traitez details comme optionnel des deux côtés.
Envoi de l'original depuis l'interface
Lors de la validation dans l'interface, si aucun envoi électronique au client n'est prévu, Scribee propose l'envoi de la facture originale par e-mail. Cela inclut un client B2B français sans adresse annuaire active : le dépôt porte alors le motif NON_TRANSMISE. L'e-mail sélectionné est préparé dès la validation réussie, sans attendre Peppol ni le PPF. Le flux PPF reste transmis lorsqu'il est dû. Une adresse e-mail client valide est nécessaire si l'option est cochée ; la décocher permet de valider sans cet envoi.
L'envoi utilise le fichier original disponible, sans filigrane de duplicata. Si une livraison électronique au client est prévue, l'option de duplicata conserve son fonctionnement. Les contrôles Schematron restent bloquants avant toute validation et tout envoi pour les ventes d'une société déclarée dans la vague d'émission, sauf pour une vente à un acheteur que la facture n'identifie pas : une telle vente, sauf si chaque ligne est marquée comme débours, ne produit aucun document à soumettre au Schematron, et ce sont les neuf refus de la voie B2C décrits plus haut qui gardent son dépôt. Hors vague, aucun contrôle Schematron ne garde le dépôt.
Le refus du contrôle Schematron : trois causes, trois codes
Le contrôle Schematron s'exerce au dépôt et peut le refuser pour trois raisons distinctes, sur lesquelles vous n'agissez pas de la même façon. Le champ code les sépare. Le statut est 422 dans les trois cas et le refus est le même sur tous les workspaces.
Ce contrôle ne vise que les ventes d'une société déclarée dans la vague d'émission. Avant de déposer une telle facture, Scribee contrôle le document UBL généré à partir de ses données actuelles, avec le numéro définitif, l'adresse destinataire et les montants de TVA retenus pour l'envoi. Si un flux 1 est dû, son extrait est également contrôlé avec les règles AIFE. Ce contrôle couvre les factures créées par API, saisies dans Scribee et importées depuis SAP, même sans fichier XML d'origine. Un ancien rapport d'export ne dispense pas de ce nouveau contrôle. Les contrôles du fichier importé restent applicables lorsqu'il existe.
:::warning Changement de comportement
Jusqu'ici, ce contrôle s'exerçait sur toute facture de vente, y compris celles d'une société hors vague d'émission - dont rien n'est pourtant transmis : ni flux 1, ni e-reporting, ni copie Peppol. Une facture refusée en 422 l'était alors au nom de règles qu'aucun envoi ne lui appliquait. Hors vague, le dépôt ne soumet plus aucun document au Schematron : aucun des trois codes ci-dessous ne peut plus être opposé à ces factures, et elles se déposent là où elles recevaient un 422. Si votre intégration s'appuyait sur ce refus pour détecter des données incomplètes, ce n'est plus lui qui vous le signalera.
:::
Dans la vague, ce que l'acheteur porte comme identifiants décide de ce qui est contrôlé, parce qu'il décide de ce qui est transmis.
Un acheteur que la facture n'identifie pas - aucun SIREN, aucun legal_registration_id et aucun vat_identifier, ou aucune partie buyer du tout - est déclaré dans l'agrégat B2C quotidien et jamais transmis comme facture, donc aucun document UBL n'est généré ni jugé pour lui : aucun des trois codes ci-dessous ne peut lui être opposé, et son dépôt est gardé par les neuf refus de la voie B2C décrits plus haut. Une vente dont chaque ligne est marquée comme débours, et qui sort ainsi de la réforme (Émettre une facture de vente), fait exception : elle n'est pas déclarée dans l'agrégat B2C, aucun des neuf refus de la voie B2C ne l'atteint, et son document UBL est généré et contrôlé comme celui de l'acheteur décrit ci-dessous, sans les deux règles d'adressage qui en sont retirées.
Un acheteur sans SIREN mais porteur d'un legal_registration_id ou d'un vat_identifier - un professionnel établi hors du territoire français de TVA -, comme un acheteur porteur d'un SIREN mais établi hors de ce territoire, relève lui aussi d'une déclaration d'opération plutôt que d'un dépôt de facture (Déclarer les transactions). Son document UBL est bien généré et contrôlé, et il l'est sur le jeu de règles complet : celui de son profil - EN 16931, ou les structures étendues EXTENDED-CTC-FR selon le customization_id porté par la facture - et le jeu BR-FR-Flux2, les règles de gestion propres à la France, qui restent applicables à une vente B2B internationale.
Deux règles exactement en sont retirées : BR-FR-12_BT-49, qui exige l'adresse électronique de l'acheteur, et BR-FR-13_BT-34, qui exige celle du vendeur. La réforme ne les impose que « dès lors que la facture électronique doit être transmise et attend des statuts de cycle de vie en retour » ; une vente déclarée en e-reporting ne transmet aucune facture et n'attend aucun statut, donc ces deux règles ne portent pas sur elle et ne sont opposées ni comme refus ni comme avertissement. Toutes les autres règles BR-FR-* continuent de juger le document : identifiants de facture BR-FR-01 et BR-FR-02, codes de type BR-FR-04, mentions légales BR-FR-05, familles BR-FR-BD-*, BR-FR-CO-*, BR-FR-DEC-* et BR-FR-MV-*. Une facture qui en viole une est refusée au dépôt, exactement comme une facture domestique.
L'extrait du flux 1 n'est pas contrôlé, puisqu'aucun flux 1 n'est dû. Une facture que seules ces deux règles d'adressage refusaient se dépose désormais ; une facture qui viole une règle de son profil - une règle de ventilation de TVA comme BR-E-10, par exemple - est refusée exactement comme avant.
En cas de refus, la facture existante reste en brouillon, aucun numéro définitif n'est consommé et les envois liés à la validation ne sont pas lancés. Corrigez les données signalées, puis redemandez le dépôt. Les avertissements seuls ne bloquent pas la validation. Lors d'une création avec lifecycle_state: "deposited", le refus annule aussi la création : aucune facture n'est enregistrée.
code | Cause | Ce que vous faites |
|---|---|---|
schematron_fatal | La facture porte une assertion Schematron fatale ; une assertion fatale n'est jamais un simple avertissement. | Refus déterministe : corrigez les champs que la réponse nomme, puis redéposez. |
schematron_engine_unavailable | Le moteur de validation devait rendre un verdict et n'a pas pu le produire. | Refus transitoire : rien n'indique que la facture est invalide, elle n'a pas été jugée. Redéposez le même document plus tard. |
schematron_profile_unsupported | Aucun jeu de règles admis par la réforme ne s'applique à ce profil. | Refus déterministe : redéposer ne changera rien, transmettez la facture sous un profil admis. |
Sur l'endpoint de transition, schematron_fatal répond dans une enveloppe spécifique : un objet errors de premier niveau, sans error, sans message et sans details. code s'y ajoute à côté d'errors, sans rien en retirer. Lors d'une création demandant le dépôt, le même refus conserve l'enveloppe error, message, code, et les champs fautifs sont dans details.
{
"errors": {
"document.payment_means[].payee_account.id": ["BR-61: [BR-61]-If the Payment means type code (BT-81) means SEPA credit transfer, Local credit transfer or Non-SEPA international credit transfer, the Payment account identifier (BT-84) shall be present."],
"document.payment_means[].type_code": ["BR-61: [BR-61]-If the Payment means type code (BT-81) means SEPA credit transfer, Local credit transfer or Non-SEPA international credit transfer, the Payment account identifier (BT-84) shall be present."]
},
"code": "schematron_fatal"
}
Les clés d'errors sont des chemins de champ, les valeurs des tableaux de messages au format IDENTIFIANT: message.
Une même assertion peut être listée sous plusieurs clés, comme BR-61 ci-dessus. Quand la règle porte sur un ensemble d'éléments - une même contrainte rejouée sur plusieurs parties, un calcul qui relie plusieurs montants, un test qui compare deux champs - elle apparaît sous chacun des champs que la règle met en cause, avec le même texte à chaque fois : c'est l'ensemble qui est en cause, et ne désigner qu'un seul de ses membres accuserait un champ conforme. Ne supposez donc pas une clé par assertion, et ne comptez pas les clés pour compter les assertions - dédupliquez sur l'identifiant en tête de message. La forme du corps ne change pas pour autant : errors reste un objet dont les clés sont des chemins de champ et les valeurs des tableaux de chaînes.
Les chemins reprennent les noms de champ de l'API, pas les codes BT. Un chemin désigne le champ tel que vous l'écrivez et tel que GET /api/v1/invoices/{id} vous le renvoie : la ventilation de TVA se lit donc document.tax_subtotals[].vat_amount, document.tax_subtotals[].tax_category_id ou document.tax_subtotals[].vat_rate, jamais BT-117 ni BT-118. Seul le message continue de citer les termes BT de la norme. Si votre intégration avait figé une correspondance entre identifiant d'assertion et chemin de champ, revérifiez-la : plusieurs chemins ont été corrigés, ceux de la ventilation de TVA en particulier.
Une assertion qu'aucun champ ne porte à lui seul arrive sous document._schematron. C'est le cas des règles qui contraignent la structure du document ou le format d'une donnée, et de celles qui portent sur un champ que l'API ne publie pas. Le message reste exploitable, il n'y a simplement aucun champ à pointer.
Les deux autres refus n'ont aucun champ à nommer - la facture n'a pas été jugée fautive, elle n'a pas été jugée du tout - et prennent l'enveloppe commune, details vide :
{
"error": "unprocessable_entity",
"code": "schematron_engine_unavailable",
"message": "La facture n'a pas pu être contrôlée : le moteur de validation est indisponible. Réessayez le dépôt dans quelques instants.",
"details": {}
}
{
"error": "unprocessable_entity",
"code": "schematron_profile_unsupported",
"message": "La facture n'a pas pu être déposée : aucun jeu de règles sémantiques ne s'applique à ce profil. Transmettez-la sous un profil admis par la réforme.",
"details": {}
}
Traitez donc les deux formes sur deposit : le corps porte soit errors et code, soit error/code/message/details. code est présent dans les deux, et c'est sur lui que vous branchez.
Les trois mêmes codes vous parviennent quand vous demandez le dépôt dès l'import - POST /api/v1/workspaces/{workspace_id}/invoices/upload avec lifecycle_state: "deposited", voir Importer des factures existantes - mais jamais sous la forme errors : cet endpoint garde son enveloppe commune et fait passer la carte des champs fautifs par son details, à la place de la clé file qu'il y met d'ordinaire. L'asymétrie est délibérée : chaque endpoint conserve la forme qu'il publiait déjà, l'un errors, l'autre details, et code s'ajoute aux deux sans rien retirer.
Le contrôle PDF/A-3 d'un Factur-X importé
Une facture de vente importée sous forme de Factur-X (source_format: "facturx", provided_pdf: false) est transmise octet pour octet. Au deposit, après le contrôle Schematron et avant le statut 200, Scribee contrôle la conformité PDF/A-3b (ISO 19005-3) de ce fichier avec veraPDF. Les factures d'achat et les factures que Scribee génère lui-même ne sont pas concernées.
Un fichier non conforme est refusé. La facture reste en brouillon, et details.file reprend le message, qui nomme les règles PDF/A en échec (trois au plus) et le fichier :
{
"error": "unprocessable_entity",
"code": "facturx_not_pdfa",
"message": "Factur-X non conforme PDF/A-3 (ISO 19005-3) : 6.8.1: The MIME type of an embedded file shall be specified using the Subtype key - FA-2026-0042.pdf. Corrigez le fichier puis déposez-le à nouveau.",
"details": {
"file": ["Factur-X non conforme PDF/A-3 (ISO 19005-3) : 6.8.1: The MIME type of an embedded file shall be specified using the Subtype key - FA-2026-0042.pdf. Corrigez le fichier puis déposez-le à nouveau."]
}
}
Le code facturx_not_pdfa signale un verdict définitif pour ce fichier : ne rejouez pas l'appel. Le verdict est enregistré sur la facture, dont facturx_conformance passe à non_compliant, deposit disparaît de lifecycle_available_transitions, et une nouvelle demande de dépôt renvoie le même message et le même code. Corrigez le fichier, supprimez le brouillon, puis importez le fichier corrigé (Importer des factures existantes, qui détaille aussi la cause la plus fréquente et la façon de l'éviter).
Si veraPDF ne peut pas rendre de verdict, le dépôt est refusé avec le code facturx_validation_unavailable et un details vide. Le fichier n'a pas été jugé et rien n'est enregistré, facturx_conformance compris : redéposez la même facture plus tard.
{
"error": "unprocessable_entity",
"code": "facturx_validation_unavailable",
"message": "La conformité PDF/A-3 (ISO 19005-3) du Factur-X n'a pas pu être contrôlée : le service de validation est indisponible. Réessayez le dépôt dans quelques instants.",
"details": {}
}
Branchez-vous sur code : facturx_not_pdfa appelle un fichier corrigé, facturx_validation_unavailable le même appel rejoué plus tard. Un dépôt accepté porte facturx_conformance: "compliant" ; avant tout contrôle, ce champ vaut null.
Les autres statuts HTTP
401 (token absent ou expiré) et 403 (scope write manquant) suivent le format commun décrit dans Conventions de l'API.
404 couvre plus de cas que la facture inconnue :
- la facture appartient à un workspace auquel votre application n'a pas accès ;
- votre adresse IP est refusée par la liste d'autorisation du workspace. La réponse est bien
404, pas le403documenté ailleurs : les workspaces refusés sont écartés avant la recherche de la facture ; - la facture est de sens
saleset l'offre de l'entreprise ne couvre pas la vente ; - la facture a été retirée du workspace après avoir été créée. Un brouillon peut être retiré depuis l'interface Scribee : il sort alors de
GET /api/v1/workspaces/{workspace_id}/invoiceset son identifiant cesse d'être résolu.
Ce retrait vaut pour toute opération qui désigne la facture par son identifiant, sans exception : GET /api/v1/invoices/{id}, PATCH /api/v1/invoices/{id}, PATCH /api/v1/invoices/{id}/transition, DELETE /api/v1/invoices/{id}, POST /api/v1/invoices/{id}/send_by_email, GET /api/v1/invoices/{id}/email_deliveries et GET /api/v1/invoices/{id}/download. Il vaut aussi pour les collections rattachées à la facture, qui se résolvent par ce même identifiant : les paiements (/api/v1/invoices/{invoice_id}/payments) et les pièces jointes (/api/v1/invoices/{invoice_id}/supporting_documents) partent avec leur facture, en lecture comme en écriture. Toutes répondent 404 là où elles répondaient 200 avant le retrait. Aucun champ de réponse ne signale ce retrait, et aucun n'a été ajouté pour lui : le contrat v1 est inchangé. Il n'existe pas non plus d'endpoint qui liste les factures retirées ou qui les rétablisse.
Traitez une facture retirée comme une facture supprimée par DELETE /api/v1/invoices/{id}, qui produit déjà le même 404 : votre intégration gère donc déjà le cas d'une facture qu'elle connaissait et qui devient introuvable. Retirez-la de votre référentiel local et cessez de l'interroger. Un 404 n'est pas une erreur passagère : rejouer l'appel ne changera rien.
Pages liées
- Émettre une facture de vente - créer, déposer et suivre vos factures de vente
- Recevoir les factures fournisseurs - dérouler le cycle de vie côté acheteur
- Enregistrer les paiements - les paiements qui déclenchent
collectetuncollect - Référence API : déclencher une transition
- Référence API : consulter une facture