Technique

Données ERP vers Factur-X CII : guide de mapping JSON → XML EN16931

11 min de lecture Par FacturX API

Mapper les données d'un ERP (JSON/CSV) vers XML CII EN16931 pour Factur-X. Champs obligatoires, autoliquidation BTP (art. 283, 2 nonies CGI), avoirs, exemples curl et Python.

Votre ERP contient déjà toutes les données de la facture. Le numéro, la date, les montants, les identifiants fiscaux, les lignes de détail — tout est là, structuré dans des tables ou des objets métier. Le défi n’est pas de trouver les données. C’est de les mapper vers le modèle sémantique EN16931 attendu par le XML CII, avec les bons codes, les bons formats et les bonnes cardinalités.

Ce guide montre comment structurer les données ERP en JSON pour générer un Factur-X conforme via l’API, en mode invoice_data. Il couvre le modèle de données, les transformations nécessaires, et les cas spéciaux qui posent problème en pratique — autoliquidation BTP, avoirs, multi-taux TVA.

Pour le contexte général de la conversion PDF vers Factur-X (les deux modes, la validation intégrée, les quotas), voir Convertir une facture PDF en Factur-X 1.08 conforme. Pour la cartographie complète des Business Terms EN16931, voir Champs obligatoires EN16931 : cartographie et mapping ERP.

Le modèle de données invoice_data

L’API Convert en mode ERP attend un champ invoice_data contenant un objet JSON. Cet objet est la représentation structurée de la facture — l’API le transforme en XML CII D22B conforme et l’embarque dans le PDF/A-3.

Champs racine

Champ JSONBT EN16931TypeObligatoireDescription
invoiceNumberBT-1stringouiNuméro de facture
issueDateBT-2stringouiDate d’émission (AAAAMMJJ)
typeCodeBT-3stringnon (défaut 380)Type de document UNTDID 1001
currencyCodeBT-5stringouiCode devise ISO 4217

Le typeCode vaut 380 pour une facture standard et 381 pour un avoir (note de crédit). D’autres codes existent (384 = facture corrective, 389 = autofacturation), mais 380 et 381 couvrent la grande majorité des cas en production.

Bloc seller

"seller": {
  "name": "Dupont BTP SAS",
  "vatId": "FR32123456789",
  "legalId": "12345678901234",
  "address": {
    "street": "45 Avenue des Chantiers",
    "city": "Lyon",
    "postalCode": "69003",
    "country": "FR"
  }
}
Champ JSONBT EN16931ObligatoireNotes
nameBT-27ouiRaison sociale
vatIdBT-31conditionnelTVA intracommunautaire (schemeID VA)
legalIdBT-30conditionnelSIRET en France (schemeID 0002)
address.streetBT-35nonAdresse ligne 1
address.cityBT-37nonVille
address.postalCodeBT-38nonCode postal
address.countryBT-40ouiCode pays ISO 3166-1 alpha-2

La règle Schematron BR-CO-26 exige qu’au moins un identifiant vendeur soit présent parmi BT-29, BT-30 et BT-31. En pratique, fournir vatId et legalId ensemble est la configuration la plus robuste pour les entreprises françaises.

Bloc buyer

La structure est identique au bloc seller. Le minimum requis est le name (BT-44) et le address.country (BT-55). En B2B, ajouter le vatId (BT-48) est fortement recommandé pour le routage PDP.

Bloc lines

"lines": [
  {
    "description": "Pose de cloisons sèches — chantier Bellecour",
    "quantity": 40,
    "unitCode": "HUR",
    "unitPrice": 55.00,
    "vatRate": 20,
    "vatCategory": "S"
  }
]
Champ JSONBT EN16931Notes
descriptionBT-153Nom / description de l’article
quantityBT-129Quantité facturée
unitCodeBT-130Code UN/ECE Rec 20 (C62 = unité, HUR = heure, KGM = kg)
unitPriceBT-146Prix unitaire net HT
vatRateBT-152Taux TVA applicable (ex: 20 pour 20%)
vatCategoryBT-151Code catégorie TVA UNCL 5305

Chaque facture doit contenir au moins une ligne (règle BR-16). Le unitCode par défaut est C62 (unité) s’il est omis. Les ERP stockent souvent des labels en clair (“heure”, “pièce”) qu’il faut convertir en codes UN/ECE Rec 20.

Bloc totals

"totals": {
  "netAmount": 2200.00,
  "vatAmount": 440.00,
  "grossAmount": 2640.00,
  "dueAmount": 2640.00
}
Champ JSONBT EN16931Description
netAmountBT-109Total HT
vatAmountBT-110Total TVA
grossAmountBT-112Total TTC
dueAmountBT-115Montant à payer

Les règles Schematron BR-CO-10, BR-CO-13 et BR-CO-15 vérifient la cohérence arithmétique. Un écart d’un centime entre netAmount et la somme des lignes suffit à déclencher un rejet.

Bloc taxBreakdown (multi-taux)

Quand la facture comporte plusieurs taux de TVA, le bloc taxBreakdown ventile les montants par catégorie :

"taxBreakdown": [
  {
    "vatCategory": "S",
    "vatRate": 20,
    "taxableAmount": 1800.00,
    "taxAmount": 360.00
  },
  {
    "vatCategory": "S",
    "vatRate": 5.5,
    "taxableAmount": 400.00,
    "taxAmount": 22.00
  }
]

Chaque entrée correspond à un bloc ram:ApplicableTradeTax dans le XML CII (BG-23). La règle BR-CO-17 vérifie que taxAmount = taxableAmount x vatRate / 100, arrondi à 2 décimales. Chaque ligne de facture doit être rattachée au bon taux via son champ vatCategory + vatRate.

Mapping ERP vers invoice_data : les champs qui posent problème

La plupart des champs se mappent directement — le nom du vendeur dans l’ERP devient seller.name, le numéro de facture devient invoiceNumber. Mais plusieurs transformations sont nécessaires pour passer du modèle ERP au modèle EN16931.

Dates : ISO 8601 versus AAAAMMJJ

En CII, les dates utilisent le format AAAAMMJJ (ex: 20260401) avec l’attribut format="102". Les ERP stockent généralement en ISO 8601 (2026-04-01).

L’API gère cette conversion automatiquement : vous pouvez envoyer "issueDate": "2026-04-01" ou "issueDate": "20260401" — les deux formats sont acceptés. Si vous construisez le XML vous-même, le format attendu est strictement AAAAMMJJ.

Catégories TVA : S, Z, E, AE, K, G, O

Le code catégorie TVA (UNCL 5305) est une source d’erreur fréquente. Les ERP utilisent des labels internes (“Normal”, “Exonéré”, “Intra-UE”) qu’il faut mapper vers les codes normalisés :

CodeSignificationQuand l’utiliser
SStandard ratedTaux normal (20%, 10%, 5.5%, 2.1% en France)
ZZero ratedOpération taxable à taux zéro
EExemptExonération de TVA (article 261 du CGI)
AEReverse chargeAutoliquidation — sous-traitance BTP (CGI art. 283, 2 nonies) ou prestations intra-UE (CGI art. 283, 1, al. 2)
KIntra-community supplyLivraison intracommunautaire de biens
GExport outside EUExportation hors UE
ONot subject to VATHors champ TVA

Chaque code active des règles Schematron spécifiques (familles BR-S-*, BR-Z-*, BR-E-*, BR-AE-*, etc.). Utiliser le mauvais code déclenche des erreurs en cascade. Le détail de ces règles est documenté dans le Catalogue des erreurs BR-* EN16931.

Identifiants légaux : SIRET et TVA intracommunautaire

En France, deux identifiants coexistent :

  • BT-30 (identifiant légal) : le SIRET à 14 chiffres, avec schemeID="0002" (répertoire SIRENE, ISO 6523)
  • BT-31 (identifiant TVA) : le numéro de TVA intracommunautaire (FRXX + 9 chiffres), avec schemeID="VA"

Dans le JSON invoice_data, ils correspondent respectivement à legalId (SIRET) et vatId (TVA intra). L’API génère automatiquement les bons schemeID dans le XML CII.

La règle BR-CO-26 exige au moins un identifiant vendeur. Pour l’autoliquidation (code AE), les règles BR-AE exigent un identifiant fiscal vendeur (BT-31, BT-32 ou BT-63) et côté acheteur un identifiant TVA ou un identifiant légal (BT-48 ou BT-47).

Adresses : BT-35, BT-37, BT-40

Seul le code pays (address.country → BT-40) est strictement obligatoire (cardinalité 1..1). La rue (BT-35) et la ville (BT-37) sont optionnelles dans EN16931 (0..1), mais fortement recommandées pour le routage PDP et la conformité pratique.

Cas spéciaux

Autoliquidation (code TVA AE)

L’autoliquidation recouvre deux régimes juridiques distincts : la sous-traitance BTP (CGI art. 283, 2 nonies) et les prestations de services intra-UE (CGI art. 283, 1, alinéa 2). Dans les deux cas, le vendeur ne facture pas de TVA — c’est l’acheteur qui la déclare et la déduit. Le code catégorie TVA AE est le même, mais le fondement légal et la mention obligatoire diffèrent.

Dans le JSON invoice_data, quatre éléments sont nécessaires :

  1. vatCategory: "AE" sur chaque ligne de facture
  2. vatRate: 0 — le taux est zéro puisque la TVA n’est pas facturée
  3. Un motif d’exonération — le champ taxExemptionReason (BT-120) avec la mention légale
  4. Un identifiant fiscal vendeur et un identifiant acheteur — pour une facture en autoliquidation (AE), les règles BR-AE exigent un identifiant fiscal vendeur (BT-31, BT-32 ou BT-63) et côté acheteur un identifiant TVA ou un identifiant légal (BT-48 ou BT-47)

Le taxBreakdown doit refléter la même configuration :

"taxBreakdown": [
  {
    "vatCategory": "AE",
    "vatRate": 0,
    "taxableAmount": 5000.00,
    "taxAmount": 0.00,
    "taxExemptionReason": "Autoliquidation - Sous-traitance BTP, article 283, 2 nonies du CGI"
  }
]

L’absence du motif d’exonération (BT-120 ou BT-121) déclenche une erreur BR-AE. L’absence d’un identifiant fiscal vendeur (BT-31, BT-32 ou BT-63) ou d’un identifiant acheteur (BT-48 ou BT-47) en déclenche une autre. Ces deux erreurs sont parmi les plus fréquentes sur les factures de sous-traitance.

Avoir / note de crédit

Un avoir utilise le typeCode 381 au lieu de 380. Les montants suivent la convention positive — l’avoir représente un montant à restituer, les lignes et totaux sont positifs.

{
  "invoiceNumber": "AV-2026-007",
  "issueDate": "20260415",
  "typeCode": "381",
  "currencyCode": "EUR",
  "precedingInvoiceReference": "FA-2026-042",
  ...
}

Le champ precedingInvoiceReference (BT-25) permet de rattacher l’avoir à la facture d’origine. Ce lien n’est pas obligatoire dans le noyau EN16931, mais il est fortement recommandé pour la traçabilité et peut devenir requis par certaines PDP.

Multi-taux TVA

Quand une facture mélange des taux différents (20% sur les prestations, 5.5% sur les matériaux), chaque ligne doit être rattachée au bon taux, et le taxBreakdown doit ventiler les montants par catégorie/taux :

"lines": [
  {
    "description": "Main d'oeuvre — pose carrelage",
    "quantity": 20,
    "unitCode": "HUR",
    "unitPrice": 45.00,
    "vatRate": 20,
    "vatCategory": "S"
  },
  {
    "description": "Fourniture carrelage grès cérame",
    "quantity": 25,
    "unitCode": "MTK",
    "unitPrice": 32.00,
    "vatRate": 5.5,
    "vatCategory": "S"
  }
],
"taxBreakdown": [
  { "vatCategory": "S", "vatRate": 20, "taxableAmount": 900.00, "taxAmount": 180.00 },
  { "vatCategory": "S", "vatRate": 5.5, "taxableAmount": 800.00, "taxAmount": 44.00 }
],
"totals": {
  "netAmount": 1700.00,
  "vatAmount": 224.00,
  "grossAmount": 1924.00,
  "dueAmount": 1924.00
}

La règle BR-CO-17 vérifie la cohérence de chaque entrée du taxBreakdown. La règle BR-CO-15 vérifie que grossAmount = netAmount + vatAmount. Un seul centime d’écart provoque un rejet.

Exemple complet : facture de sous-traitance BTP en autoliquidation

Voici un JSON invoice_data réaliste pour une facture de sous-traitance avec autoliquidation, deux lignes de prestation, SIRET et TVA intracommunautaire.

{
  "invoiceNumber": "FA-2026-158",
  "issueDate": "20260401",
  "typeCode": "380",
  "currencyCode": "EUR",
  "seller": {
    "name": "Dupont BTP SAS",
    "vatId": "FR32123456789",
    "legalId": "12345678901234",
    "address": {
      "street": "45 Avenue des Chantiers",
      "city": "Lyon",
      "postalCode": "69003",
      "country": "FR"
    }
  },
  "buyer": {
    "name": "Grands Travaux Rhône SA",
    "vatId": "FR87987654321",
    "legalId": "98765432109876",
    "address": {
      "street": "12 Rue de la République",
      "city": "Lyon",
      "postalCode": "69002",
      "country": "FR"
    }
  },
  "lines": [
    {
      "description": "Pose cloisons sèches — Lot 3 chantier Bellecour",
      "quantity": 120,
      "unitCode": "HUR",
      "unitPrice": 48.00,
      "vatRate": 0,
      "vatCategory": "AE"
    },
    {
      "description": "Fourniture et pose faux plafonds — Lot 3 chantier Bellecour",
      "quantity": 85,
      "unitCode": "MTK",
      "unitPrice": 35.00,
      "vatRate": 0,
      "vatCategory": "AE"
    }
  ],
  "taxBreakdown": [
    {
      "vatCategory": "AE",
      "vatRate": 0,
      "taxableAmount": 8735.00,
      "taxAmount": 0.00,
      "taxExemptionReason": "Autoliquidation - Sous-traitance BTP, article 283, 2 nonies du CGI"
    }
  ],
  "totals": {
    "netAmount": 8735.00,
    "vatAmount": 0.00,
    "grossAmount": 8735.00,
    "dueAmount": 8735.00
  }
}

Les montants se vérifient : ligne 1 (120 x 48.00 = 5 760.00) + ligne 2 (85 x 35.00 = 2 975.00) = 8 735.00 = netAmount. TVA à 0 (autoliquidation), donc grossAmount = netAmount.

Appel curl

curl -X POST https://api.facturxapi.com/api/v1/convert \
  -H "X-API-Key: votre-cle-api" \
  -F "file=@facture-btp.pdf" \
  -F 'invoice_data={
    "invoiceNumber": "FA-2026-158",
    "issueDate": "20260401",
    "typeCode": "380",
    "currencyCode": "EUR",
    "seller": {
      "name": "Dupont BTP SAS",
      "vatId": "FR32123456789",
      "legalId": "12345678901234",
      "address": {
        "street": "45 Avenue des Chantiers",
        "city": "Lyon",
        "postalCode": "69003",
        "country": "FR"
      }
    },
    "buyer": {
      "name": "Grands Travaux Rhône SA",
      "vatId": "FR87987654321",
      "legalId": "98765432109876",
      "address": {
        "street": "12 Rue de la République",
        "city": "Lyon",
        "postalCode": "69002",
        "country": "FR"
      }
    },
    "lines": [
      {
        "description": "Pose cloisons sèches — Lot 3 chantier Bellecour",
        "quantity": 120,
        "unitCode": "HUR",
        "unitPrice": 48.00,
        "vatRate": 0,
        "vatCategory": "AE"
      },
      {
        "description": "Fourniture et pose faux plafonds — Lot 3 chantier Bellecour",
        "quantity": 85,
        "unitCode": "MTK",
        "unitPrice": 35.00,
        "vatRate": 0,
        "vatCategory": "AE"
      }
    ],
    "taxBreakdown": [
      {
        "vatCategory": "AE",
        "vatRate": 0,
        "taxableAmount": 8735.00,
        "taxAmount": 0.00,
        "taxExemptionReason": "Autoliquidation - Sous-traitance BTP, article 283, 2 nonies du CGI"
      }
    ],
    "totals": {
      "netAmount": 8735.00,
      "vatAmount": 0.00,
      "grossAmount": 8735.00,
      "dueAmount": 8735.00
    }
  }'

Réponse attendue

{
  "durationMs": 2800,
  "targetProfile": "EN16931",
  "conversionSuccessful": true,
  "xml": "PD94bWwg...",
  "xmlSize": 5120,
  "pdf": "JVBERi0x...",
  "pdfSize": 115200,
  "extraction": {
    "sourceType": "structured_data",
    "pageCount": 1
  },
  "validation": {
    "valid": true,
    "profile": "EN16931",
    "summary": { "errorCount": 0, "warningCount": 0 }
  }
}

Le sourceType: "structured_data" confirme que les données viennent du JSON invoice_data et non d’une extraction PDF. La validation intégrée confirme la conformité EN16931 sans erreur.

Validation des données avant conversion

L’API valide les données ERP avant de générer le XML CII. Si une incohérence est détectée, elle retourne un code 422 avec un diagnostic précis avant même de toucher au PDF.

Erreurs courantes côté ERP

ErreurCause ERP typiqueDiagnostic API
totals.netAmount != somme des lignesArrondi ou ligne oubliée dans l’exportarithmetic_mismatch: netAmount
Identifiant fiscal absent avec catégorie AEChamp TVA ou identifiant légal non mappé dans l’export ERPmissing_field: seller tax identifier (required for AE)
vatRate > 0 avec catégorie AECode TVA ERP mal convertiinvalid_combination: AE with vatRate > 0
taxBreakdown incohérent avec les lignesVentilation TVA calculée indépendamment des lignestax_breakdown_mismatch
unitCode invalideLabel en clair au lieu du code UN/ECEinvalid_unit_code

Recommandation : valider côté ERP avant d’envoyer

Plutôt que de découvrir les erreurs via l’API, implémentez les vérifications en amont dans votre pipeline ERP :

  1. Cohérence arithmétique — somme des lignes (quantity x unitPrice) = netAmount, netAmount + vatAmount = grossAmount
  2. Complétude des identifiants — au moins un identifiant vendeur (vatId ou legalId), code pays présent
  3. Codes normalisés — vatCategory parmi S, Z, E, AE, K, G, O ; unitCode valide UN/ECE Rec 20 ; currencyCode ISO 4217
  4. Cohérence TVA — si vatCategory = “AE”, alors vatRate = 0 et taxExemptionReason présent

Cela réduit les allers-retours avec l’API et accélère le flux de production. Les erreurs de format (dates, décimaux) peuvent être corrigées automatiquement — voir Corriger automatiquement un XML CII invalide.

Aller plus loin

#ERP #JSON #CII #factur-x #EN16931 #mapping #autoliquidation #API