API REST · OpenAPI 3.1 · Factur-X CII

Préparez les Factur-X de vos clients avant leur envoi via une Plateforme Agréée (PA) ou une solution compatible (SC).

Une brique technique REST à brancher sur votre ERP. Diagnostiquez les PDF/XML et réparez les XML CII récupérables. Pour générer un Factur-X PDF/A-3, fournissez soit un invoice_data complet, dont l’API rend le porteur sans PDF source, soit un PDF existant comme porteur alternatif. Avec output=xml_cii, l’API produit uniquement le XML CII.

Endpoints
OpenAPI
Usage
REST
Hébergement
Front : Cloudflare API : Railway
PDF/A-3
veraPDF

POST api.facturxapi.com/api/v1/validate

Live
curl -X POST https://api.facturxapi.com/api/v1/validate \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "file=@./facture.pdf"

Remplacez YOUR_API_KEY par votre clé Sandbox gratuite, activée par email.

Premier appel en 12 lignes

Une simulation (dry_run) sur le cas « facture enrichie France 2026 (mentions PMT/PMD/AAB, adressage) » du contrat d’intégration : la clé reste dans une variable d’environnement.

Avant de lancer l’appel : créez votre clé API.

# 1. Copiez le cas « facture enrichie France 2026 » dans invoice-data.json
#    (contrat d’intégration, section « Cas types », bouton « Copier le JSON »)
export FACTURX_API_KEY="votre-clé-api"
curl -sS -X POST https://api.facturxapi.com/api/v1/convert \
  -H "X-API-Key: $FACTURX_API_KEY" \
  -F "dry_run=true" -F "validation_target=france_2026" \
  -F 'invoice_data=<invoice-data.json' \
  -w "\nHTTP %{http_code}\n"
# Réponse :
  "dry_run": true,
  "result": { "processedInvoicesCharged": 0, … }
HTTP 200

Ensuite : le guide JSON ERP avec vos données complètes.

Ce que vous pouvez faire aujourd’hui

Une ligne par usage, avec la commande ou la page pour commencer.

Versions publiques et runtime observé

Deux sources distinctes : publications upstream et exécution FacturX API

Les versions publiques viennent des éditeurs (FNFE-MPE, CEN). Le runtime exécuté est lu séparément via GET https://api.facturxapi.com/meta pour le service configuré sur cette page (sur l'hôte de l'API, pas sur ce site). Une publication plus récente n'est pas automatiquement celle du moteur ; en l'absence de réponse exploitable, la version runtime reste non confirmée.

Publications publiques (sources upstream)

Factur-X publique
1.09.2

FNFE-MPE / FeRD (publication mise à jour le 2026-08-04)

Runtime déclaré par le service configuré dans GET /meta

Factur-X runtime exécuté
1.09.2
Détail machine (GET /meta)
Source
FACTURX_RUNTIME_VERSION
execution_status
verified
manifest_digest
fd05a55ec6d973b667eb0e2d385f050aafe3068be42a1b4d334f7a0eaee3c15a
compiled_en16931_xslt_digest
67f03050e3add4c83dc93de36e4501f6a11aae1ac790b3f9558feb0581019bcb
source_zip
Factur-X-1.09.2-Zugferd-2.5.2-2026-08-04-FINAL-FR.zip
7d2fe79580270c8babea3e40dbdae47bd68c0baa7022051be4f955921a1cd29a
artifact_base_path
resources/facturx_1.09.2

Source GET /meta · réponse générée 2026-11-14T01:09:31.281653+00:00

Règles EN 16931 par syntaxe (/meta)
UBL : règles CEN 1.3.16 ; CII : schematron Factur-X 1.09.2
Artefacts France / FR-CTC
v1.4.0.04

resources/fr_ctc/1.4.0.04/extracted/cii/EN16931-CII-validation-preprocessed.sch

Listes de codes (famille runtime vérifiée)
10 artefact(s) codedb rattaché(s) à la famille runtime dans /meta.

Démarrer

De 0 à un premier appel vérifiable

Trois étapes pour obtenir une clé, envoyer un fichier et lire le résultat des contrôles.

  1. Votre clé Sandbox par email

    Saisissez votre email, ouvrez le lien reçu, puis confirmez l'activation. Clé Sandbox incluse : 100 factures traitées offertes les 14 premiers jours, puis 10 factures traitées/mois, sans carte bancaire.

    Créer ma clé
  2. Lancer votre premier appel

    Collez le curl ci-dessus dans un terminal. La réponse HTTP 200 indique dry_run: true et processedInvoicesCharged: 0.

    Recevoir mon lien d’activation
  3. Parcours ERP/GED /convert

    Guide dédié JSON ERP : créer une clé API, préparer les données invoice_data, choisir Factur-X PDF/A-3 ou XML CII seul, gérer les champs manquants et les limites UBL/PA.

    Ouvrir le guide /convert ERP
📦

Ressources d'intégration

Importez la spec OpenAPI dans votre outil favori (Insomnia, Bruno, Stoplight, Swagger UI…) ou la collection Postman pour démarrer avec une base prête à importer.

{}

Spécification OpenAPI 3.1

Schéma public : le fichier OpenAPI publie les routes validate, extract, convert, repair, integration-contract, meta et health. /meta et /health répondent sur l'hôte de l'API (https://api.facturxapi.com/meta, https://api.facturxapi.com/health), pas sur ce site. Les routes dashboard, billing, admin, webhooks, création de clé et gestion connectée restent hors de cette documentation.

ID

Contrat invoice_data

Contrat machine-readable pour préparer invoice_data : champs acceptés, règles EN16931 / règles françaises, mapping CII et exemples générés depuis la source de vérité API.

P

Collection Postman

Collection publique prête à l'emploi avec les endpoints d'intégration. Créez d'abord votre clé depuis la page clé API : ce parcours utilise /public/api-keys/free/step1 puis /public/api-keys/free/step2. La gestion connectée passe ensuite par /api/v1/me/api-keys, volontairement absente de cette collection publique. Ajoutez votre clé dans la variable apiKey et lancez vos premiers appels.

Télécharger la collection

Dans Postman : File → Import → Link puis coller l'URL copiée.

Tiers payeur : contrat JSON

Ajoutez payer uniquement lorsque la partie qui règle la facture diffère de l’acheteur. payer.name est requis si le bloc est présent. Vous pouvez aussi fournir son adresse postale dans postal_address — avec country — et son email de contact dans email. Cet email ne remplace pas electronic_address : ce dernier est un identifiant de routage qualifié contenant ensemble value et scheme_id, comme les identifiants optionnels global_id et legal_registration.

Ce tiers payeur correspond à EXT-FR-FE-BG-02. Il ne remplace ni le bénéficiaire payee de BG-10, ni le compte débité payment.debtor_account_iban de BT-91. Sa présence ne crée pas de montant prépayé BT-113 : envoyez totals.prepaid_amount seulement si cette donnée est réellement connue. Le profil effectif produit est EXTENDED ; vérifiez-le dans result.targetProfile.

Le champ payer est optionnel. Si votre facture relève d’une livraison intracommunautaire, le contrat peut aussi demander delivery.date et delivery.address.country ; cette exigence est indépendante du tiers payeur.

Extrait minimal du bloc payer à ajouter

{
  "payer": {
    "name": "OPCO Formation SA",
    "postal_address": {
      "street": "12 rue de la Formation",
      "city": "Paris",
      "postal_code": "75008",
      "country": "FR"
    },
    "email": "[email protected]",
    "electronic_address": {
      "value": "FRROUTEOPCO001",
      "scheme_id": "0225"
    }
  }
}

Snippets prêts par langage

PHP, Python, Node.js, Laravel, Symfony, .NET — avec retries, timeouts et codes d'erreur.

Voir les intégrations →

Introduction

L'API FacturX permet de traiter des factures électroniques au format Factur-X (PDF hybride avec XML embarqué) ou XML standalone selon les standards EN16931.

Cette page détaille la validation, l'extraction, la conversion et la réparation ; le fichier OpenAPI publie aussi le contrat d'intégration, les métadonnées et la santé du service.

  • Validate : Validation XSD + Schematron complète d'un PDF Factur-X ou XML CII
  • Extract : Extraction du XML CII embarqué dans un PDF Factur-X
  • Convert : Génération d'un Factur-X PDF/A-3 avec contrôles de sortie
  • Repair : Correction automatique déterministe d'un XML CII invalide

Qu'est-ce que Factur-X ?

Factur-X est un format de facture électronique hybride franco-allemand, également connu sous le nom de ZUGFeRD 2.x en Allemagne.

Une facture Factur-X combine deux éléments :

  • Un PDF lisible : La facture visuelle classique, imprimable et lisible par l'humain
  • Un fichier XML embarqué : Les données structurées de la facture, lisibles par les machines

Cette approche hybride permet une transition progressive vers la facturation électronique : les comptables peuvent continuer à lire les PDF, tandis que les systèmes informatiques peuvent extraire automatiquement les données.

Profils Factur-X

Factur-X définit plusieurs profils correspondant à différents niveaux de détail :

  • Minimum : Données minimales (montants, dates, parties)
  • Basic : Informations de base pour le traitement automatisé
  • EN16931 : Profil couvrant le modèle sémantique complet de la norme européenne (recommandé)
  • Extended : Champs additionnels pour besoins spécifiques

Quand le profil et les parties de la facture sont lisibles, l’API détecte le profil et applique les règles de validation correspondantes ; sinon, ni la détection ni la liste des champs manquants ne sont garanties.

Référence rapide des URN, schémas XSD et règles Schematron par profil : URN, XSD et Schematron Factur-X.

Structure d'un PDF Factur-X

Un PDF Factur-X doit contenir un XML en pièce jointe. La spécification Factur-X 1.07.2 (§6.2) fixe son nom : factur-x.xml, ou xrechnung.xml pour le profil de référence XRechnung. ZUGFeRD 2.0 nommait ce fichier zugferd-invoice.xml ; depuis ZUGFeRD 2.1 le nom prévu est aussi factur-x.xml.

Comment vérifier manuellement ?

Dans Adobe Acrobat ou un lecteur PDF compatible :

  1. Ouvrez le PDF
  2. Allez dans le panneau des pièces jointes (icône trombone)
  3. Vérifiez la présence d'un fichier .xml

Si aucun fichier XML n'est présent, il s'agit d'un PDF classique, pas d'une facture Factur-X.

Erreur "No Factur-X XML found"

Cette erreur signifie que le PDF uploadé ne contient pas de XML embarqué. Causes possibles :

  • Le PDF a été généré par un outil qui ne supporte pas Factur-X
  • Le XML a été supprimé lors d'une modification du PDF
  • Le fichier est un PDF classique, pas une facture Factur-X

Comment créer une facture Factur-X ?

Avec FacturX API : depuis vos données de facture

Envoyez un invoice_data complet (les données de facture de votre ERP, au format JSON) à POST /api/v1/convert : l’API génère le XML CII seul (output=xml_cii) ou un Factur-X PDF/A-3 avec XML embarqué (output=facturx_pdfa3, porteur visuel rendu depuis les mêmes données ou votre PDF existant). Le fichier produit est soumis aux contrôles de la cible demandée (validation_target) ; il n’obtient le verdict « Factur-X EN16931 valide » que si ces contrôles passent. Les données manquantes ne sont jamais inventées ; quand le profil et les parties sont lisibles, elles sont listées. Parcours complet : guide /convert avec JSON ERP.

D’autres outils savent aussi produire des Factur-X :

Logiciels de facturation

L'export Factur-X dépend du logiciel, de sa version et du profil choisi. Le nom de l'éditeur ne prouve pas qu'un PDF contient un XML Factur-X.

  • À contrôler dans la documentation de l'éditeur : Sage, Cegid, EBP, SAP, Oracle, QuickBooks, Xero.

Bibliothèques open-source

Pour les développeurs souhaitant générer des Factur-X programmatiquement :

Plateformes Agréées (PA)

Depuis le 1er septembre 2026, toutes les entreprises doivent être en capacité de recevoir une facture électronique. Les grandes entreprises et les entreprises de taille intermédiaire doivent émettre désormais leurs factures au format dématérialisé dès le 1er septembre 2026. Au 1er septembre 2027, les micro-entreprises, TPE et PME devront être en capacité d'émettre des factures électroniques.

Dans le cadre de la réforme française 2026-2027, les Plateformes Agréées assurent la transmission et le routage réglementaire. FacturX API prépare, valide et répare le document en amont, avant que votre ERP ou votre client ne le remette à sa PA.

Règles métier EN16931

La norme EN16931 définit plus de 200 règles métier (Business Rules) que doit respecter une facture électronique. Ces règles sont vérifiées via Schematron.

Catégories de règles

  • BR-xx : Règles de base (champs obligatoires, formats)
  • BR-CO-xx : Règles de cohérence (calculs, totaux)
  • BR-S-xx : Règles spécifiques aux régimes de TVA
  • BR-CL-xx : Règles sur les codes et listes de valeurs

Exemples de règles courantes

Code Description
BR-01 Une facture doit avoir un identifiant de spécification (BT-24)
BR-02 Une facture doit avoir un numéro de facture (BT-1)
BR-CO-10 La somme des montants HT doit correspondre au total HT
BR-CO-15 Le montant TTC doit être égal au HT + TVA
BR-S-01 Une facture contenant une ligne, une remise ou une charge de catégorie TVA S doit contenir une ventilation TVA de catégorie S

Lorsqu'une règle est violée, l'API retourne une erreur de type schematron_failed_assert avec le code de la règle concernée.

Sécurité & RGPD

Cette section résume les contrôles de sécurité et les traitements externes à connaître avant d'intégrer l'API.

Infrastructure

  • Hébergement : Cloudflare Pages (front) + Railway (API). Voir la politique de confidentialité pour la liste complète des sous-traitants et leur localisation.
  • Chiffrement en transit : TLS sur toutes les communications (HTTPS obligatoire).

Traitement des données

  • Validation et scan : Les fichiers transmis servent au traitement demandé. Selon le parcours, des copies temporaires peuvent être conservées pour le traitement, sa reprise ou l’accès au résultat ; les principales situations sont décrites dans la politique de confidentialité.
  • Espace de facturation (plans Launch, Scale et Business) : les profils émetteurs, clients, factures, lignes et références de PDF sont conservés pour créer, retrouver et justifier les factures émises.
  • Traitement limité au service demandé : L'API analyse les champs nécessaires au scan, à la validation ou à la génération, sans scoring commercial.
  • Enrichissement Sirene désactivable via l'API : Par défaut, les identifiants entreprise peuvent être vérifiés auprès de l'INSEE Sirene et de recherche-entreprises.api.gouv.fr. Le paramètre enrich=false est disponible uniquement sur les requêtes API directes ; les outils du dashboard ne proposent pas ce réglage aujourd'hui.
  • Usage des données : les données ne sont pas utilisées pour entraîner un modèle, enrichir un tiers ou revendre des informations métier.
  • Logs minimaux : Seules les métadonnées techniques sont conservées (horodatage, taille, résultat)

Les fichiers et données sont traités selon le parcours utilisé. Certaines données sont conservées pour permettre le diagnostic, la reprise ou l’accès aux résultats ; les détails figurent dans la politique de confidentialité. Lire la matrice de conservation.

Conformité

  • Transparence RGPD : bases légales, droits d'accès/export/suppression, sous-traitants confirmés et services publics interrogés sont documentés dans la politique de confidentialité.

Pour plus de détails, consultez notre politique de confidentialité.

Base URL

https://api.facturxapi.com

Toutes les routes de l'API, y compris GET /meta et GET /health, se trouvent sur cet hôte : https://api.facturxapi.com/meta et https://api.facturxapi.com/health. Le site de documentation ne les sert pas.

Authentication

Pour l’authentification par clé API, l’API accepte ces deux headers ; les exemples de cette documentation utilisent X-API-Key, comme sur la page d’accueil :

X-API-Key: YOUR_API_KEY
Authorization: Bearer YOUR_API_KEY

Si les deux headers contiennent des clés API, X-API-Key est prioritaire.

Obtenez votre clé sandbox (100 factures traitées offertes les 14 premiers jours, puis 10 factures traitées/mois).

Scan public

Le scan public disponible sur /scan est un diagnostic sans compte.

  • Le scan peut conclure que le socle Factur-X / EN16931 est exploitable tout en indiquant que les règles françaises (FNFE-MPE) demandent des compléments.
  • Les compléments listés sont actionnables via /convert uniquement si conversionInputRecipe les expose comme champs guidés supportés.
  • Un point nécessitant une vérification manuelle doit être traité séparément ; ce n’est pas une promesse de correction automatique.

Désactiver l'enrichissement Sirene

Par défaut, les endpoints publics peuvent vérifier les SIREN/SIRET et le nom d'entreprise détectés dans la facture, et signaler un écart de code postal, auprès de l'INSEE Sirene et de recherche-entreprises.api.gouv.fr. Ces vérifications ressortent en avertissements ou en informations ; elles ne tranchent pas le verdict de validation. Sur une requête API directe, ajoutez enrich=false pour garder ce flux sur un traitement hors registres publics. Les outils du dashboard ne proposent pas ce réglage aujourd'hui.

curl -X POST "https://api.facturxapi.com/api/v1/validate?enrich=false" \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "file=@./facture.xml"

curl -X POST "https://api.facturxapi.com/api/v1/extract?validate=true&enrich=false" \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "file=@./factur-x.pdf"

curl -X POST "https://api.facturxapi.com/api/v1/repair?enrich=false" \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "file=@./facture.xml"

curl -X POST "https://api.facturxapi.com/api/v1/convert?enrich=false" \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "file=@./facture.pdf"

Endpoints

Règle de décompte

Endpoint Description Factures traitées
POST /api/v1/validate Valider un PDF Factur-X ou XML CII 1 facture traitée
POST /api/v1/extract Extraire le XML embarqué d'un PDF Factur-X 1 facture traitée
POST /api/v1/convert Générer un Factur-X PDF/A-3 avec contrôles de sortie 1 par génération finale, décomptée une seule fois · 0 en simulation dry_run
POST /api/v1/repair Réparer un XML Factur-X 1 facture traitée

POST /api/v1/validate

Produit un diagnostic technique pour un PDF Factur-X ou un XML CII. Sans cible France explicite, le socle affiché est Factur-X / EN16931 (PDF/A-3 et XML embarqué pour les PDF, XSD et Schematron EN16931 pour l’XML). La vérification des règles françaises (FNFE-MPE) n’est incluse que lorsqu’elle est demandée et exécutable par le runtime.

Paramètres

  • file (multipart, requis) : Fichier PDF Factur-X ou XML CII
  • lang (query, optionnel) : Langue de la réponse (en / fr)
  • legacy (query, optionnel, défaut : false) : Retourner l'ancien format court si votre intégration historique en dépend encore
  • validation_target (query, recommandé) : en16931 pour le socle Factur-X / EN16931, france_2026 pour le socle EN16931 et les exigences des règles françaises (FNFE-MPE) disponibles
  • validation_profile, jurisdiction, require_profile (query, legacy) : contrôles historiques encore acceptés pour les intégrations existantes

La réponse inclut toujours target.requested, target.executed, target.status, target.missing_inputs et target.not_run_reason. Les contrôles des règles françaises (FNFE-MPE) utilisent uniquement le pack déclaré par le runtime servi (version 1.4.0.04 confirmée par le moteur lors de la génération de cette page), et seulement lorsqu’ils sont exécutés et listés dans appliedValidationProfiles / validationScopeLabel. Si la cible explicite n’est pas exécutable, l’API retourne 422 validation_profile_unavailable.

Décompte

1 facture traitée. Si une cible explicite n'est pas exécutable et retourne 422 validation_profile_unavailable, si le refus part avant tout débit, aucune facture traitée n'est décomptée ; s'il survient après le débit, le remboursement n'est pas garanti et la facture traitée peut rester décomptée. Contrairement à /convert, /extract et /repair, /validate ne signale pas un remboursement non confirmé (503 quota_refund_unconfirmed).

Exemple curl

curl -X POST https://api.facturxapi.com/api/v1/validate?validation_target=en16931 \
  -H "X-API-Key: votre-cle-api" \
  -F "file=@./facture.pdf"

Réponse par défaut (200)

{
  "valid": true,
  "target": {
    "requested": "en16931",
    "executed": "en16931",
    "status": "verified",
    "missing_inputs": [],
    "not_run_reason": null
  },
  "validatedAt": "2026-04-28T10:00:00.000Z",
  "filename": "invoice.xml",
  "profile": "EN16931",
  "durationMs": 124,
  "summary": {
    "errorCount": 0,
    "warningCount": 0
  },
  "issues": [],
  "stages": {
    "pdfa": {
      "passed": true,
      "skipped": true,
      "skip_reason": "XML file - PDF/A validation not applicable",
      "error_count": 0,
      "warning_count": 0,
      "info_count": 0,
      "issues": []
    },
    "facturx_pdf": {
      "passed": true,
      "skipped": true,
      "skip_reason": "XML file - PDF checks not applicable",
      "error_count": 0,
      "warning_count": 0,
      "info_count": 0,
      "issues": []
    },
    "xsd": {
      "passed": true,
      "skipped": false,
      "error_count": 0,
      "warning_count": 0,
      "info_count": 0,
      "issues": []
    },
    "schematron": {
      "passed": true,
      "skipped": false,
      "error_count": 0,
      "warning_count": 0,
      "info_count": 0,
      "issues": []
    }
  },
  "errors": [],
  "warnings": []
}

Réponse legacy (?legacy=true)

{
  "valid": true,
  "errors": [],
  "warnings": [],
  "profile": "EN16931",
  "message": "Validation completed",
  "target": {
    "requested": "en16931",
    "executed": "en16931",
    "status": "verified",
    "missing_inputs": [],
    "not_run_reason": null
  }
}

Codes d'erreur spécifiques

  • 401 authentication_required : Aucune clé API dans la requête (ni X-API-Key ni Authorization: Bearer). 401 invalid_api_key : Clé invalide ou révoquée. Corps JSON dans les deux cas : detail.error, detail.message, detail.supported_methods (méthodes d’authentification acceptées) et request_id
  • 400 : Fichier vide ou invalide
  • 415 : Format non supporté (ni PDF, ni XML)
  • 422 : Cible de validation demandée mais non exécutable

POST /api/v1/extract

Extrait le fichier XML embarqué (factur-x.xml) d'un PDF Factur-X et retourne les données structurées. Optionnellement, valide le XML extrait.

Paramètres

  • file (multipart, requis) : Fichier PDF Factur-X
  • validate (query, optionnel, défaut : false) : Ajouter une validation complète du XML extrait dans la même réponse
  • lang (query, optionnel) : Langue de la réponse (en / fr)

Décompte

1 facture traitée.

Exemple curl

curl -X POST https://api.facturxapi.com/api/v1/extract \
  -H "X-API-Key: votre-cle-api" \
  -F "file=@./facture-facturx.pdf"

Réponse (200)

{
  "xml": "PD94bWwgdmVyc2lvbj0iMS4wIj8+...",
  "xmlSize": 2048,
  "profile": "EN16931",
  "filename": "facture.pdf",
  "extractedAt": "2026-04-01T12:00:00Z",
  "durationMs": 42
}

Codes d'erreur spécifiques

  • 415 : Le fichier n'est pas un PDF
  • 422 : Aucun XML embarqué trouvé dans le PDF

POST /api/v1/convert

Produit un Factur-X PDF/A-3 avec XML CII embarqué (output=facturx_pdfa3, défaut) ou le XML CII seul (output=xml_cii), puis contrôle le fichier produit pour la cible demandée. Sources acceptées :

  • PDF de facture : l’API extrait les données du PDF, qui sert aussi de porteur visuel.
  • invoice_data complet : la source complète de la facture ERP (JSON). L’extraction du PDF est alors ignorée ; sans PDF, l’API rend le porteur depuis les mêmes données.
  • XML Factur-X CII (guideline BASIC ou EN16931), envoyé seul dans file, lorsque le déploiement de l’API active cette source (sinon 415 unsupported_media_type) : le XML est validé, repris sans inventer de valeur et le porteur est rendu depuis ces données. Il est converti tel quel : invoice_data, invoice_patch, contextes et réutilisation de profils sont refusés (422 cii_xml_source_overlay_unsupported). Un CII sans lignes (MINIMUM, BASIC WL) est refusé (422 cii_source_profile_header_only), comme ZUGFeRD 1.0 et toute autre guideline (422 cii_source_unsupported) : seuls les identifiants exacts urn:cen.eu:en16931:2017 (EN16931) et urn:cen.eu:en16931:2017#compliant#urn:factur-x.eu:1p0:basic (BASIC) sont acceptés, ce qui exclut EXTENDED, XRECHNUNG et les guidelines ZUGFeRD.
  • XML UBL Invoice ou CreditNote : le UBL doit d’abord passer les contrôles EN16931 UBL (422 ubl_source_not_valid_en16931 sinon), puis être entièrement représentable par le mapping UBL → CII strict ; un champ non représentable est refusé (422 ubl_source_mapping_unsupported) au lieu d’être perdu. output=xml_cii produit le XML CII ; un Factur-X PDF/A-3 depuis un UBL seul suppose que le déploiement active le rendu du porteur (sinon 422 ubl_pdf_carrier_required). invoice_data ne se combine pas avec un UBL (400 invalid_invoice_data_source_conflict).
  • Compléments partiels sur un document PDF : invoice_patch, seller_context, buyer_context, uniquement pour les chemins que conversionInputRecipe expose comme supportés. Ils ne remplacent jamais silencieusement une donnée visible (409 inline_context_conflict en cas de désaccord).
  • Réutilisation explicite d’un profil enregistré : avec profile_reuse=explicit et des sélecteurs limités à votre compte (issuer_id/seller_profile_id pour le vendeur, client_id/buyer_profile_id pour l’acheteur), l’API reprend l’adresse électronique et les notes de document enregistrées du vendeur, et l’adresse électronique enregistrée de l’acheteur ; l’identité et la version du profil entrent dans l’identité de la requête. Aucun profil n’est chargé silencieusement ni depuis un autre compte : sélecteur sans opt-in (422 profile_reuse_requires_explicit_opt_in), opt-in sans sélecteur (422 profile_reuse_selector_required), sélecteurs en désaccord (409 profile_selector_conflict), profil introuvable pour votre compte (404 seller_profile_not_found / 404 buyer_profile_not_found), profil en conflit avec le contexte inline (409 profile_inline_context_conflict).

Pour le parcours intégrateur PDF + JSON ERP, consultez le guide dédié /convert avec JSON ERP. Pour explorer chaque champ invoice_data (code BT/BG, règle EN16931 ou française, XPath CII) et valider un payload en direct, ouvrez le contrat d'intégration.

Choisissez toujours la cible avec validation_target. en16931 vérifie le socle Factur-X / EN16931. france_2026 inclut EN16931 plus les exigences françaises FNFE-MPE disponibles dans le runtime.

Commencez par une simulation (dry_run=true) : elle ne consomme pas le quota et ne produit pas de livrable final. Un XML candidat peut toutefois être généré. La réponse selon les règles françaises inclut alors targetPreflight. Si targetPreflight.exhaustive vaut false, utilisez targetPreflight.status, targetPreflight.exhaustive et les points bloquants structurés pour informer l'utilisateur sans présenter la liste comme complète. Les champs de targetPreflight.requiredInputs doivent être envoyés via les racines partielles indiquées par l’API : invoice_patch, seller_context et buyer_context, jamais comme un invoice_data partiel.

La génération finale selon les règles françaises doit reprendre la structure de conversionInputRecipe.examplePayload retournée par la simulation, puis remplacer toute valeur modèle par des valeurs réelles confirmées. Ne lancez la génération avec validation_target=france_2026 que si targetPreflight.exhaustive=true et si targetPreflight.unsupportedBlockers est vide ; sinon conservez le résultat comme diagnostic et affichez les points bloquants restants. Cette condition ne bloque pas la génération d’un Factur-X EN16931 valide avec validation_target=en16931 : les compléments des règles françaises restent du travail à traiter avant transmission.

Si la réponse signale une fonctionnalité API non disponible pour un champ requis, ne lancez pas la génération finale : affichez la raison structurée, conservez le diagnostic, puis reprenez uniquement quand l’API expose un chemin supporté pour ce champ via conversionInputRecipe.requiredInputs, une correction automatique documentée, ou une exclusion explicite qui retire ce champ des données attendues.

Paramètres

  • dry_run (form field, optionnel, défaut : false) : Lance une simulation sans livrable final et sans décompte de quota. Ce n’est pas une absence de génération.
  • output (form field, optionnel) : facturx_pdfa3 par défaut, ou xml_cii pour obtenir uniquement le XML CII.
  • file (multipart) : porteur PDF optionnel pour output=facturx_pdfa3. Sans fichier, un invoice_data complet permet à l’API de rendre le porteur avant le packaging PDF/A-3. Pour output=xml_cii, aucun PDF n’est produit.
  • validation_target (form field, recommandé) : en16931 pour un Factur-X EN16931, france_2026 pour le socle EN16931 et les contrôles des règles françaises (FNFE-MPE) disponibles.
  • options (form field, optionnel) : Options de conversion au format JSON
    • output_profile : Profil Factur-X cible (défaut : "EN16931")
    • confidence_mode : "strict" (défaut) ou "lenient" pour l'OCR
    • validation_target peut aussi être envoyé dans options si votre client préfère garder tous les réglages en JSON.
  • invoice_data (form field, optionnel) : Données structurées complètes de la facture au format JSON. Quand fourni, l'extraction texte du PDF est ignorée.
  • Sous le pack France, supporting_documents[].description, lorsqu’il est renseigné, doit être un code de la liste BT-123.
  • invoice_patch (form field, optionnel) : Complément facture partiel. Support actuel : preceding_invoice.issue_date pour BT-26, avec référence d'origine effective déjà détectée ou fournie de façon cohérente, et payment.iban pour BT-84 quand le compte vendeur manque au document.
  • seller_context ou seller_profile_inline (form field, optionnel) : Contexte vendeur inline non persistant pour BT-34 et les mentions BT-22 PMT/PMD/AAB.
  • buyer_context ou buyer_profile_inline (form field, optionnel) : Contexte acheteur inline non persistant pour BT-49.
  • lang (query, optionnel) : Langue de la réponse (en / fr)
  • Idempotency-Key (header, optionnel) : Clé d'idempotence pour les retries. Le hash d'identité couvre la requête complète : le fichier PDF, invoice_data, invoice_patch, seller_context, buyer_context, les sélecteurs de profils enregistrés (profile_reuse, issuer_id/client_id, seller_profile_id/buyer_profile_id), output, validation_target et les options. Rejouer la même clé avec un de ces éléments modifié retourne 409 idempotency_conflict.

invoice_patch et les contextes inline ne doivent pas écraser silencieusement une valeur visible dans le PDF/XML. En cas de désaccord document/contexte, l'API retourne 409 inline_context_conflict. invoice_patch ne se combine pas avec invoice_data complet.

Décompte

1 par génération finale, décomptée une seule fois · 0 en simulation dry_run. La simulation dry_run=true ne consomme pas le quota et ne produit pas de livrable final. Un XML candidat peut toutefois être généré. Elle accepte aussi les fichiers XML pour diagnostic, sans création d'un livrable Factur-X.

Exemple curl (mode ERP — recommandé)

curl -X POST https://api.facturxapi.com/api/v1/convert \
  -H "X-API-Key: votre-cle-api" \
  -F "file=@./facture.pdf" \
  -F "validation_target=en16931" \
  -F 'invoice_data={
  "invoice_number": "FA-2026-042",
  "issue_date": "2026-04-01",
  "invoice_type": "380",
  "currency": "EUR",
  "business_process": "S1",
  "document_notes": [
    {
      "subject_code": "PMT",
      "content": "Indemnite forfaitaire de recouvrement de 40 EUR due en cas de retard de paiement"
    },
    {
      "subject_code": "PMD",
      "content": "Penalites de retard exigibles selon les conditions contractuelles"
    },
    {
      "subject_code": "AAB",
      "content": "Aucun escompte pour paiement anticipe"
    }
  ],
  "seller": {
    "name": "Ma Societe SAS",
    "siret": "99999123500014",
    "vat_id": "FR09999991235",
    "electronic_address": {
      "value": "FRROUTESELLER001",
      "scheme_id": "0225"
    },
    "address": {
      "street": "10 rue de Rivoli",
      "city": "Paris",
      "postal_code": "75001",
      "country": "FR"
    }
  },
  "buyer": {
    "name": "Client SA",
    "siret": "99999245600015",
    "electronic_address": {
      "value": "FRROUTEBUYER001",
      "scheme_id": "0225"
    },
    "address": {
      "street": "20 rue de la Republique",
      "city": "Lyon",
      "postal_code": "69002",
      "country": "FR"
    }
  },
  "payer": {
    "name": "OPCO Formation SA",
    "postal_address": {
      "street": "12 rue de la Formation",
      "city": "Paris",
      "postal_code": "75008",
      "country": "FR"
    },
    "email": "[email protected]",
    "electronic_address": {
      "value": "FRROUTEOPCO001",
      "scheme_id": "0225"
    }
  },
  "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 curl (mode extraction texte)

curl -X POST https://api.facturxapi.com/api/v1/convert \
  -H "X-API-Key: votre-cle-api" \
  -F "validation_target=en16931" \
  -F "file=@./facture.pdf"

Exemple curl (simulation selon les règles françaises avec targetPreflight)

curl -X POST https://api.facturxapi.com/api/v1/convert \
  -H "X-API-Key: votre-cle-api" \
  -F "dry_run=true" \
  -F "validation_target=france_2026" \
  -F "file=@./facture.pdf"

Génération finale selon les règles françaises après une simulation exhaustive

Utilisez les racines et chemins indiqués par le conversionInputRecipe.examplePayload de la simulation, puis remplacez les exemples par des valeurs réelles confirmées. Les valeurs modèles inchangées sont refusées par l'API ; cet exemple montre le chemin dry_run → exemple exploitable → /convert final, pas qu’un IBAN seul suffit pour les règles françaises.

curl -X POST https://api.facturxapi.com/api/v1/convert \
  -H "X-API-Key: votre-cle-api" \
  -H "Idempotency-Key: facture-france-2026-001" \
  -F "validation_target=france_2026" \
  -F "file=@./facture.pdf" \
  -F 'invoice_patch={"payment":{"iban":"FR7630006000011234567890189"}}' \
  -F 'seller_context={"electronic_address":{"value":"FRROUTESELLER001","scheme_id":"0225"},"document_notes":[{"subject_code":"PMT","content":"Indemnite forfaitaire de recouvrement de 40 EUR due en cas de retard de paiement"},{"subject_code":"PMD","content":"Penalites de retard exigibles selon les conditions contractuelles"},{"subject_code":"AAB","content":"Aucun escompte pour paiement anticipe"}]}' \
  -F 'buyer_context={"electronic_address":{"value":"FRROUTEBUYER001","scheme_id":"0225"}}'

Exemple curl (avoir DI 0414 avec compléments des règles françaises)

curl -X POST https://api.facturxapi.com/api/v1/convert \
  -H "X-API-Key: votre-cle-api" \
  -H "Idempotency-Key: di-0414-2026-05-05-001" \
  -F "file=@./avoir-exemple-public.pdf" \
  -F "validation_target=france_2026" \
  -F 'invoice_patch={"preceding_invoice":{"issue_date":"2025-08-31"},"payment":{"iban":"FR7630006000011234567890189"}}' \
  -F 'seller_context={"electronic_address":{"value":"FRROUTESELLER001","scheme_id":"0225"},"document_notes":[{"subject_code":"PMT","content":"Indemnite forfaitaire de recouvrement de 40 EUR due en cas de retard de paiement"},{"subject_code":"PMD","content":"Penalites de retard exigibles selon les conditions contractuelles"},{"subject_code":"AAB","content":"Aucun escompte pour paiement anticipe"}]}' \
  -F 'buyer_context={"electronic_address":{"value":"FRROUTEBUYER001","scheme_id":"0225"}}'

Données inline envoyées

{
  "invoice_patch": {
    "preceding_invoice": {
      "issue_date": "2025-08-31"
    },
    "payment": {
      "iban": "FR7630006000011234567890189"
    }
  },
  "seller_context": {
    "electronic_address": {
      "value": "FRROUTESELLER001",
      "scheme_id": "0225"
    },
    "document_notes": [
      {
        "subject_code": "PMT",
        "content": "Indemnite forfaitaire de recouvrement de 40 EUR due en cas de retard de paiement"
      },
      {
        "subject_code": "PMD",
        "content": "Penalites de retard exigibles selon les conditions contractuelles"
      },
      {
        "subject_code": "AAB",
        "content": "Aucun escompte pour paiement anticipe"
      }
    ]
  },
  "buyer_context": {
    "electronic_address": {
      "value": "FRROUTEBUYER001",
      "scheme_id": "0225"
    }
  }
}

Réponse (200)

{
  "success": true,
  "target": {
    "requested": "en16931",
    "executed": "en16931",
    "status": "verified",
    "missing_inputs": [],
    "not_run_reason": null
  },
  "result": {
    "durationMs": 2500,
    "targetProfile": "EN16931",
    "conversionSuccessful": true,
    "packagingPerformed": true,
    "xml": "PD94bWwg...",
    "xmlSize": 4096,
    "pdf": "JVBERi0x...",
    "pdfSize": 102400,
    "extraction": {
      "sourceType": "native_text",
      "pageCount": 1
    },
    "validation": {
      "valid": true,
      "profile": "EN16931"
    },
    "processedInvoicesCharged": 1,
    "remainingInvoices": 99
  }
}

Suivre son quota en production

Pour afficher l'usage côté intégration sur un succès /convert, lisez les headers X-Quota-Limit, X-Quota-Remaining et X-Quota-Reset quand ils sont présents. Complétez avec result.remainingInvoices dans le corps JSON de la réponse, quand il est présent. Sur une génération finale, result.processedInvoicesCharged vaut 1 quand l'appel est décompté ; la clé est omise quand l'appel ne crée pas de nouvelle charge. Un rejeu avec la même Idempotency-Key renvoie la réponse enregistrée avec l'en-tête Idempotent-Replayed: true ; si elle portait result.processedInvoicesCharged, la clé y vaut 0. En simulation dry_run=true, le champ vaut 0. Une génération refusée sans livrable le porte à 0 dans result ; une erreur HTTP renvoyée avant la génération le porte à 0 dans detail.processedInvoicesCharged, quand il y figure.

Formats attendus dans invoice_data

  • Pays : Code ISO 3166-1 alpha-2, deux lettres : « FR », « DE ». Jamais le nom du pays (« France »). Exemple : "country": "FR". Champs : seller.address.country, buyer.address.country, delivery.address.country, payer.postal_address.country.
  • Dates : AAAA-MM-JJ (ISO 8601) : « 2026-01-15 ». Ces champs refusent JJ/MM/AAAA et une heure. payment.due_date n’a pas ce motif à l’entrée : la conversion le refuse pourtant en 422 invalid_invoice_data s’il n’est pas une date calendaire AAAA-MM-JJ (année 1900 à 2100). Exemple : "issue_date": "2026-01-15". Champs : issue_date, delivery.date, invoice_reference_issue_date, vat_point_date, invoicing_period.start_date, invoicing_period.end_date, line_items[].period.start_date, line_items[].period.end_date.
  • Facture d’origine d’un avoir (BT-25) : Le numéro exact de la facture d’origine, tel qu’il y est écrit : lettres, chiffres et + - _ /, sans espace ni phrase ; 35 caractères au plus pour les règles françaises (règles BR-FR-01 et BR-FR-02). L’API accepte jusqu’à 100 caractères en entrée. Exemple : "invoice_reference": "FA-2026-001". Champs : invoice_reference, invoice_patch.preceding_invoice.reference.

Lire un refus 422

  1. Le corps d'un refus 422 suit le schéma ApiErrorEnvelope : { "detail": { "error", "message", … }, "request_id" }, où request_id peut valoir null. Lisez detail.error : c'est le code stable (par exemple invalid_data_format ou insufficient_data) ; detail.message est le texte lisible.
  2. Cherchez le contexte dans detail : field nomme le champ en cause (exemple du contrat : "error": "invalid_data_format", "field": "issue_date") et missing_fields liste les champs manquants ("error": "insufficient_data").
  3. Corrigez l'entrée, puis envoyez-la de nouveau avec une nouvelle Idempotency-Key : un 422 ne se relance jamais à l'identique (voir Politique de relance).
  4. Pour voir tous les constats d'un document avant de générer, lancez dry_run=true : la réponse 200 porte la liste issues[], qui n'est pas dans le corps d'un 422.
  5. Gardez request_id (et l'en-tête X-Request-ID) pour le support.
  6. errors[] : le contrat ne décrit aucune liste errors[] dans le corps d'un 422 de /convert aujourd'hui ; cette page le décrira quand l'API le publiera, ne vous appuyez pas dessus. (La réponse de /validate a sa propre liste errors[], avec un autre schéma.)

Réponse avec compléments manquants

{
  "success": false,
  "dry_run": true,
  "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",
  "missing_fields": [
    "seller_context.electronic_address.value",
    "buyer_context.electronic_address.value"
  ],
  "targetPreflight": {
    "requested": "france_2026",
    "status": "incomplete",
    "exhaustive": false,
    "remainingAfterInputsPolicy": "unknown",
    "requiredInputs": [
      {
        "btCode": "BT-84",
        "ruleRef": "FX-SCH-A-000132",
        "requestContextPath": "invoice_patch.payment.iban"
      },
      {
        "btCode": "BT-34",
        "ruleRef": "BR-FR-13_BT-34",
        "requestContextPath": "seller_context.electronic_address.value"
      },
      {
        "btCode": "BT-49",
        "ruleRef": "BR-FR-12_BT-49",
        "requestContextPath": "buyer_context.electronic_address.value"
      }
    ],
    "automaticFixes": [],
    "unsupportedBlockers": []
  }
}

Codes d'erreur spécifiques

  • 400 file_required_for_pdf_output : Livrable Factur-X PDF/A-3 demandé sans PDF fourni et sans invoice_data complet permettant de rendre le porteur. Fournissez l’une de ces deux sources
  • 400 invalid_options_json : options n'est pas un JSON valide ou n'est pas un objet
  • 400 invalid_options : Combinaison invalide, par exemple options.mode="xml_only" qui n'est plus le sélecteur public du livrable (utilisez output=xml_cii)
  • 400 invalid_invoice_patch : invoice_patch combiné avec invoice_data complet, ou JSON non objet (même schéma de code pour invoice_data, seller_context, buyer_context)
  • 409 inline_context_conflict : Complément inline en conflit avec une valeur visible dans le document
  • 409 idempotency_conflict : Même Idempotency-Key avec une requête différente (fichier, invoice_data, sortie, cible ou options modifiés). Corrigez la requête ou utilisez une nouvelle clé intentionnellement
  • 409 idempotency_in_progress : même Idempotency-Key et même requête, première requête encore en cours de traitement. Attendez puis réessayez avec la même clé ; n’en créez pas une nouvelle pour forcer un second job.
  • 413 file_too_large : Fichier au-delà de la taille maximale
  • 415 unsupported_media_type : Fichier ni PDF, ni XML source accepté par ce déploiement (CII BASIC/EN16931 lorsque la source CII est activée, UBL Invoice/CreditNote)
  • 422 cii_xml_source_overlay_unsupported / cii_source_profile_header_only / cii_source_unsupported : uniquement lorsque la source CII est activée sur le déploiement — source XML CII combinée à un complément, sans lignes, ou de guideline non prise en charge
  • 400 xml_security_error : uniquement lorsque la source CII est activée sur le déploiement — XML déclarant un DOCTYPE ou des entités, refusé par sécurité
  • 422 ubl_source_not_valid_en16931 / ubl_source_mapping_unsupported / ubl_source_mapping_incomplete / ubl_pdf_carrier_required : Source UBL invalide, non représentable sans perte, incomplète, ou PDF/A-3 demandé sans rendu de porteur disponible
  • 422 invalid_invoice_data : invoice_data refusé par une règle EN16931 contrôlée par le moteur (par exemple BR-CO-14 : total de TVA différent de la somme de la ventilation par taux), ou par une règle de catégorie TVA ou de motif d'exonération — le message cite la règle BR en cause
  • 422 invalid_invoice_patch : Date impossible ou champ inline invalide
  • 422 insufficient_data : BT-26 fourni sans référence de facture d'origine effective, ou données insuffisantes pour produire la cible
  • 422 unextractable_pdf : Aucune donnée exploitable extraite du PDF (et pas d'invoice_data fourni)
  • 422 ocr_confidence_too_low : Confiance OCR insuffisante en mode strict
  • 422 unsupported_output_profile_for_invoice_data : invoice_data ne peut pas cibler MINIMUM, BASIC_WL ou BASIC — gardez EN16931 (défaut) ou EXTENDED
  • 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
  • 503 ocr_service_unavailable / 503 ocr_quota_exhausted : Service OCR indisponible ou quota OCR épuisé (header Retry-After fourni)

Les 400, 413, 415 et 422 ci-dessus ne se relancent jamais : corrigez l'entrée (voir Politique de relance).

Conversion asynchrone : suivre un job

Quand une conversion est traitée en asynchrone, POST /api/v1/convert répond 202 avec result.jobId et result.status = "processing". Le traitement asynchrone ne concerne que les PDF scannés (OCR) dont le nombre de pages atteint un seuil fixé par le déploiement, sur les déploiements qui l'activent : un envoi invoice_data ou un XML source (CII ou UBL) est toujours traité en synchrone. Cette route de suivi n'apparaît pas encore dans le fichier OpenAPI public, qui ne décrit que la réponse 202.

  • GET /api/v1/convert/jobs/{jobId} : même authentification que /convert (X-API-Key ou Authorization: Bearer). Sans clé : 401 authentication_required. Un job d'un autre compte ou inconnu : 404, avec error = job_not_found.
  • En-tête de réponse X-Convert-Job-Status : queued, running, completed, failed, canceled, expired.
  • Tant que le job est queued ou running, le corps est success: false avec result.status = "processing" et target.not_run_reason = "async_processing_not_completed" : continuez à interroger.
  • Job completed : même enveloppe que la réponse synchrone de /convert (success, target, result), avec des URL signées d'artefacts régénérées à chaque lecture. completed ne veut pas dire « vérifié » : lisez success et target.status.
  • Job failed, canceled ou expired : success: false et result.jobId. Un job canceled ou expired, un job failed sans result_json, ou un result_json qui n’est pas un objet JSON, rend result.status = "fail" avec result.error.code et result.error.message. Un job failed dont le result_json est un objet JSON ne conserve result.error que si result est lui-même un objet : les fichiers livrables en sont retirés, et result.error n’est pas réécrit avec code et message. Si result manque ou n’est pas un objet, la réponse remplace result par status "fail", error.code "job_result_malformed" et message "Stored job result is malformed". Un conflit de contexte porte error et message, sans code. Lisez result.error en entier plutôt que de supposer result.error.code.
  • Pas de Retry-After sur un 200 : l'intervalle de polling est à votre charge. Les exemples ci-dessous commencent à 2 s, doublent jusqu'à 10 s et s'arrêtent après 5 minutes : ce sont des valeurs d'exemple, pas des garanties de l'API. Le polling consomme la même limite par minute que le reste de l'API : une 429 porte Retry-After.
  • Durée de vie : le résultat d'un job est conservé au moins 1 h, 24 h par défaut selon la configuration actuelle du déploiement ; ensuite le job passe en expired. Les URL signées d'artefacts ne survivent pas au job.
  • Ne refaites pas le POST pour « relancer » un job en cours. Avec la même Idempotency-Key et la même requête, POST /api/v1/convert répond 202 avec ce jobId et result.status = "processing" quand la clé porte l’identifiant du job et qu’elle est processing, ou qu’elle est completed et que ce job correspond encore : le résultat se lit alors par la route de suivi, pas dans ce 202. Une clé completed dont le job ne correspond plus ne répond pas 202 : la réponse est un autre 409 (ou 503 si la lecture du job échoue), pas 409 idempotency_in_progress. Une même clé avec une autre requête répond 409 idempotency_conflict. Sans Idempotency-Key, un nouvel envoi asynchrone crée un nouveau job. Les relances du polling suivent la Politique de relance : seulement 429, 502, 503 et 504, en nombre borné ; 401, 404 et 500 arrêtent la boucle.
Exemple Python
import os
import time

import requests

API_KEY = os.environ['FACTURX_API_KEY']
API_URL = 'https://api.facturxapi.com'


def retry_after_seconds(response, default):
    try:
        return float(response.headers.get('Retry-After', default))
    except ValueError:
        return default


def wait_for_convert_job(job_id, max_wait_seconds=300, first_delay=2.0, max_delay=10.0, max_server_errors=3):
    """Poll GET /api/v1/convert/jobs/{job_id}. Never POST /convert again for the same file: that starts a second job."""
    deadline = time.monotonic() + max_wait_seconds
    delay = first_delay
    server_errors = 0
    while time.monotonic() < deadline:
        response = requests.get(
            f'{API_URL}/api/v1/convert/jobs/{job_id}',
            headers={'Authorization': f'Bearer {API_KEY}'},
            timeout=20,
        )
        if response.status_code == 200:
            body = response.json()
            if body['result']['status'] != 'processing':
                return body
            server_errors = 0
            wait = delay
            delay = min(delay * 2, max_delay)
        elif response.status_code == 429:
            wait = retry_after_seconds(response, delay)
        elif response.status_code in (502, 503, 504):
            server_errors += 1
            if server_errors > max_server_errors:
                raise RuntimeError(f'Job {job_id}: HTTP {response.status_code} after {max_server_errors} retries')
            wait = delay
        else:
            # 401, 404 job_not_found, 500 and every other 4xx: no retry. Read "error" and keep the request id.
            raise RuntimeError(f'Job {job_id}: HTTP {response.status_code} {response.text}')
        if time.monotonic() + wait >= deadline:
            break
        time.sleep(wait)
    raise TimeoutError(f'Job {job_id} still processing after {max_wait_seconds} s')


# accepted = response.json() after a 202 from POST /api/v1/convert
job_id = accepted['result']['jobId']
final = wait_for_convert_job(job_id)
if final['result']['status'] == 'fail':
    print('Job failed, canceled or expired:', final['result']['error'])
elif not final.get('success'):
    print('Not verified:', final['target'])
else:
    print('Done:', final['result'].get('pdfUrl') or final['result'].get('xmlUrl'))
Exemple Node.js (20+)
const API_KEY = process.env.FACTURX_API_KEY;
const API_URL = 'https://api.facturxapi.com';
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

function retryAfterMs(response, fallbackMs) {
  const seconds = Number(response.headers.get('Retry-After'));
  return Number.isFinite(seconds) && seconds > 0 ? seconds * 1000 : fallbackMs;
}

// Poll GET /api/v1/convert/jobs/{jobId}. Never POST /convert again for the same file: that starts a second job.
async function waitForConvertJob(jobId, { maxWaitMs = 300_000, firstDelayMs = 2_000, maxDelayMs = 10_000, maxServerErrors = 3 } = {}) {
  const deadline = Date.now() + maxWaitMs;
  let delayMs = firstDelayMs;
  let serverErrors = 0;
  while (Date.now() < deadline) {
    const response = await fetch(API_URL + '/api/v1/convert/jobs/' + jobId, {
      headers: { Authorization: 'Bearer ' + API_KEY },
      signal: AbortSignal.timeout(20_000),
    });
    let waitMs;
    if (response.status === 200) {
      const body = await response.json();
      if (body.result.status !== 'processing') return body;
      serverErrors = 0;
      waitMs = delayMs;
      delayMs = Math.min(delayMs * 2, maxDelayMs);
    } else if (response.status === 429) {
      waitMs = retryAfterMs(response, delayMs);
    } else if ([502, 503, 504].includes(response.status)) {
      serverErrors += 1;
      if (serverErrors > maxServerErrors) throw new Error('Job ' + jobId + ': HTTP ' + response.status + ' after ' + maxServerErrors + ' retries');
      waitMs = delayMs;
    } else {
      // 401, 404 job_not_found, 500 and every other 4xx: no retry. Read "error" and keep the request id.
      throw new Error('Job ' + jobId + ': HTTP ' + response.status + ' ' + (await response.text()));
    }
    if (Date.now() + waitMs >= deadline) break;
    await sleep(waitMs);
  }
  throw new Error('Job ' + jobId + ' still processing after ' + maxWaitMs / 1000 + ' s');
}

// accepted = await response.json() after a 202 from POST /api/v1/convert
const jobId = accepted.result.jobId;
const final = await waitForConvertJob(jobId);
if (final.result.status === 'fail') {
  console.log('Job failed, canceled or expired:', final.result.error);
} else if (!final.success) {
  console.log('Not verified:', final.target);
} else {
  console.log('Done:', final.result.pdfUrl ?? final.result.xmlUrl);
}

POST /api/v1/repair

Analyse un fichier XML CII et applique uniquement les corrections déterministes couvertes (format de date, séparateur décimal, namespaces, codes d’unité UN/ECE, casse devise, schemeID manquants lorsqu’une valeur prise en charge peut être déduite sans ambiguïté). Ne recalcule ni les totaux ni la TVA et n’invente aucune donnée métier. Retourne le XML obtenu (repaired_xml), le diff détaillé et la validation avant/après. repairSuccessful vaut true seulement si au moins une correction a été appliquée et si la cible demandée est vérifiée après correction (targetVerified) ; sinon la réponse reste un diagnostic, et la présence de repaired_xml ne prouve pas la conformité. Dans l’outil Web, le téléchargement du XML n’est proposé que lorsque la cible est vérifiée.

Paramètres

  • file (multipart, requis) : Fichier XML CII à corriger
  • constraints (form field, optionnel) : Contraintes de réparation au format JSON
    • max_changes_allowed : Nombre max de modifications (0 à 1 000)
    • do_not_modify_amounts : Interdit toute modification de montant (booléen, défaut : true). Si false, seules les normalisations décimales déterministes couvertes peuvent modifier les montants ; aucun total métier n’est recalculé.
    • do_not_invent_missing_parties : Interdit tout ajout de partie absente (booléen, défaut : true). Aucune donnée métier manquante n’est déduite ou créée.
  • target_profile (query, optionnel) : Profil Factur-X cible
  • lang (query, optionnel) : Langue de la réponse (en / fr)
  • Idempotency-Key (header, optionnel) : Clé d'idempotence pour les retries

Décompte

1 facture traitée.

Exemple curl

curl -X POST https://api.facturxapi.com/api/v1/repair \
  -H "X-API-Key: votre-cle-api" \
  -F "file=@./facture.xml"

Réponse (200)

{
  "durationMs": 124,
  "repaired_xml": "PD94bWwg...",
  "target": {
    "requested": "en16931",
    "executed": "en16931",
    "status": "verified",
    "missing_inputs": [],
    "not_run_reason": null
  },
  "repairSuccessful": true,
  "diff_summary": [
    {
      "code": "format_corrected",
      "path": "/rsm:CrossIndustryInvoice/.../ram:IssueDateTime",
      "message": "Corrected date format from '2024-01-15' to '20240115'",
      "ruleReference": "R001"
    }
  ],
  "validation_before": {
    "valid": false,
    "target": {
      "requested": "en16931",
      "executed": "en16931",
      "status": "failed",
      "missing_inputs": [],
      "not_run_reason": null
    },
    "summary": {
      "errorCount": 2,
      "warningCount": 0
    }
  },
  "validation_after": {
    "valid": true,
    "target": {
      "requested": "en16931",
      "executed": "en16931",
      "status": "verified",
      "missing_inputs": [],
      "not_run_reason": null
    },
    "summary": {
      "errorCount": 0,
      "warningCount": 0
    }
  }
}

Codes d'erreur spécifiques

  • 401 authentication_required : Aucune clé API dans la requête (ni X-API-Key ni Authorization: Bearer). 401 invalid_api_key : Clé invalide ou révoquée. Corps JSON dans les deux cas : detail.error, detail.message, detail.supported_methods (méthodes d’authentification acceptées) et request_id
  • 415 : Le fichier n'est pas du XML
  • 422 : Erreur de parsing XML ou contrainte de réparation dépassée

Format de réponse

Chaque endpoint retourne un objet JSON. Les formats de réponse spécifiques sont documentés dans chaque section d'endpoint ci-dessus. Pour /validate, le format par défaut est le rapport unifié multi-étapes.

Validation — format unifié par défaut (200 OK)

La réponse détaille le verdict global, le temps de traitement, le résumé, les étapes PDF/A-3, Factur-X PDF, XSD et Schematron, ainsi que le périmètre exécuté. Le champ message n'existe que dans le mode legacy.

{
  "valid": true,
  "target": {
    "requested": "en16931",
    "executed": "en16931",
    "status": "verified",
    "missing_inputs": [],
    "not_run_reason": null
  },
  "validatedAt": "2026-04-28T10:00:00.000Z",
  "filename": "invoice.xml",
  "profile": "EN16931",
  "durationMs": 124,
  "summary": {
    "errorCount": 0,
    "warningCount": 0
  },
  "issues": [],
  "stages": {
    "pdfa": {
      "passed": true,
      "skipped": true,
      "skip_reason": "XML file - PDF/A validation not applicable",
      "error_count": 0,
      "warning_count": 0,
      "info_count": 0,
      "issues": []
    },
    "facturx_pdf": {
      "passed": true,
      "skipped": true,
      "skip_reason": "XML file - PDF checks not applicable",
      "error_count": 0,
      "warning_count": 0,
      "info_count": 0,
      "issues": []
    },
    "xsd": {
      "passed": true,
      "skipped": false,
      "error_count": 0,
      "warning_count": 0,
      "info_count": 0,
      "issues": []
    },
    "schematron": {
      "passed": true,
      "skipped": false,
      "error_count": 0,
      "warning_count": 0,
      "info_count": 0,
      "issues": []
    }
  },
  "errors": [],
  "warnings": []
}

Champs

  • valid (boolean) : true si aucune erreur n’est détectée dans le périmètre exécuté, false sinon
  • validatedAt (string) : Horodatage ISO du diagnostic
  • filename (string|null) : Nom du fichier analysé
  • durationMs (number) : Durée du traitement
  • summary (object) : Compteurs errorCount et warningCount
  • issues (array) : Liste plate des erreurs, avertissements et informations
  • stages (object) : Résultat détaillé par étape de validation
  • validationScopeLabel / appliedValidationProfiles : périmètre réellement exécuté (EN16931 seul ou règles françaises (FNFE-MPE) si demandé et disponible)
  • skippedValidationProfiles / unsupportedValidationProfiles : cible demandée mais non exécutée ou non supportée
  • legalDisclaimer : mention à afficher aux utilisateurs; le diagnostic ne vaut pas acceptation PA
  • profile (string|null) : Profil Factur-X détecté (EN16931, Basic, Minimum, Extended)
  • errors / warnings (array) : Champs de compatibilité pour les anciens clients

Format court legacy (?legacy=true)

{
  "valid": true,
  "errors": [],
  "warnings": [],
  "profile": "EN16931",
  "message": "Validation completed",
  "target": {
    "requested": "en16931",
    "executed": "en16931",
    "status": "verified",
    "missing_inputs": [],
    "not_run_reason": null
  }
}

Structure d'une issue

{
  "code": "BR-CO-10",
  "severity": "error",
  "stage": "schematron",
  "message": "Sum of invoice line net amounts must equal invoice total net amount.",
  "line": 42,
  "location": "line 42",
  "ruleId": "BR-CO-10",
  "xpath": "/rsm:CrossIndustryInvoice/.../ram:SpecifiedTradeSettlementHeaderMonetarySummation"
}

Types d'erreurs

  • stage: "pdfa" : conformité PDF/A-3 du conteneur
  • stage: "facturx_pdf" : présence et cohérence des métadonnées Factur-X dans le PDF
  • stage: "xsd" : structure XML CII
  • stage: "schematron" : règles métier EN16931 et profil Factur-X

Exemples de code

curl -X POST https://api.facturxapi.com/api/v1/validate \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "file=@./facture.pdf"

Codes d'erreur HTTP

Code Description
200 Requête traitée avec succès (le résultat peut indiquer des erreurs de validation)
400 Fichier vide ou invalide (Empty file)
401 Clé API manquante ou invalide
413 Fichier trop volumineux (file_too_large)
415 Format de fichier non supporté (PDF attendu pour extract, XML pour repair ; sources de convert : voir POST /api/v1/convert)
422 Contenu non traitable (XML absent, données insuffisantes, confiance OCR trop basse, contrainte de réparation dépassée)
402 Quota mensuel de factures atteint (quota_exceeded)
429 Rate limit par minute (rate_limit_exceeded) : 60 requêtes par minute sur cette API, selon GET /meta (voir Limite par minute)
500 Erreur serveur (stockage, traitement)
503 Service temporairement indisponible (moteur de validation, OCR ou génération du PDF/A-3)

Limites

Quota mensuel

Le quota mensuel dépend de l'offre choisie. En public, retenez surtout : Sandbox = 100 factures traitées offertes les 14 premiers jours, puis 10 factures traitées/mois, Launch = 100 factures traitées par mois, Scale = 500 factures traitées par mois, Business = 2 000 factures traitées par mois ; sur-volume et contrat sur devis. Pilote accompagné se qualifie au cas par cas.

Comprendre le quota mensuel

Règle de décompte

POST /api/v1/validate 1 facture traitée Diagnostiquer une facture Factur-X déjà produite
POST /api/v1/extract 1 facture traitée Extraire le XML CII embarqué dans un PDF Factur-X
POST /api/v1/repair 1 facture traitée Réparer un XML CII invalide avant transmission
POST /api/v1/convert 1 par génération finale, décomptée une seule fois · 0 en simulation dry_run Générer un Factur-X PDF/A-3 avec XML embarqué et contrôles de sortie

Le validateur public reste gratuit et séparé du quota API.

Limite par minute

60 requêtes par minute sur https://api.facturxapi.com : valeur lue dans GET https://api.facturxapi.com/meta (limits.rate_limit_requests_per_minute) lors de la génération de cette page. La limite dépend de l'environnement : relisez /meta de l'API que vous appelez. Au-delà, l'API répond 429 rate_limit_exceeded avec l'en-tête Retry-After.

Taille fichier

Taille maximale par défaut : 10 MB

Timeout

Sur /convert, un délai dépassé est signalé par une erreur 504 (OCR ou génération du PDF). Prévoyez un timeout client large pour cet appel.

Bonnes pratiques

  • Toujours vérifier le champ valid avant de traiter la réponse
  • Logger les erreurs de type schematron_failed_assert pour analyse métier
  • Gérer les timeouts et les erreurs réseau (429, 502, 503, 504, timeout) avec un nombre de relances borné
  • Ne pas stocker la clé API en dur dans le code (variables d'environnement)
  • Relancer avec un backoff exponentiel seulement dans les cas de la politique de relance ci-dessous

Politique de relance

  • Ne relancez pas à l’identique un 400, 413, 415 ou 422 : selon la stratégie publiée pour le code, corrigez l’entrée et envoyez la requête corrigée avec une nouvelle Idempotency-Key, ou contactez le support avec le request_id (par exemple request_failed).
  • Seuls 429 (en attendant le délai du header Retry-After), 502, 503 et 504 peuvent se relancer, et pas tous : relancez seulement si la stratégie publiée pour le code le prévoit, avec un nombre de relances borné (par exemple 3) et la même Idempotency-Key pour la même requête. Cas particulier, 409 idempotency_in_progress (même requête, même clé, première requête encore en cours de traitement) : attendez puis relancez avec la même clé ; ne changez pas de clé, cela lancerait une seconde opération. D’autres codes 409 ont leur propre stratégie dans x-facturx-convert-error-registry.
  • 402 : ce n’est pas une relance. Pour quota_exceeded (quota mensuel atteint), le renouvellement du quota ou un changement de plan le lève ; les autres 402 ont leur stratégie dans x-facturx-convert-error-registry.
  • 500 : pas de relance automatique. Conservez le request_id et contactez le support.
  • La stratégie de chaque code est publiée dans le fichier OpenAPI, extension x-facturx-convert-error-registry de POST /api/v1/convert : relancer avec la même clé, attendre Retry-After, corriger la requête, passer en output=xml_cii, suivre le job, contacter le support avec le request_id, ou quota remboursé. Un 503 ou un 504 ne se relance pas toujours : certains codes demandent de contacter le support, parfois après le délai Retry-After.

Support

Besoin d'aide ? Contactez-nous :

Prêt à intégrer l'API ?

Obtenez votre clé API gratuitement et commencez à appeler le moteur avec un quota clair en factures traitées.

Obtenir une clé API