Importer des factures existantes
Vos factures n'existent pas toutes dans Scribee : factures fournisseurs reçues hors des réseaux d'échange, factures émises par un autre logiciel, historique à reprendre. POST /api/v1/workspaces/{workspace_id}/invoices/upload les fait entrer dans votre workspace en un appel : vous envoyez le fichier, Scribee détecte le format, lit les données et crée un brouillon prêt à entrer dans le cycle de vie réglementaire. Le scope write est requis (Authentification).
Cet endpoint accepte deux familles de fichiers, traitées différemment. Un fichier structuré - un XML UBL ou CII, ou un PDF qui embarque ce XML (Factur-X) - est lu pendant l'appel et la facture est retournée en 201. Un PDF sans XML de facture reconnu et une image (jpg, jpeg, png, heic) partent en extraction assistée par IA : l'appel répond 202 sans facture, et la facture est créée en tâche de fond. Ce second cas dépend de l'offre du workspace : les offres qui n'autorisent que le dépôt structuré le refusent en 422.
Ce que Scribee fait pour vous
- Détection du format sur le contenu : le fichier est classé d'après ses octets et son XML embarqué - UBL 2.1, CII (EN16931) ou Factur-X (PDF avec XML CII embarqué) - jamais d'après son extension ou son type MIME déclaré, qui ne servent que de repli. Dans un PDF, la pièce jointe qui porte le XML est reconnue par son nom quel que soit l'encodage de chaîne texte employé pour l'écrire (UTF-16, UTF-8, PDFDocEncoding) : le producteur de votre Factur-X n'a rien à respecter de plus que la norme. Un XML annexe comme
index.xmlne rend pas le PDF structuré : avec un nom non standard, le XML doit porter une racineInvoice,CreditNoteouCrossIndustryInvoice. La recherche continue parmi les autres pièces jointes avant de classer le PDF comme ordinaire. Les noms de facture standard restent prioritaires et leur contenu reste soumis à la validation structurée, même s'il est invalide. - Analyse structurée : en-tête, vendeur et acheteur, lignes, taxes et moyens de paiement sont lus depuis le XML, sans mapping de votre côté.
- Remises portant deux fois leur sens : EN 16931 porte le sens d'un groupe remise/frais dans le seul indicateur (BT-91,
cbc:ChargeIndicatoren UBL,ram:ChargeIndicatoren CII) et garde le montant (BT-92, BT-136) non négatif. Certains émetteurs énoncent le sens deux fois, en envoyant une remise dont le montant est négatif. Scribee lit ce montant comme une grandeur et importe la facture, au lieu de la refuser sur une double négation. Un frais au montant négatif, lui, se contredit et reste refusé : il diminuerait ce que vous facturez alors que son indicateur dit l'augmenter. Le montant conservé dans Scribee, et celui que Scribee remet sur le fil quand il régénère le fichier, est donc toujours non négatif - un montant nul reste un montant valide et traverse l'import inchangé. - Représentation lisible de l'émetteur : quand le fichier structuré porte un PDF (BT-125-1
application/pdf) en pièce jointe BG-24 dont la description (BT-123) vautLISIBLE, quelle que soit la casse, ce PDF devient la couche visible de la facture au lieu du rendu que Scribee produirait, etprovided_pdfvaut alorstrue. La norme AFNOR XP Z12-012 réserve ce marqueur à la représentation lisible complète de la facture. Par tolérance envers les émetteurs qui ne le posent pas, un PDF dont le nom de fichier est exactementlisible.pdf, quelle que soit la casse, est reconnu de la même façon ; un nom qui ne fait que le contenir, commefacture_lisible.pdf, ne suffit pas. Quand une pièce marquéeLISIBLEet une pièce nomméelisible.pdfcoexistent, la pièce marquéeLISIBLEl'emporte. Le PDF retenu n'est pas enregistré parmi les pièces jointes de la facture. Hors de ces deux cas, une pièce jointe portant une autre description (RIB,BON_LIVRAISON, ...) reste un justificatif et ne remplace jamais le rendu. Un lisible que Scribee ne peut pas traiter - fichier chiffré, corrompu, au-delà de 50 Mo - est ignoré sans faire échouer l'import : la facture est créée et repart sur le rendu Scribee. - Sens du flux : Scribee compare les identifiants de votre entreprise (SIREN, numéro de TVA) aux parties du fichier - votre entreprise en acheteur donne une facture d'achat, en vendeur une facture de vente. Sans correspondance, le sens reste celui du champ
directionde l'appel,purchasespar défaut. - Rapprochement des tiers : le vendeur et l'acheteur sont rapprochés de vos fournisseurs et clients existants (voir plus bas).
- Conservation du fichier d'origine : le fichier importé est attaché à la facture tant qu'il pèse moins de 50 Mo et qu'il est bien un PDF ou un XML. Le type MIME que votre client HTTP déclare dans la partie
filen'a pas besoin d'être exact : un type non reconnu -application/octet-streamnotamment - est remplacé par celui déduit du contenu déjà analysé, et le fichier est conservé quand même. S'il reste refusé (au-delà de 50 Mo, ou un contenu qui n'est ni PDF ni XML), l'appel répond201et la facture est créée sans son fichier d'origine.upload_sourcevautapisur toute facture créée par cet endpoint : la provenance est écrite dans la même transaction que la facture, et un refus de cette écriture annule l'import au lieu de le laisser passer. L'API vous restitue ce fichier dans un cas : le téléchargement au formatfacturxd'une facture déposée en Factur-X renvoie les octets déposés eux-mêmes (Formats et téléchargements). Partout ailleurs, le téléchargement ne sert que les formats produits par Scribee (pdf,ubl,cii,facturx). - Pièces jointes du PDF Factur-X : les fichiers complémentaires embarqués dans le PDF sont enregistrés comme pièces jointes de la facture, sous réserve des formats acceptés. Le XML utilisé pour lire la facture est exclu. Ces pièces ont
kind: otheretattachable_to_invoice: false. Le PDF original reste inchangé. L'extraction accepte au plus 100 fichiers embarqués et moins de 50 Mo de données décompressées au total, XML compris. Un fichier refusé ou une limite atteinte produit un avertissement d'import. La limite des ajouts manuels reste de 10 Mo par fichier.
Le parcours d'un fichier importé
Importer un fichier structuré
Cet appel crée une facture en brouillon dans votre workspace ; il ne transmet rien au PPF (Portail Public de Facturation), au réseau Peppol ni à aucun destinataire. Le brouillon reste en l'état tant que vous ne déclenchez pas de transition de cycle de vie. Il n'est en revanche pas supprimable par l'API : une facture importée porte le numéro du fichier, et seuls les brouillons sans numéro définitif peuvent être supprimés. Une correction passe par la mise à jour du brouillon (PATCH /api/v1/invoices/{id}, référence API).
Envoyez le fichier en multipart/form-data, champ file :
curl -X POST https://app.scribee.tech/api/v1/workspaces/YOUR_WORKSPACE_ID/invoices/upload \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-F "file=@facture-fournisseur.xml" \
-F "company_id=317" \
-F "direction=purchases"
Trois champs facultatifs accompagnent file :
company_id: l'entreprise du workspace qui reçoit la facture. Sans lui, la première entreprise du workspace est retenue - dans un workspace multi-entreprises, c'est le seul moyen de choisir laquelle. Un identifiant inconnu renvoie404.direction:salesoupurchases,purchasespar défaut. Sur cet endpoint de dépôt de fichier, la valeur n'est pas validée : elle est écrasée quand les parties du fichier structuré identifient votre entreprise, et l'import aboutit alors normalement même si vous aviez envoyé n'importe quoi. Quand les parties ne l'identifient pas, la valeur envoyée est conservée telle quelle et l'import échoue plus loin, sans message ciblantdirection. N'envoyez quesalesoupurchases. (La création JSONPOST /api/v1/workspaces/{workspace_id}/invoices, elle, valide bien ce champ et répond422.)lifecycle_state: fait franchir une transition de cycle de vie à la facture dans le même appel, à la place d'unPATCH /api/v1/invoices/{id}/transitionséparé. Valeurs acceptées :draft(défaut, aucune transition),deposited(dépôt, sur une facture de vente) ouavailable(mise à disposition, sur une facture d'achat) - toute autre valeur est refusée en422. Sur un fichier qui part en extraction IA, seuldraftpasse sans refus :deposited,availableet toute valeur hors liste sont refusés (voir plus bas).
Un fichier structuré (UBL, CII, Factur-X) est traité pendant l'appel et la facture créée est retournée (statut 201). Réponse abrégée aux champs utiles ici :
{
"data": {
"id": 12456,
"invoice_number": "FAC-2026-0183",
"issue_date": "2026-07-10",
"type_code": "invoice",
"currency_code": "EUR",
"direction": "purchases",
"tax_inclusive_amount": 1770.0,
"lifecycle_state": "draft",
"lifecycle_status_code": "000",
"lifecycle_available_transitions": ["make_available"],
"source_format": "ubl",
"upload_source": "api"
}
}
source_format vaut ubl, cii, facturx ou api : pour un document reçu, il porte le porteur, et non la syntaxe qu'il contient. La valeur api est à part : elle désigne une facture créée par l'API sans dépôt de fichier structuré, donc sans porteur à décrire. Un PDF Factur-X est renvoyé facturx. Un XML CII reste cii même lorsqu'il porte une guideline Factur-X, parce qu'il est un XML et non un PDF/A-3.
:::warning Changement de comportement
Jusqu'ici un PDF Factur-X était renvoyé cii et la valeur facturx n'apparaissait jamais. Si votre intégration teste source_format == "cii" pour reconnaître un dépôt Factur-X, elle doit désormais accepter facturx.
:::
lifecycle_available_transitions liste les transitions que vous pouvez déclencher : make_available sur une facture d'achat (statut 203, mise à disposition), deposit sur une facture de vente (statut 200, dépôt). Le détail des états et des codes est dans Cycle de vie des factures.
lifecycle_state : la cause réelle d'un refus est dans details.file, pas dans message
Passer lifecycle_state fait franchir la transition demandée dans le même appel que l'import, sous les mêmes règles que l'endpoint de transition dédié (PATCH /api/v1/invoices/{id}/transition, Cycle de vie des factures). C'est tout ou rien : si la transition est refusée, rien n'est créé, y compris la facture.
Trois causes de refus renvoient toutes la même enveloppe 422 que les autres échecs de traitement de fichier : message reste le générique Échec du traitement du fichier de facture, et c'est details.file[0] qui porte la cause réelle. Un partenaire qui ne journalise que message ne verra rien d'exploitable. La troisième, propre à deposited : cette transition n'est déclenchable manuellement que sur une facture de vente, et direction vaut purchases par défaut sur cet endpoint (voir plus haut) - si le fichier ne fait pas basculer le sens vers la vente, la transition est refusée avec details.file[0] : La transition deposit ne peut pas être déclenchée manuellement., un message qui ne mentionne pas direction. Un dépôt refusé par le contrôle Schematron fait exception à cette forme quand il nomme des champs : details est alors indexé par chemin de champ plutôt que par file (voir plus bas).
Demander deposited ou available sur un fichier qui part en extraction IA (PDF sans XML de facture reconnu, image) est refusé - draft reste accepté (voir plus haut) :
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "Échec du traitement du fichier de facture",
"details": {
"file": ["lifecycle_state ne peut être demandé que pour une facture électronique structurée (Factur-X, UBL, CII). Ce fichier nécessite une extraction assistée : rien n'a été créé. Déposez-le sans lifecycle_state, puis utilisez l'endpoint de transition."]
}
}
Une valeur hors de draft, deposited ou available :
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "Échec du traitement du fichier de facture",
"details": {
"file": ["Valeur lifecycle_state invalide à la création : 'approved'. Valeurs autorisées : draft, deposited, available"]
}
}
Un fichier non structuré répond 202, pas 201
Un PDF sans XML de facture reconnu et une image sont acceptés quand l'offre du workspace autorise le dépôt non structuré (sinon, 422 : voir plus bas). Le fichier est alors confié à une extraction assistée par IA, en tâche de fond. La réponse n'est pas la facture, et son corps n'a rien de commun avec celui du 201 :
{
"data": {
"status": "processing",
"upload_file_id": 4821
}
}
Aucune facture n'existe encore à ce moment. Elle est créée plus tard, quand l'extraction aboutit, et c'est à ce moment-là que l'événement invoice.created part vers vos endpoints webhook. Aucun endpoint de l'API ne permet de suivre upload_file_id : attendez le webhook, ou relisez la liste des factures du workspace. L'extraction peut aussi échouer, auquel cas aucune facture n'est créée et aucun événement n'est émis.
L'extraction peut enfin aboutir sans produire de facture visible. Quand le tri automatique des documents est activé pour le workspace, un fichier que l'extraction reconnaît comme autre chose qu'une facture à payer est classé hors de vos factures : bon de livraison, devis, bon de commande, relevé bancaire, document d'expédition ou de douane - y compris une facture qui porte la mention for customs purposes only, no commercial value ou billing invoice will be sent separately -, et facture dont le numéro lu ne figure nulle part dans le texte du document. Ce document n'apparaît ni dans GET /api/v1/workspaces/{workspace_id}/invoices ni par GET /api/v1/invoices/{id}, et aucun événement invoice.created n'est émis. S'il est restauré depuis l'interface Scribee, il devient une facture visible et invoice.created part à ce moment-là ; écarté pour un numéro introuvable, il revient avec un numéro provisoire DRAFT- à remplacer par PATCH avant le dépôt.
Traitez donc les deux statuts distinctement : un 201 porte la facture, un 202 ne porte qu'un accusé de prise en charge.
:::warning Changement de comportement
Un PDF Factur-X conforme dont le nom de la pièce jointe XML est écrit en UTF-16 n'était pas reconnu comme structuré : il partait en extraction assistée et répondait 202, ou était refusé en 422 sur une offre réservée au dépôt structuré. Ces fichiers sont désormais lus pendant l'appel et la facture est retournée en 201. Si votre intégration attend un 202 pour ces dépôts-là, elle doit accepter un 201 portant la facture.
:::
Le rapprochement des tiers
Le vendeur d'une facture d'achat est comparé à vos fournisseurs, l'acheteur d'une facture de vente à vos clients, dans cet ordre - le premier critère concluant l'emporte :
- Identifiant légal et son schéma (SIREN, SIRET) - le critère le plus précis.
- Croisement SIREN : un SIRET du fichier est rapproché du SIREN correspondant et inversement, y compris via un numéro de TVA français.
- Numéro de TVA intracommunautaire.
- Nom exact (insensible à la casse), comparé au nom et au nom commercial du tiers.
Un fournisseur que la synchronisation SAP Business One de l'entreprise signale comme gelé n'est jamais candidat, quel que soit le critère, alors qu'il reste listé parmi vos fournisseurs.
Les critères 2 et 4 s'abstiennent quand deux tiers ou plus correspondent : le rapprochement n'a pas lieu, plutôt que d'être fait à tort. Les critères 1 et 3 ne font pas ce contrôle : quand deux tiers portent le même identifiant légal, ou le même numéro de TVA, l'un des deux est retenu et vous ne pouvez pas prévoir lequel. Tenez vos identifiants uniques dans votre annuaire.
Le résultat est lisible dans la réponse : seller.party_id et buyer.party_id portent l'identifiant du fournisseur ou du client rapproché, et valent null quand aucun rapprochement n'a eu lieu. Un tiers non rapproché ne bloque rien et n'ajoute rien à la facture ; le rapprochement se fait ensuite depuis l'interface Scribee.
Le vendeur d'une facture d'achat peut aussi être rapproché plus tard, en tâche de fond, quand la gestion des commandes d'achat est activée pour l'entreprise. Tant que la facture est en draft ou available, si les commandes d'achat que cite la facture - par son purchase_order_reference, par l'order_line_reference de ses lignes, ou par son numéro repris sur une réception - appartiennent toutes à un seul fournisseur, ce fournisseur devient son vendeur et seller.party_id prend son identifiant. Un vendeur rapproché par l'un des quatre critères ci-dessus, ou choisi par un utilisateur, n'est jamais remplacé. Un PATCH /api/v1/invoices/{id} qui modifie purchase_order_reference, l'order_line_reference d'une ligne ou invoice_number relance cette recherche.
L'identifiant du lieu de livraison (BT-71)
L'identifiant du lieu de livraison n'est repris que lorsque son schéma d'identification (BT-71-1) accompagne la valeur dans le fichier - cac:DeliveryLocation/cbc:ID/@schemeID en UBL, ram:ShipToTradeParty/ram:GlobalID/@schemeID en CII. Un identifiant sans schéma est ignoré, et delivery.location_id comme delivery.location_scheme_id valent alors null : la règle BR-FR-CO-10_BT-71-1 rend le schéma obligatoire dès que l'identifiant est déclaré, donc un identifiant non qualifié ne pourrait jamais être ré-émis. Le nom du destinataire (BT-70) n'est pas concerné et reste repris dans delivery.location_name.
Commande, avis d'expédition et livraison à la ligne
Chaque ligne d'un fichier UBL ou CII importé reprend son numéro de commande (EXT-FR-FE-135), son avis d'expédition (EXT-FR-FE-140, EXT-FR-FE-141 et EXT-FR-FE-201) et sa livraison (EXT-FR-FE-BG-10), lus aux emplacements décrits dans Émettre une facture de vente. Relisez-les avec GET /api/v1/invoices/{id}?include=lines, dans purchase_order_reference, despatch_advice et delivery sur la ligne.
Deux restrictions s'appliquent à la livraison de la ligne :
- l'identifiant du lieu n'est repris qu'avec son schéma, comme celui de l'en-tête. En CII, où une ligne peut en déclarer plusieurs, seul le premier
ram:GlobalIDqui porte unschemeIDest retenu ; - l'adresse n'est reprise que si elle porte son pays -
ram:PostalTradeAddress/ram:CountryIDen CII,cac:Address/cac:Country/cbc:IdentificationCodeen UBL. Sans pays,delivery.addressvautnull. Le nom du lieu reste repris dansdelivery.location_name.
Les références aux factures antérieures
Les références d'un fichier structuré aux factures antérieures sont reprises avec leur type. Au niveau de la facture (BG-3), chaque entrée de invoice_references renvoie le numéro, la date et le type de la facture citée (EXT-FR-FE-02), et document_id y vaut null. Au niveau d'une ligne (EXT-FR-FE-BG-06), par exemple la ligne d'une facture définitive qui reprend un acompte, le numéro, la date, le type et la ligne de la facture citée reviennent dans l'objet invoice_reference de la ligne. Le type est renvoyé sous sa clé Scribee, retainer_invoice pour une facture d'acompte (386) ; la liste des clés figure dans Émettre une facture de vente.
Identifiants externes (external_source, external_id)
Ces deux champs sont ignorés en silence par l'endpoint d'import : l'appel répond 201 sans erreur, mais la facture ne les porte pas. Envoyez-les via la mise à jour du brouillon (PATCH /api/v1/invoices/{id}, référence API) ou dès la création JSON (POST /api/v1/workspaces/{workspace_id}/invoices) pour lier la facture importée à l'enregistrement correspondant dans votre système - un ERP, par exemple.
Les deux champs vont ensemble : envoyer l'un sans l'autre échoue en 422, aussi bien à la création qu'à la mise à jour.
{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "external_source et external_id doivent être fournis tous les deux, ou omis tous les deux"
}
external_id est unique par (entreprise, external_source) et limité à 255 caractères, comme external_source. external_source ne peut pas prendre une valeur réservée à l'une des intégrations propres à Scribee : ses intégrations comptables (l'import SAP Business One, entre autres) et la réception des factures venues de Chorus Pro, qui réserve la valeur chorus. Un tel envoi échoue en 422, aussi bien à la création qu'à la mise à jour :
{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "'sap_business_one' est une valeur external_source réservée à l'une des intégrations de Scribee et ne peut pas être définie via l'API"
}
Une fois qu'une facture porte un external_source réservé - parce qu'elle a été importée par l'une de ces intégrations - ni external_source ni external_id ne peuvent plus être modifiés par l'API, même vers d'autres valeurs :
{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "external_source et external_id ne peuvent pas être modifiés sur un document importé par une intégration automatisée"
}
Ces trois refus n'ont pas la forme des erreurs de traitement de fichier ci-dessus : le corps ne porte pas de clé details, seulement error, code et message.
Ce qui se passe ensuite
- L'événement
invoice.createdest délivré à vos endpoints webhook (Webhooks). - Les formats de téléchargement de la facture (
pdf,ubl,cii,facturx) sont générés, sauf erreur d'import bloquante : voir Formats et téléchargements. - Une validation EN16931 (règles Schematron) est lancée en tâche de fond sur le XML. Ni son rapport ni ses constats ne figurent dans le payload de la facture ; ils ne se manifestent qu'au dépôt (voir plus bas).
- Rien ne part vers l'extérieur : la transmission au destinataire et le cycle de vie réglementaire ne commencent qu'à la transition que vous déclenchez.
Erreurs et cas limites
La plupart des échecs de traitement du fichier renvoient un statut 422 de la même forme : error vaut unprocessable_entity, message vaut Échec du traitement du fichier de facture et details.file porte la cause précise. Un company_id inconnu sort de cette forme et renvoie 404. Un direction invalide que l'inférence n'a pas corrigé en produit deux, selon ce que le fichier permet de décider :
- Si les parties structurées identifient une entreprise mais pas la vôtre, le contrôle d'appartenance échoue en premier et vous recevez l'enveloppe de traitement de fichier ci-dessus, cause dans
details.file. - Si ce contrôle ne peut pas se prononcer - aucun identifiant de partie exploitable, par exemple - la valeur invalide atteint l'enregistrement et vous recevez l'enveloppe de validation générique (
messagevautLa validation a échoué,detailsest indexé par champ, icidetails.direction).
Quand le fichier porte des parties structurées qui identifient bien votre entreprise, l'inférence l'emporte et la valeur invalide est remplacée sans erreur - voir plus haut. Les erreurs communes à tous les endpoints (401, 403, 404) suivent le format décrit dans Conventions de l'API.
422 : fichier illisible ou format inconnu
Un XML qui n'est ni UBL ni CII et un XML invalide sont refusés :
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "Échec du traitement du fichier de facture",
"details": {
"file": ["Contenu XML invalide"]
}
}
Selon la cause, details.file contient Un fichier est requis, Format de fichier non reconnu (XML, PDF ou image attendu), Format de facture inconnu : <identifiant>, Contenu XML invalide ou Le fichier PDF est malformé : <détail>. Ce dernier message est réservé au PDF qu'aucun lecteur ne parvient à ouvrir : un PDF techniquement malformé mais qui reste lisible - longueur de flux déclarée qui ne correspond pas aux octets réels, table de références croisées introuvable, objet d'un type inattendu - est accepté et, faute de XML de facture reconnu, part en extraction IA (202, voir plus haut) comme n'importe quel PDF sans XML de facture reconnu. Pour les autres causes, corrigez le fichier ou réexportez-le depuis le logiciel d'origine avant de rejouer l'appel.
422 : l'offre n'autorise que le dépôt structuré
Un PDF sans XML de facture reconnu et une image (jpg, jpeg, png, heic) partent normalement en extraction IA (202, voir plus haut). Certaines offres réservent l'endpoint aux factures structurées : le fichier est alors refusé, et aucune facture n'est créée.
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "Échec du traitement du fichier de facture",
"details": {
"file": ["Votre offre n'autorise que le dépôt de factures électroniques structurées (Factur-X, UBL, CII). Fichier(s) rejeté(s) : facture-scan.pdf"]
}
}
Déposez le fichier au format Factur-X, UBL ou CII.
422 : votre entreprise n'est pas partie à la facture
Quand le fichier porte des identifiants et que l'entreprise visée n'y figure ni comme vendeur ni comme acheteur :
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "Échec du traitement du fichier de facture",
"details": {
"file": ["L'entreprise sélectionnée (843295671) n'apparaît ni comme vendeur (152836408) ni comme acheteur (503241636) dans ce fichier."]
}
}
Vérifiez le company_id que vous envoyez - sans lui, la première entreprise du workspace est retenue - et que le SIREN ou le numéro de TVA de cette entreprise correspond bien à l'une des parties du fichier. Le contrôle est sauté quand votre entreprise ne porte ni identifiant légal ni numéro de TVA, et quand l'une des parties nommées par le fichier n'en porte aucun : une partie désignée par son seul nom peut parfaitement être votre entreprise, et la refuser sur ce silence rejetterait un achat valable - le cas d'un fournisseur étranger dont le bloc acheteur ne porte pas de SIRET. Le refus ci-dessus ne tombe donc que si toutes les parties nommées par le fichier portent un identifiant et qu'aucune ne correspond à votre entreprise.
422 : numéro de facture déjà importé
Le numéro de facture est unique par entreprise. Rejouer l'import du même fichier renvoie :
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "Échec du traitement du fichier de facture",
"details": {
"file": ["Une facture avec le numéro 'FAC-2026-0183' existe déjà pour cette entreprise."]
}
}
Ce refus vous protège des doublons tant que le fichier porte un numéro. Un fichier sans numéro de facture reçoit un numéro provisoire tiré au hasard : rejouer cet import-là crée bien un second brouillon.
422 : votre offre n'inclut pas la facturation de vente
Un fichier dont votre entreprise est le vendeur crée une facture de vente ; certaines offres ne l'incluent pas. details.file contient alors : Votre offre n'inclut pas la facturation de vente. Contactez votre cabinet comptable pour mettre à niveau votre offre.
Les erreurs d'import ne sont pas des erreurs HTTP
L'import réussit (statut 201) même quand un champ obligatoire manque : le numéro de facture, la date d'émission, le type ou la devise absents reçoivent une valeur provisoire et une erreur d'import est enregistrée sur la facture. L'API n'expose pas ces erreurs ; le signal observable est lifecycle_available_transitions, dont deposit et make_available disparaissent tant qu'une erreur bloquante est présente. Mettez le brouillon à jour avec les données correctes (PATCH /api/v1/invoices/{id}) : la mise à jour efface les erreurs d'import et la transition redevient disponible. Elle purge aussi les quatre formats de téléchargement et les régénère en tâche de fond. Un téléchargement demandé juste après un PATCH n'échoue pas pour autant : le format absent est régénéré pendant la requête. Il peut en revanche échouer pour une raison de fond - des données encore incomplètes, par exemple. Ce ne peut pas être à cause d'un PDF que vous aviez fourni : la mise à jour repasse provided_pdf à false et purge le PDF d'origine, si bien que la régénération part du rendu Scribee.
Un code pays hors ISO 3166-1 alpha-2 n'est pas stocké
EN 16931 type le code pays (BT-40, BT-55, BT-69) en ISO 3166-1 alpha-2 : deux lettres, FR et non FRA. Une valeur qui n'en est pas une - un alpha-3, un nom de pays, une chaîne libre - n'est pas stockée : le champ revient à null et l'appel réussit, au lieu du 422 que vous receviez auparavant.
Ce n'est pas un assouplissement de la règle, c'est un changement de juge. L'obligation est portée par les constats EN16931 BR-09 et BR-11, tous deux fatals, qui se manifestent au dépôt comme décrit juste au-dessus - avec le chemin du champ dans details. Auparavant la même règle était aussi appliquée à l'écriture, et un code pays illisible faisait échouer la facture entière plutôt que le seul champ ; sur les imports assistés par IA, la contrepartie elle-même n'était alors pas enregistrée.
Corrigez la valeur avec PATCH /api/v1/invoices/{id} avant de déposer.
Les constats Schematron se manifestent au dépôt
Un constat de la validation EN16931 n'est pas une erreur d'import : il ne retire pas deposit de lifecycle_available_transitions, là où une erreur d'import l'en retire. La transition reste listée et appelable, et le contrôle s'exerce au moment où vous l'appelez. Ne guettez donc pas les constats dans la liste des transitions, traitez le 422. La facture reste dans son état d'origine.
Trois causes distinctes refusent ce dépôt, et le champ code vous dit laquelle : schematron_fatal (une assertion fatale - corrigez les champs que la réponse nomme, puis redéposez), schematron_engine_unavailable (le verdict n'a pas pu être produit - la facture n'est pas en cause, redéposez-la plus tard) et schematron_profile_unsupported (aucun jeu de règles ne s'applique à ce profil - redéposer ne changera rien, transmettez un autre profil). Le détail des trois enveloppes est dans Cycle de vie des factures.
Sur l'endpoint de transition, schematron_fatal renvoie un objet errors de premier niveau accompagné de code, sans error ni message. Demandé dès l'import avec lifecycle_state: "deposited", le même refus garde l'enveloppe de traitement de fichier de cet endpoint, et c'est details qui porte les champs fautifs, à la place de sa clé file habituelle :
{
"error": "unprocessable_entity",
"code": "schematron_fatal",
"message": "Échec du traitement du fichier de facture",
"details": {
"document.lines[].price.unit_price": ["BR-27: [BR-27]-The Item net price (BT-146) shall NOT be negative."]
}
}
Les clés de ce details obéissent aux mêmes règles que celles d'errors sur l'endpoint de transition : ce sont les noms de champ de l'API et non les codes BT, une même assertion peut y être listée sous plusieurs champs quand la règle porte sur un ensemble d'éléments, et celle qu'aucun champ ne porte à lui seul arrive sous document._schematron. Le détail est dans Cycle de vie des factures.
Les deux autres codes gardent le details.file habituel, dont le premier élément porte la cause. Dans tous les cas, corrigez le document ou attendez, puis redéposez.
422 au dépôt : Factur-X non conforme PDF/A-3
Une facture de vente importée sous forme de Factur-X (source_format: "facturx", provided_pdf: false) est transmise telle quelle, octet pour octet. Avant de la déposer - avant le statut 200, avant toute transmission -, Scribee contrôle donc la conformité PDF/A-3b (ISO 19005-3) du fichier importé avec veraPDF. Le contrôle s'exerce au dépôt, que vous le demandiez dès l'import (lifecycle_state: "deposited") ou plus tard par PATCH /api/v1/invoices/{id}/transition. Les factures d'achat ne sont pas concernées, pas plus que les factures que Scribee génère lui-même (POST /api/v1/workspaces/{workspace_id}/invoices, avec ou sans pdf_base64).
Un fichier non conforme est refusé, et rien n'est créé quand le dépôt était demandé dès l'import. details.file[0] nomme les règles PDF/A en échec (trois au plus) et le fichier :
{
"error": "unprocessable_entity",
"code": "facturx_not_pdfa",
"message": "Échec du traitement du fichier de facture",
"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."]
}
}
code: "facturx_not_pdfa" signale un verdict sur le fichier, et un fichier importé ne se remplace pas : redéposer la même facture renvoie le même refus, ne rejouez donc pas l'appel. Corrigez le fichier dans votre générateur, puis importez-le à nouveau. Quand le refus est survenu sur l'endpoint de transition, la facture reste en brouillon avec son numéro ; supprimez-la (DELETE /api/v1/invoices/{id}) avant de réimporter le fichier corrigé, sans quoi l'import est refusé pour numéro déjà importé. L'enveloppe de l'endpoint de transition est décrite dans Cycle de vie des factures.
Si veraPDF ne peut pas rendre de verdict, le dépôt est refusé lui aussi, avec code: "facturx_validation_unavailable" et dans details.file[0] : 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. Le fichier n'a pas été jugé et rien n'est enregistré : rejouez le même appel plus tard. Branchez votre traitement sur code, pas sur le texte de details.file[0].
Le verdict est publié sur la facture dans facturx_conformance. Sur une facture de vente importée sous forme de Factur-X, ce champ vaut null tant que le dépôt n'a pas été contrôlé, y compris après un refus facturx_validation_unavailable. Il passe à compliant quand le dépôt est accepté, et à non_compliant après un refus facturx_not_pdfa sur l'endpoint de transition. Après un refus du dépôt demandé dès l'import, aucune facture n'existe pour le porter.
La cause la plus fréquente est une pièce jointe complémentaire embarquée sans type MIME : la règle 6.8.1 exige que chaque fichier embarqué déclare son type MIME (clé /Subtype de la spécification de fichier). Avant d'envoyer vos Factur-X :
- donnez à chaque fichier embarqué, XML de facture compris, un type MIME (
/Subtype) et une relationAFRelationship; - validez vos fichiers avec veraPDF, profil PDF/A-3b (
verapdf --flavour 3b facture.pdf).
Pages liées
- Cycle de vie des factures - états, codes de statut et transitions
- Factures fournisseurs - traiter les factures d'achat après l'import
- Référence API : importer un fichier de facture