Intégration ERP/GED

Produire un Factur-X PDF/A-3 ou un XML CII depuis votre ERP

Envoyez une facture PDF avec votre invoice_data pour récupérer un PDF/A-3 avec XML CII embarqué. Si vous avez seulement besoin du XML CII, demandez output=xml_cii avec des données ERP complètes.

Avant de câbler votre mapping, vérifiez les champs listés plus bas. Une version API antérieure peut refuser un champ documenté ici.

1. Obtenir une clé

Ajoutez Authorization: Bearer ... à chaque appel. Vous pouvez créer une clé depuis /api-key.

2. Choisir la sortie

Le Factur-X PDF/A-3 demande un file. Le XML CII seul peut être produit sans PDF si invoice_data est complet.

3. Lire la réponse

Avant de stocker le résultat, vérifiez success, target.status et la présence du fichier demandé.

4. Garder la PA séparée

Un résultat /convert reste une production documentaire. Il ne soumet pas la facture et ne garantit pas une acceptation par une Plateforme Agréée.

Formats à ne pas confondre

  • Factur-X PDF/A-3 : PDF lisible avec XML CII embarqué. Demandez-le avec output=facturx_pdfa3, ou omettez output puisque c’est la sortie par défaut.
  • XML CII seul : fichier XML sans conteneur PDF, demandé avec output=xml_cii. Avec un invoice_data complet, le PDF source est optionnel. Ce livrable ne doit pas être présenté comme un Factur-X.
  • UBL : non supporté aujourd’hui par /convert. C’est un chantier séparé, même si UBL est une syntaxe EN16931 dans d’autres contextes.
  • PA/SC : /convert ne réalise ni routage réglementaire, ni dépôt, ni acceptation PA.

Premier appel : Factur-X PDF/A-3

La clé API s'envoie en header X-API-Key (recommandé pour une clé statique ERP) ou, à l'identique, en Authorization: Bearer <clé> — les deux fonctionnent. Définissez votre clé API dans FACTURX_API_KEY, puis gardez la même clé d’idempotence uniquement pour rejouer la même requête complète. Utilisez une nouvelle clé si le fichier, le JSON, la sortie, la cible de validation, les options, un complément inline ou un sélecteur de profil enregistré (profile_reuse, issuer_id/client_id, seller_profile_id/buyer_profile_id) changent. Comportement vérifié en production : une requête différente sous la même clé est refusée en 409 idempotency_conflict sans exécution ni consommation de quota ; une clé rejouée pendant le traitement répond 409 idempotency_in_progress — attendez la fin du premier appel plutôt que de rejouer en boucle.

curl -X POST https://api.facturxapi.com/api/v1/convert \
  -H "Authorization: Bearer $FACTURX_API_KEY" \
  -H "Idempotency-Key: erp-fa-2026-042-v1" \
  -F "file=@./facture.pdf" \
  -F "output=facturx_pdfa3" \
  -F "validation_target=en16931" \
  -F 'invoice_data={
  "invoice_number": "FA-2026-042",
  "issue_date": "2026-04-01",
  "invoice_type": "380",
  "currency": "EUR",
  "seller": {
    "name": "Ma Societe SAS",
    "siret": "10000000900017",
    "vat_id": "FR88100000009",
    "address": {
      "street": "10 rue de Rivoli",
      "city": "Paris",
      "postal_code": "75001",
      "country": "FR"
    }
  },
  "buyer": {
    "name": "Client SA",
    "siret": "10000001700010",
    "address": {
      "street": "20 rue de la Republique",
      "city": "Lyon",
      "postal_code": "69002",
      "country": "FR"
    }
  },
  "references": {
    "buyer_reference": "SERVICE-ACHATS",
    "purchase_order_reference": "PO-2026-0017",
    "contract_reference": "CTR-2026-04"
  },
  "totals": {
    "net": "970.00",
    "tax": "194.00",
    "gross": "1164.00",
    "prepaid_amount": "100.00",
    "rounding_amount": "0.00",
    "due": "1064.00"
  },
  "tax_breakdown": [
    {
      "rate": "20.00",
      "category": "S",
      "base": "970.00",
      "amount": "194.00"
    }
  ],
  "line_items": [
    {
      "number": "1",
      "description": "Prestation conseil",
      "quantity": "10",
      "unit": "C62",
      "unit_price": "100.00",
      "net_amount": "970.00",
      "vat_rate": "20.00",
      "vat_category": "S",
      "purchase_order_line_reference": "10",
      "gross_unit_price": "120.00",
      "price_discount": "20.00",
      "allowances": [
        {
          "amount": "50.00",
          "reason": "Remise ligne",
          "reason_code": "95"
        }
      ],
      "charges": [
        {
          "amount": "20.00",
          "reason": "Supplement urgent"
        }
      ]
    }
  ],
  "payment": {
    "due_date": "2026-05-01",
    "terms": "Paiement a 30 jours",
    "iban": "FR7630006000011234567890189"
  },
  "delivery": {
    "date": "2026-04-01",
    "address": {
      "street": "20 rue de la Republique",
      "city": "Lyon",
      "postal_code": "69002",
      "country": "FR"
    }
  },
  "invoicing_period": {
    "start_date": "2026-04-01",
    "end_date": "2026-04-30"
  }
}'

Flux sans PDF : XML CII seul

Pour un flux ERP sans conteneur PDF, demandez explicitement output=xml_cii. Le résultat est un XML CII, pas un document Factur-X.

curl -X POST https://api.facturxapi.com/api/v1/convert \
  -H "Authorization: Bearer $FACTURX_API_KEY" \
  -H "Idempotency-Key: erp-fa-2026-042-xml-v1" \
  -F "output=xml_cii" \
  -F "validation_target=en16931" \
  -F 'invoice_data={
  "invoice_number": "FA-2026-042",
  "issue_date": "2026-04-01",
  "invoice_type": "380",
  "currency": "EUR",
  "seller": {
    "name": "Ma Societe SAS",
    "siret": "10000000900017",
    "vat_id": "FR88100000009",
    "address": {
      "street": "10 rue de Rivoli",
      "city": "Paris",
      "postal_code": "75001",
      "country": "FR"
    }
  },
  "buyer": {
    "name": "Client SA",
    "siret": "10000001700010",
    "address": {
      "street": "20 rue de la Republique",
      "city": "Lyon",
      "postal_code": "69002",
      "country": "FR"
    }
  },
  "references": {
    "buyer_reference": "SERVICE-ACHATS",
    "purchase_order_reference": "PO-2026-0017",
    "contract_reference": "CTR-2026-04"
  },
  "totals": {
    "net": "970.00",
    "tax": "194.00",
    "gross": "1164.00",
    "prepaid_amount": "100.00",
    "rounding_amount": "0.00",
    "due": "1064.00"
  },
  "tax_breakdown": [
    {
      "rate": "20.00",
      "category": "S",
      "base": "970.00",
      "amount": "194.00"
    }
  ],
  "line_items": [
    {
      "number": "1",
      "description": "Prestation conseil",
      "quantity": "10",
      "unit": "C62",
      "unit_price": "100.00",
      "net_amount": "970.00",
      "vat_rate": "20.00",
      "vat_category": "S",
      "purchase_order_line_reference": "10",
      "gross_unit_price": "120.00",
      "price_discount": "20.00",
      "allowances": [
        {
          "amount": "50.00",
          "reason": "Remise ligne",
          "reason_code": "95"
        }
      ],
      "charges": [
        {
          "amount": "20.00",
          "reason": "Supplement urgent"
        }
      ]
    }
  ],
  "payment": {
    "due_date": "2026-05-01",
    "terms": "Paiement a 30 jours",
    "iban": "FR7630006000011234567890189"
  },
  "delivery": {
    "date": "2026-04-01",
    "address": {
      "street": "20 rue de la Republique",
      "city": "Lyon",
      "postal_code": "69002",
      "country": "FR"
    }
  },
  "invoicing_period": {
    "start_date": "2026-04-01",
    "end_date": "2026-04-30"
  }
}'

Exemple Node minimal

Le code ci-dessous illustre seulement l’appel HTTP. Votre intégration doit construire invoiceData depuis votre ERP.

import { readFile } from "node:fs/promises";

const invoiceData = {
  "invoice_number": "FA-2026-042",
  "issue_date": "2026-04-01",
  "invoice_type": "380",
  "currency": "EUR",
  "seller": {
    "name": "Ma Societe SAS",
    "siret": "10000000900017",
    "vat_id": "FR88100000009",
    "address": {
      "street": "10 rue de Rivoli",
      "city": "Paris",
      "postal_code": "75001",
      "country": "FR"
    }
  },
  "buyer": {
    "name": "Client SA",
    "siret": "10000001700010",
    "address": {
      "street": "20 rue de la Republique",
      "city": "Lyon",
      "postal_code": "69002",
      "country": "FR"
    }
  },
  "references": {
    "buyer_reference": "SERVICE-ACHATS",
    "purchase_order_reference": "PO-2026-0017",
    "contract_reference": "CTR-2026-04"
  },
  "totals": {
    "net": "970.00",
    "tax": "194.00",
    "gross": "1164.00",
    "prepaid_amount": "100.00",
    "rounding_amount": "0.00",
    "due": "1064.00"
  },
  "tax_breakdown": [
    {
      "rate": "20.00",
      "category": "S",
      "base": "970.00",
      "amount": "194.00"
    }
  ],
  "line_items": [
    {
      "number": "1",
      "description": "Prestation conseil",
      "quantity": "10",
      "unit": "C62",
      "unit_price": "100.00",
      "net_amount": "970.00",
      "vat_rate": "20.00",
      "vat_category": "S",
      "purchase_order_line_reference": "10",
      "gross_unit_price": "120.00",
      "price_discount": "20.00",
      "allowances": [
        {
          "amount": "50.00",
          "reason": "Remise ligne",
          "reason_code": "95"
        }
      ],
      "charges": [
        {
          "amount": "20.00",
          "reason": "Supplement urgent"
        }
      ]
    }
  ],
  "payment": {
    "due_date": "2026-05-01",
    "terms": "Paiement a 30 jours",
    "iban": "FR7630006000011234567890189"
  },
  "delivery": {
    "date": "2026-04-01",
    "address": {
      "street": "20 rue de la Republique",
      "city": "Lyon",
      "postal_code": "69002",
      "country": "FR"
    }
  },
  "invoicing_period": {
    "start_date": "2026-04-01",
    "end_date": "2026-04-30"
  }
};

const form = new FormData();
form.set("file", new Blob([await readFile("./facture.pdf")], { type: "application/pdf" }), "facture.pdf");
form.set("output", "facturx_pdfa3");
form.set("validation_target", "en16931");
form.set("invoice_data", JSON.stringify(invoiceData));

const response = await fetch("https://api.facturxapi.com/api/v1/convert", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.FACTURX_API_KEY}`,
    "Idempotency-Key": "erp-fa-2026-042-v1",
  },
  body: form,
});

const result = await response.json();
if (!response.ok || result.success !== true) {
  throw new Error(JSON.stringify(result));
}

JSON d’entrée

Exemple synthétique pour invoice_data. Il couvre les zones les plus utiles pour un premier test ERP/GED : références, totaux de paiement, remises/charges, ventilation TVA, livraison, période de facturation et tarification ligne.

{
  "invoice_number": "FA-2026-042",
  "issue_date": "2026-04-01",
  "invoice_type": "380",
  "currency": "EUR",
  "seller": {
    "name": "Ma Societe SAS",
    "siret": "10000000900017",
    "vat_id": "FR88100000009",
    "address": {
      "street": "10 rue de Rivoli",
      "city": "Paris",
      "postal_code": "75001",
      "country": "FR"
    }
  },
  "buyer": {
    "name": "Client SA",
    "siret": "10000001700010",
    "address": {
      "street": "20 rue de la Republique",
      "city": "Lyon",
      "postal_code": "69002",
      "country": "FR"
    }
  },
  "references": {
    "buyer_reference": "SERVICE-ACHATS",
    "purchase_order_reference": "PO-2026-0017",
    "contract_reference": "CTR-2026-04"
  },
  "totals": {
    "net": "970.00",
    "tax": "194.00",
    "gross": "1164.00",
    "prepaid_amount": "100.00",
    "rounding_amount": "0.00",
    "due": "1064.00"
  },
  "tax_breakdown": [
    {
      "rate": "20.00",
      "category": "S",
      "base": "970.00",
      "amount": "194.00"
    }
  ],
  "line_items": [
    {
      "number": "1",
      "description": "Prestation conseil",
      "quantity": "10",
      "unit": "C62",
      "unit_price": "100.00",
      "net_amount": "970.00",
      "vat_rate": "20.00",
      "vat_category": "S",
      "purchase_order_line_reference": "10",
      "gross_unit_price": "120.00",
      "price_discount": "20.00",
      "allowances": [
        {
          "amount": "50.00",
          "reason": "Remise ligne",
          "reason_code": "95"
        }
      ],
      "charges": [
        {
          "amount": "20.00",
          "reason": "Supplement urgent"
        }
      ]
    }
  ],
  "payment": {
    "due_date": "2026-05-01",
    "terms": "Paiement a 30 jours",
    "iban": "FR7630006000011234567890189"
  },
  "delivery": {
    "date": "2026-04-01",
    "address": {
      "street": "20 rue de la Republique",
      "city": "Lyon",
      "postal_code": "69002",
      "country": "FR"
    }
  },
  "invoicing_period": {
    "start_date": "2026-04-01",
    "end_date": "2026-04-30"
  }
}

Champs utilisables dans invoice_data

invoice_number
Rôle
Numéro facture
BT/BG
BT-1
Disponibilité
Disponible
issue_date
Rôle
Date émission
BT/BG
BT-2
Disponibilité
Disponible
invoice_type
Rôle
Type document
BT/BG
BT-3
Disponibilité
Disponible
currency
Rôle
Devise
BT/BG
BT-5
Disponibilité
Disponible
seller.name
Rôle
Nom vendeur
BT/BG
BT-27
Disponibilité
Disponible
seller.siret
Rôle
Identifiant vendeur
BT/BG
BT-30
Disponibilité
Disponible
seller.vat_id
Rôle
TVA intracom vendeur
BT/BG
BT-31
Disponibilité
Disponible
seller.tax_registration_id
Rôle
Identifiant fiscal vendeur hors TVA
BT/BG
BT-32
Disponibilité
Disponible pour les cas IGIC/IPSI
seller.address.street / city / postal_code / country
Rôle
Adresse postale vendeur
BT/BG
BT-35 / BT-37 / BT-38 / BT-40
Disponibilité
Disponible pour EN16931 ; country est requis
seller.electronic_address.value / scheme_id
Rôle
Adresse électronique vendeur
BT/BG
BT-34
Disponibilité
Disponible ; utilisez uniquement un identifiant de routage confirmé
buyer.name
Rôle
Nom acheteur
BT/BG
BT-44
Disponibilité
Disponible
buyer.siret
Rôle
Identifiant acheteur
BT/BG
BT-46 + BT-47 SIREN calculé
Disponibilité
Disponible ; le SIREN est déduit du SIRET
buyer.address.street / city / postal_code / country
Rôle
Adresse postale acheteur
BT/BG
BT-50 / BT-52 / BT-53 / BT-55
Disponibilité
Disponible pour EN16931 ; country est requis
buyer.electronic_address.value / scheme_id
Rôle
Adresse électronique acheteur
BT/BG
BT-49
Disponibilité
Disponible ; utilisez uniquement un identifiant de routage confirmé
references.buyer_reference / purchase_order_reference / contract_reference / project_reference / despatch_advice_reference / receiving_advice_reference / buyer_accounting_reference
Rôle
Références acheteur, commande, contrat, projet, avis
BT/BG
BT-10 / BT-11 / BT-12 / BT-13 / BT-15 / BT-16 / BT-19
Disponibilité
Disponible
invoice_reference / invoice_reference_issue_date
Rôle
Facture précédente pour un avoir ou une correction
BT/BG
BT-25 / BT-26 / BG-3
Disponibilité
Disponible pour les types 381 et 384
document_notes[].content / subject_code
Rôle
Notes de document et mentions PMT, PMD ou AAB
BT/BG
BT-22 / BT-21
Disponibilité
Disponible ; subject_code est optionnel, mais limité à PMT, PMD ou AAB lorsqu’il est fourni
totals.net / totals.tax / totals.gross
Rôle
Totaux HT, TVA, TTC
BT/BG
BT-109 / BT-110 / BT-112
Disponibilité
Disponible
totals.prepaid_amount / totals.rounding_amount / totals.due
Rôle
Acompte, arrondi, net à payer
BT/BG
BT-113 / BT-114 / BT-115
Disponibilité
Disponible
allowances[] / charges[]
Rôle
Remises et charges au niveau document
BT/BG
BG-20 / BG-21
Disponibilité
Disponible
tax_breakdown[]
Rôle
Ventilation TVA fournie par votre ERP
BT/BG
BG-23
Disponibilité
Disponible
tax_breakdown[].exemption_reason / exemption_reason_code
Rôle
Motif ou code exonération par ventilation TVA
BT/BG
BT-120 / BT-121
Disponibilité
Disponible pour toutes les catégories exonérantes (E, AE, K, G, O), y compris les mélanges — en multi-catégories, le motif par ventilation est requis
vat_exemption_reason / vat_exemption_code
Rôle
Motif ou code exonération global
BT/BG
BT-120 / BT-121
Disponibilité
Disponible seulement si une seule catégorie exonérante le porte
line_items[]
Rôle
Lignes facture
BT/BG
BG-25
Disponibilité
Disponible
line_items[].purchase_order_line_reference / buyer_accounting_reference
Rôle
Références ligne
BT/BG
BT-132 / BT-133
Disponibilité
Disponible
line_items[].allowances[] / charges[]
Rôle
Remises et charges de ligne
BT/BG
BG-27 / BG-28
Disponibilité
Disponible dans la version API correspondant à cette documentation
line_items[].gross_unit_price (BT-148) / price_discount (BT-147)
Rôle
Prix brut et remise de prix
BT/BG
BT-148 / BT-147
Disponibilité
Disponible dans la version API correspondant à cette documentation
payment.due_date / payment.terms / payment.iban / payment.bic
Rôle
Échéance et paiement
BT/BG
BT-9 / BT-20 / BT-84 / BT-86
Disponibilité
Disponible
delivery.date / delivery.address
Rôle
Livraison et adresse de livraison
BT/BG
BT-72 / BG-13
Disponibilité
Disponible
invoicing_period (alias accepté : billing_period)
Rôle
Période de facturation
BT/BG
BG-14 / BT-73 / BT-74
Disponibilité
Disponible ; billing_period est accepté comme alias

Compléments pour un document extrait et champs refusés

Pour une source ERP complète, envoyez les adresses électroniques, les notes et la référence de facture précédente directement dans invoice_data avec les chemins confirmés ci-dessus. Les racines ci-dessous servent surtout à compléter un PDF ou XML analysé ; les champs réellement non supportés sont refusés explicitement plutôt qu’ignorés.

seller_context.electronic_address / buyer_context.electronic_address
Rôle
Routage France pour un document extrait
BT/BG
BT-34 / BT-49
Comment l’envoyer
Complément contextuel d’un PDF ou XML analysé ; avec une source ERP complète, utilisez seller.electronic_address et buyer.electronic_address dans invoice_data
seller_context.document_notes[] / buyer_context.document_notes[]
Rôle
Mentions PMT, PMD ou AAB pour un document extrait
BT/BG
BT-22 / BT-21
Comment l’envoyer
Complément contextuel d’un PDF ou XML analysé ; avec une source ERP complète, utilisez document_notes[] dans invoice_data
invoice_patch.preceding_invoice.reference / issue_date
Rôle
Facture précédente pour un document extrait
BT/BG
BT-25 / BT-26
Comment l’envoyer
Complément d’un PDF analysé ; invoice_patch ne peut pas être combiné avec un invoice_data complet
buyer.tax_registration_id
Rôle
Identifiant fiscal acheteur hors TVA
BT/BG
non émis
Comment l’envoyer
Refusé explicitement dans invoice_data : BT-32 est vendeur uniquement

Exemple de données complémentaires

Quand un diagnostic ou un scan demande de compléter un document analysé, reprenez uniquement les racines indiquées : invoice_patch, seller_context ou buyer_context. invoice_patch ne peut pas être combiné avec un invoice_data complet ; pour un flux ERP complet, préférez les champs directs de la matrice.

Les valeurs entre chevrons sont des champs à renseigner, pas des valeurs par défaut. Pour le schéma 0225, utilisez l’identifiant de routage confirmé par votre contexte métier ou par le diagnostic ; ne recopiez pas automatiquement un SIRET.

{
  "invoice_patch": {
    "preceding_invoice": {
      "issue_date": "2025-08-31"
    },
    "payment": {
      "iban": "FR7630006000011234567890189"
    }
  },
  "seller_context": {
    "electronic_address": {
      "value": "<identifiant_routage_vendeur_0225>",
      "scheme_id": "0225"
    },
    "document_notes": [
      {
        "subject_code": "PMT",
        "content": "<mention_frais_recouvrement_pmt_a_completer>"
      },
      {
        "subject_code": "PMD",
        "content": "<mention_penalites_retard_pmd_a_completer>"
      },
      {
        "subject_code": "AAB",
        "content": "<mention_escompte_aab_a_completer>"
      }
    ]
  },
  "buyer_context": {
    "electronic_address": {
      "value": "<identifiant_routage_acheteur_0225>",
      "scheme_id": "0225"
    }
  }
}

Réponses à gérer

Succès PDF/A-3

Ne traitez le PDF généré comme disponible que si la réponse indique le succès, la cible vérifiée et packagingPerformed=true.

{
  "success": true,
  "target": {
    "requested": "en16931",
    "executed": "en16931",
    "status": "verified",
    "missing_inputs": [],
    "not_run_reason": null
  },
  "result": {
    "targetProfile": "EN16931",
    "conversionSuccessful": true,
    "xml": "PD94bWwgdmVyc2lvbj0iMS4wIj8+...",
    "xmlSize": 4096,
    "pdf": "JVBERi0xLjQKJc...",
    "pdfSize": 102400,
    "packagingPerformed": true,
    "validation": {
      "valid": true,
      "profile": "EN16931"
    }
  }
}

Succès XML CII seul

Pour output=xml_cii, utilisez result.xml. Le PDF est absent et packagingPerformed=false ; cette absence est normale pour ce livrable.

{
  "success": true,
  "target": {
    "requested": "en16931",
    "executed": "en16931",
    "status": "verified",
    "missing_inputs": [],
    "not_run_reason": null
  },
  "result": {
    "targetProfile": "EN16931",
    "conversionSuccessful": true,
    "xml": "PD94bWwgdmVyc2lvbj0iMS4wIj8+...",
    "xmlSize": 4096,
    "pdf": null,
    "pdfUrl": null,
    "packagingPerformed": false,
    "validation": {
      "valid": true,
      "profile": "EN16931"
    }
  }
}

Champs à fournir

Si target.status est bloqué ou incomplet, affichez les champs retournés au lieu de masquer l’échec. Une liste non exhaustive ne doit pas être présentée comme une checklist finale.

{
  "success": false,
  "target": {
    "requested": "france_2026",
    "executed": null,
    "status": "blocked",
    "missing_inputs": [
      "seller_context.electronic_address.value",
      "buyer_context.electronic_address.value"
    ],
    "not_run_reason": "missing_inputs"
  },
  "verdict": "convert_with_inputs",
  "targetPreflight": {
    "requested": "france_2026",
    "status": "incomplete",
    "exhaustive": false,
    "requiredInputs": [
      {
        "btCode": "BT-34",
        "requestContextPath": "seller_context.electronic_address.value"
      },
      {
        "btCode": "BT-49",
        "requestContextPath": "buyer_context.electronic_address.value"
      }
    ],
    "unsupportedBlockers": []
  }
}

Erreurs fréquentes

  • 401 : clé absente, invalide ou révoquée. Reprenez la clé depuis /api-key ou /dashboard/keys.
  • 400 file_required_for_pdf_output : vous demandez le livrable PDF/A-3 sans fournir de PDF. Ajoutez file ou demandez explicitement output=xml_cii avec un invoice_data complet.
  • 400 invalid_options : options.mode="xml_only" n’est pas le sélecteur public du livrable final. Utilisez output=xml_cii.
  • 400 invalid_invoice_patch : invoice_patch combiné avec un invoice_data complet, ou JSON de patch invalide.
  • 409 inline_context_conflict : une valeur inline contredit une valeur visible dans le document.
  • 409 idempotency_conflict : même Idempotency-Key rejouée avec une requête différente (fichier, invoice_data, sortie, cible ou options modifiés). Action : nouvelle clé. La requête rejouée n'est pas exécutée et ne consomme pas de quota (vérifié en production).
  • 409 idempotency_in_progress : même clé rejouée pendant qu'une opération est en cours. Action : attendre puis re-vérifier côté ERP ; ne changez pas de clé pour « forcer » un doublon.
  • 413 file_too_large : fichier au-delà de la taille maximale (10 Mo).
  • 415 unsupported_media_type : fichier non PDF pour le flux Factur-X PDF/A-3.
  • 422 insufficient_data : données insuffisantes pour produire la cible demandée.
  • 422 invalid_invoice_data : arithmétique ligne incohérente, remise de prix sans prix brut, catégorie TVA O/K/G/L/M incohérente, motif d’exonération ambigu en mode multi-catégories, buyer.tax_registration_id non supporté, ou règle EN16931 refusée explicitement.
  • 422 unsupported_output_profile_for_invoice_data : invoice_data ne peut pas cibler MINIMUM, BASIC_WL ou BASIC. Gardez le profil EN16931 par défaut ou utilisez EXTENDED si votre contrat le prévoit.
  • 422 unextractable_pdf : aucune donnée exploitable extraite du PDF, sans invoice_data pour compenser.
  • 503 requested_output_unavailable : le PDF/A-3 demandé n’a pas pu être produit ; l’API échoue explicitement (quota remboursé) au lieu de retourner un succès XML-only silencieux.
  • 422 ocr_confidence_too_low : confiance OCR insuffisante en mode strict. Action : fournir invoice_data (recommandé) ou options.confidence_mode="lenient".
  • 503 ocr_service_unavailable / 503 ocr_quota_exhausted : OCR indisponible ou quota OCR épuisé — header Retry-After fourni. Action : rejouer après le délai, même clé d'idempotence. Sans objet en mode invoice_data complet (pas d'OCR).
  • 402 quota_exceeded : quota mensuel de factures traitées atteint (définitif jusqu'au renouvellement ou upgrade — pas de Retry-After).
  • 429 : rate limit par minute — header Retry-After fourni. Action : backoff puis rejouer à l'identique (même clé d'idempotence).

En résumé pour votre gestionnaire d'erreurs : les 4xx hors 429 et hors 409 idempotency_in_progress sont des erreurs de requête à corriger avant un nouvel appel (nouvelle clé d'idempotence) ; 409 idempotency_in_progress est le seul cas où il faut attendre sur la clé d'origine — en changer relancerait une seconde conversion ; 429 et les 503 se rejouent à l'identique après le délai ; 503 requested_output_unavailable rembourse le quota et se rejoue tel quel.

Exemple multi-taux avec remise documentaire

Facture 20 % + 5,5 % avec une remise BG-20 portée par son taux : une entrée tax_breakdown par couple (catégorie, taux), bases exactes par groupe (BR-S-08). Exemple exécuté et vérifié contre la production.

{
  "invoice_number": "FA-2026-118",
  "issue_date": "2026-06-11",
  "seller": {
    "name": "Smoke Editeur SAS",
    "address": {
      "country": "FR",
      "street": "10 rue de la Paix",
      "city": "Paris",
      "postal_code": "75002"
    },
    "vat_id": "FR80421347006",
    "siret": "42134700600080",
    "electronic_address": {
      "value": "42134700600080",
      "scheme_id": "0225"
    }
  },
  "buyer": {
    "name": "Smoke Client SARL",
    "address": {
      "country": "FR",
      "street": "5 avenue des Champs",
      "city": "Lyon",
      "postal_code": "69002"
    },
    "vat_id": "FR40303265045",
    "siret": "30326504500017"
  },
  "totals": {
    "net": "73.33",
    "tax": "7.42",
    "gross": "80.75"
  },
  "tax_breakdown": [
    {
      "rate": "20",
      "category": "S",
      "base": "23.33",
      "amount": "4.67"
    },
    {
      "rate": "5.5",
      "category": "S",
      "base": "50.00",
      "amount": "2.75"
    }
  ],
  "line_items": [
    {
      "number": "1",
      "description": "Prestation 20%",
      "quantity": "1",
      "unit_price": "33.33",
      "net_amount": "33.33",
      "vat_rate": "20"
    },
    {
      "number": "2",
      "description": "Prestation 5.5%",
      "quantity": "1",
      "unit_price": "50.00",
      "net_amount": "50.00",
      "vat_rate": "5.5"
    }
  ],
  "allowances": [
    {
      "amount": "10.00",
      "reason": "Remise",
      "vat_category": "S",
      "vat_rate": "20"
    }
  ],
  "payment": {
    "due_date": "2026-07-11",
    "terms": "30 jours"
  }
}

À préparer avant le premier test

Pour valider votre mapping sans générer de livrable ni consommer de quota, ajoutez dry_run=true : la réponse liste les champs manquants (targetPreflight, et convertPreparation.fieldsToProvide quand des champs guidés sont attendus) avant votre premier appel final.

  • Un PDF facture source testable, sans donnée client sensible si vous partagez un exemple.
  • Un JSON ERP qui contient les champs dont votre flux a besoin. invoice_data est un champ multipart de type chaîne contenant le JSON sérialisé (pas un body JSON) — en shell, préférez -F "invoice_data=$(cat facture.json)" ou --form-string pour éviter les surprises d'échappement.
  • Montants et taux en chaînes de caractères, arrondis à 2 décimales ("970.00", "20.00") — un montant sub-centime est refusé en 422 ; quantity et unit_price peuvent porter plus de décimales.
  • Une règle d’idempotence : même clé uniquement pour la même requête complète ; nouvelle clé si le fichier, le JSON, la sortie, la cible, les options ou un complément changent.
  • Un affichage clair des erreurs : champs manquants, conflit document/contexte, cible non exécutée.
  • Un message produit séparé : document généré ou diagnostiqué, mais aucun statut PA inventé.