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.
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 :
- Ouvrez le PDF
- Allez dans le panneau des pièces jointes (icône trombone)
- 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 :
- Python : factur-x (Akretion)
- Java : Mustang Project
- PHP : horstoeko/zugferd
- .NET : ZUGFeRD-csharp
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=falseest 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_KEYAuthorization: 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
/convertuniquement siconversionInputRecipeles 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
| 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 CIIlang(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 encorevalidation_target(query, recommandé) :en16931pour le socle Factur-X / EN16931,france_2026pour le socle EN16931 et les exigences des règles françaises (FNFE-MPE) disponiblesvalidation_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 (niX-API-KeyniAuthorization: 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) etrequest_id400: Fichier vide ou invalide415: 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-Xvalidate(query, optionnel, défaut :false) : Ajouter une validation complète du XML extrait dans la même réponselang(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 PDF422: 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_datacomplet : 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 (sinon415 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 exactsurn:cen.eu:en16931:2017(EN16931) eturn: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_en16931sinon), 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_ciiproduit le XML CII ; un Factur-X PDF/A-3 depuis un UBL seul suppose que le déploiement active le rendu du porteur (sinon422 ubl_pdf_carrier_required).invoice_datane 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 queconversionInputRecipeexpose comme supportés. Ils ne remplacent jamais silencieusement une donnée visible (409 inline_context_conflicten cas de désaccord). - Réutilisation explicite d’un profil enregistré : avec
profile_reuse=explicitet des sélecteurs limités à votre compte (issuer_id/seller_profile_idpour le vendeur,client_id/buyer_profile_idpour 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_pdfa3par défaut, ouxml_ciipour obtenir uniquement le XML CII.file(multipart) : porteur PDF optionnel pouroutput=facturx_pdfa3. Sans fichier, uninvoice_datacomplet permet à l’API de rendre le porteur avant le packaging PDF/A-3. Pouroutput=xml_cii, aucun PDF n’est produit.validation_target(form field, recommandé) :en16931pour un Factur-X EN16931,france_2026pour le socle EN16931 et les contrôles des règles françaises (FNFE-MPE) disponibles.options(form field, optionnel) : Options de conversion au format JSONoutput_profile: Profil Factur-X cible (défaut :"EN16931")confidence_mode:"strict"(défaut) ou"lenient"pour l'OCRvalidation_targetpeut aussi être envoyé dansoptionssi 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_datepour BT-26, avec référence d'origine effective déjà détectée ou fournie de façon cohérente, etpayment.ibanpour BT-84 quand le compte vendeur manque au document.seller_contextouseller_profile_inline(form field, optionnel) : Contexte vendeur inline non persistant pour BT-34 et les mentions BT-22 PMT/PMD/AAB.buyer_contextoubuyer_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_targetet les options. Rejouer la même clé avec un de ces éléments modifié retourne409 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
- Le corps d'un refus
422suit le schémaApiErrorEnvelope:{ "detail": { "error", "message", … }, "request_id" }, oùrequest_idpeut valoirnull. Lisezdetail.error: c'est le code stable (par exempleinvalid_data_formatouinsufficient_data) ;detail.messageest le texte lisible. - Cherchez le contexte dans
detail:fieldnomme le champ en cause (exemple du contrat :"error": "invalid_data_format", "field": "issue_date") etmissing_fieldsliste les champs manquants ("error": "insufficient_data"). - Corrigez l'entrée, puis envoyez-la de nouveau avec une nouvelle
Idempotency-Key: un422ne se relance jamais à l'identique (voir Politique de relance). - Pour voir tous les constats d'un document avant de générer, lancez
dry_run=true: la réponse200porte la listeissues[], qui n'est pas dans le corps d'un422. - Gardez
request_id(et l'en-têteX-Request-ID) pour le support. errors[]: le contrat ne décrit aucune listeerrors[]dans le corps d'un422de/convertaujourd'hui ; cette page le décrira quand l'API le publiera, ne vous appuyez pas dessus. (La réponse de/validatea sa propre listeerrors[], 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 sansinvoice_datacomplet permettant de rendre le porteur. Fournissez l’une de ces deux sources400 invalid_options_json:optionsn'est pas un JSON valide ou n'est pas un objet400 invalid_options: Combinaison invalide, par exempleoptions.mode="xml_only"qui n'est plus le sélecteur public du livrable (utilisezoutput=xml_cii)400 invalid_invoice_patch:invoice_patchcombiné avecinvoice_datacomplet, ou JSON non objet (même schéma de code pourinvoice_data,seller_context,buyer_context)409 inline_context_conflict: Complément inline en conflit avec une valeur visible dans le document409 idempotency_conflict: MêmeIdempotency-Keyavec une requête différente (fichier,invoice_data, sortie, cible ou options modifiés). Corrigez la requête ou utilisez une nouvelle clé intentionnellement409 idempotency_in_progress: mêmeIdempotency-Keyet 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 maximale415 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 charge400 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 disponible422 invalid_invoice_data:invoice_datarefusé 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 cause422 invalid_invoice_patch: Date impossible ou champ inline invalide422 insufficient_data: BT-26 fourni sans référence de facture d'origine effective, ou données insuffisantes pour produire la cible422 unextractable_pdf: Aucune donnée exploitable extraite du PDF (et pas d'invoice_datafourni)422 ocr_confidence_too_low: Confiance OCR insuffisante en modestrict422 unsupported_output_profile_for_invoice_data:invoice_datane peut pas ciblerMINIMUM,BASIC_WLouBASIC— gardezEN16931(défaut) ouEXTENDED503 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 silencieux503 ocr_service_unavailable/503 ocr_quota_exhausted: Service OCR indisponible ou quota OCR épuisé (headerRetry-Afterfourni)
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-KeyouAuthorization: Bearer). Sans clé :401 authentication_required. Un job d'un autre compte ou inconnu :404, avecerror=job_not_found.- En-tête de réponse
X-Convert-Job-Status:queued,running,completed,failed,canceled,expired. - Tant que le job est
queuedourunning, le corps estsuccess: falseavecresult.status = "processing"ettarget.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.completedne veut pas dire « vérifié » : lisezsuccessettarget.status. - Job
failed,canceledouexpired:success: falseetresult.jobId. Un jobcanceledouexpired, un jobfailedsansresult_json, ou unresult_jsonqui n’est pas un objet JSON, rendresult.status = "fail"avecresult.error.codeetresult.error.message. Un jobfaileddont leresult_jsonest un objet JSON ne conserveresult.errorque siresultest lui-même un objet : les fichiers livrables en sont retirés, etresult.errorn’est pas réécrit aveccodeetmessage. Siresultmanque ou n’est pas un objet, la réponse remplaceresultparstatus"fail",error.code"job_result_malformed"etmessage"Stored job result is malformed". Un conflit de contexte porteerroretmessage, sanscode. Lisezresult.erroren entier plutôt que de supposerresult.error.code. - Pas de
Retry-Aftersur un200: 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 : une429porteRetry-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
POSTpour « relancer » un job en cours. Avec la mêmeIdempotency-Keyet la même requête,POST /api/v1/convertrépond202avec cejobIdetresult.status = "processing"quand la clé porte l’identifiant du job et qu’elle estprocessing, ou qu’elle estcompletedet que ce job correspond encore : le résultat se lit alors par la route de suivi, pas dans ce202. Une clécompleteddont le job ne correspond plus ne répond pas202: la réponse est un autre409(ou503si la lecture du job échoue), pas409 idempotency_in_progress. Une même clé avec une autre requête répond409 idempotency_conflict. SansIdempotency-Key, un nouvel envoi asynchrone crée un nouveau job. Les relances du polling suivent la Politique de relance : seulement429,502,503et504, en nombre borné ;401,404et500arrê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 à corrigerconstraints(form field, optionnel) : Contraintes de réparation au format JSONmax_changes_allowed: Nombre max de modifications (0 à 1 000)do_not_modify_amounts: Interdit toute modification de montant (booléen, défaut :true). Sifalse, 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 ciblelang(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 (niX-API-KeyniAuthorization: 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) etrequest_id415: Le fichier n'est pas du XML422: 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) :truesi aucune erreur n’est détectée dans le périmètre exécuté,falsesinonvalidatedAt(string) : Horodatage ISO du diagnosticfilename(string|null) : Nom du fichier analysédurationMs(number) : Durée du traitementsummary(object) : CompteurserrorCountetwarningCountissues(array) : Liste plate des erreurs, avertissements et informationsstages(object) : Résultat détaillé par étape de validationvalidationScopeLabel/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éelegalDisclaimer: mention à afficher aux utilisateurs; le diagnostic ne vaut pas acceptation PAprofile(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 conteneurstage: "facturx_pdf": présence et cohérence des métadonnées Factur-X dans le PDFstage: "xsd": structure XML CIIstage: "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
| Fonction API | Factures traitées | Quand l'utiliser |
|---|---|---|
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
validavant de traiter la réponse - Logger les erreurs de type
schematron_failed_assertpour 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 :
- Email : [email protected]
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