Un PDF de facture classique ne suffit plus. La réforme de facturation électronique impose que les factures B2B échangées via les PDP soient au format normé — Factur-X, UBL ou CII. Pour les millions de factures PDF qui circulent encore sans XML structuré, la question technique est concrète : comment convertir un PDF existant en document Factur-X conforme ?
Ce guide se place dans le contexte de Factur-X 1.08 / ZUGFeRD 2.4, version applicable depuis le 15 janvier 2026, basée sur UN/CEFACT CII D22B. Il couvre les deux approches de conversion, leurs cas d’usage, et leur implémentation via l’API FacturX. Le contexte réglementaire complet est traité dans Facturation électronique 2026 : le guide technique complet pour développeurs.
Pourquoi un PDF classique ne suffit pas
Un PDF “normal” est un document visuel. Il contient du texte rendu dans une mise en page, mais aucune donnée structurée exploitable par une machine. Un destinataire humain peut le lire — un ERP ne peut pas l’intégrer automatiquement sans ressaisie ou OCR.
Factur-X résout ce problème avec un format hybride : un PDF lisible par l’humain et un XML CII embarqué lisible par la machine, le tout dans un conteneur PDF/A-3 conforme à l’archivage long terme.
Les trois éléments manquants dans un PDF classique :
- Le XML CII (
factur-x.xml) — les données structurées de la facture au format UN/CEFACT Cross-Industry Invoice - Le conteneur PDF/A-3 — conformité ISO 19005-3 avec polices embarquées, profil ICC, métadonnées XMP
- Les métadonnées Factur-X —
AFRelationshipcohérent avec la relation PDF/XML (Data,SourceouAlternativeselon le cas —Alternativeétant le plus courant dans le contexte Factur-X), namespacefx:dans le XMP, GuidelineID correspondant au profil
Construire ce pipeline soi-même — Ghostscript pour le PDF/A-3, une bibliothèque XML pour le CII, l’embedding avec les bons attributs, la validation veraPDF — est faisable mais fragile. Les pièges sont documentés dans PDF/A-3 pour Factur-X : checklist de conformité et pièges courants.
Anatomie d’un PDF Factur-X conforme
Avant de convertir, il faut comprendre ce qu’on produit. Un PDF Factur-X valide est un fichier qui passe quatre niveaux de validation :
1. PDF/A-3 (ISO 19005-3) — Le conteneur est conforme : polices embarquées, profil ICC dans OutputIntents, pas de JavaScript ni d’actions Launch, métadonnées XMP avec pdfaid:part=3.
2. Structure Factur-X — Un fichier factur-x.xml est présent dans le name tree EmbeddedFiles du catalogue PDF, avec un AFRelationship approprié (typiquement /Alternative pour Factur-X) et le type MIME text/xml.
3. XSD — Le XML embarqué est structurellement valide par rapport au schéma CII D22B (CrossIndustryInvoice_100pD22B.xsd).
4. Schematron — Le XML respecte les règles métier EN16931 : totaux arithmétiquement cohérents, codes devise ISO 4217, champs obligatoires présents selon le profil déclaré.
Le GuidelineID dans le XML doit correspondre au profil déclaré. Pour le profil EN16931 (recommandé) : urn:cen.eu:en16931:2017. Pour la liste complète des profils et leurs GuidelineIDs, voir Profils Factur-X : MINIMUM, BASIC, EN16931, EXTENDED.
Les deux modes de conversion
L’API FacturX propose deux approches distinctes pour convertir un PDF en Factur-X, selon la source des données.
Mode 1 : extraction automatique depuis le PDF
Le moteur lit le texte natif du PDF et identifie les champs de la facture : numéro, date d’émission, montants HT/TVA/TTC, informations vendeur et acheteur, lignes de facturation.
Si le PDF est scanné (image sans couche texte), un moteur OCR prend le relais automatiquement. Un score de confiance est calculé pour chaque champ extrait — le seuil par défaut est 70% en mode strict.
Cas d’usage :
- Factures fournisseurs reçues en PDF classique
- Numérisation de l’existant (archives PDF)
- Prototypage rapide sans intégration ERP
Limites :
- La qualité de l’extraction dépend de la mise en page du PDF
- Les layouts exotiques (tableaux imbriqués, multi-colonnes) peuvent produire des résultats incomplets
- Le mode OCR consomme plus de quota (5 à 15 unités selon le nombre de pages)
Mode 2 : données structurées depuis l’ERP (recommandé)
L’ERP fournit les données comptables au format JSON via le champ invoice_data. Le PDF reste le support visuel — les données structurées JSON sont la source de vérité pour générer le XML CII.
Cas d’usage :
- Intégration ERP (Sage, SAP, Odoo, Dolibarr)
- Flux B2B automatisés en production
- Toute situation où les données sont déjà structurées dans le système source
Avantages :
- Déterministe : pas d’extraction aléatoire, pas d’OCR
- Coût fixe : 5 unités par conversion, quelle que soit la complexité du PDF
- Fiabilité maximale : les données viennent directement de la source comptable
Exemple pratique : mode ERP
Le mode ERP est l’approche recommandée pour la production. L’appel API envoie le PDF et les données structurées en parallèle.
curl
curl -X POST https://api.facturxapi.com/api/v1/convert \
-H "X-API-Key: votre-cle-api" \
-F "file=@facture.pdf" \
-F 'invoice_data={
"invoiceNumber": "FA-2026-042",
"issueDate": "20260401",
"seller": {
"name": "Ma Société SAS",
"vatId": "FR12345678901",
"address": {
"street": "10 Rue de Rivoli",
"city": "Paris",
"postalCode": "75001",
"country": "FR"
}
},
"buyer": {
"name": "Client SA",
"address": { "country": "FR" }
},
"currencyCode": "EUR",
"lines": [
{
"description": "Prestation conseil",
"quantity": 1,
"unitPrice": 1000,
"vatRate": 20,
"vatCategory": "S"
}
],
"totals": {
"netAmount": 1000,
"vatAmount": 200,
"grossAmount": 1200,
"dueAmount": 1200
}
}'
La réponse contient le PDF/A-3 et le XML CII en base64, avec la validation intégrée :
{
"durationMs": 2500,
"targetProfile": "EN16931",
"conversionSuccessful": true,
"xml": "PD94bWwg...",
"xmlSize": 4096,
"pdf": "JVBERi0x...",
"pdfSize": 102400,
"extraction": {
"sourceType": "structured_data",
"pageCount": 1
},
"validation": {
"valid": true,
"profile": "EN16931",
"summary": { "errorCount": 0, "warningCount": 0 }
}
}
Python
import requests
import json
import base64
API_KEY = "votre-cle-api"
API_URL = "https://api.facturxapi.com/api/v1/convert"
invoice_data = {
"invoiceNumber": "FA-2026-042",
"issueDate": "20260401",
"seller": {
"name": "Ma Société SAS",
"vatId": "FR12345678901",
"address": {
"street": "10 Rue de Rivoli",
"city": "Paris",
"postalCode": "75001",
"country": "FR",
},
},
"buyer": {
"name": "Client SA",
"address": {"country": "FR"},
},
"currencyCode": "EUR",
"lines": [
{
"description": "Prestation conseil",
"quantity": 1,
"unitPrice": 1000,
"vatRate": 20,
"vatCategory": "S",
}
],
"totals": {
"netAmount": 1000,
"vatAmount": 200,
"grossAmount": 1200,
"dueAmount": 1200,
},
}
with open("facture.pdf", "rb") as f:
response = requests.post(
API_URL,
headers={"X-API-Key": API_KEY},
files={"file": ("facture.pdf", f, "application/pdf")},
data={"invoice_data": json.dumps(invoice_data)},
timeout=90,
)
response.raise_for_status()
result = response.json()
if result["conversionSuccessful"]:
# Sauvegarder le PDF Factur-X
pdf_bytes = base64.b64decode(result["pdf"])
with open("facture-facturx.pdf", "wb") as out:
out.write(pdf_bytes)
# Sauvegarder le XML CII
xml_bytes = base64.b64decode(result["xml"])
with open("factur-x.xml", "wb") as out:
out.write(xml_bytes)
profile = result["validation"]["profile"]
valid = result["validation"]["valid"]
print(f"Conversion réussie — profil {profile}, valide: {valid}")
else:
print("Conversion échouée")
Exemple pratique : mode extraction PDF
Quand les données structurées ne sont pas disponibles, l’API extrait les informations directement depuis le texte du PDF.
curl -X POST https://api.facturxapi.com/api/v1/convert \
-H "X-API-Key: votre-cle-api" \
-F "file=@facture.pdf"
La réponse indique la méthode d’extraction utilisée :
{
"durationMs": 4200,
"targetProfile": "EN16931",
"conversionSuccessful": true,
"xml": "PD94bWwg...",
"xmlSize": 3584,
"pdf": "JVBERi0x...",
"extraction": {
"sourceType": "native_text",
"pageCount": 1,
"extractionQualityScore": 0.92,
"extractionFieldsCount": 18
},
"validation": {
"valid": true,
"profile": "EN16931"
}
}
Le champ extraction.sourceType indique comment les données ont été obtenues :
native_text— le PDF contient une couche texte exploitableocr— le PDF est une image, le moteur OCR a été utiliséstructured_data— les données viennent du champinvoice_data(mode ERP)
Le champ extractionQualityScore (0 à 1) donne une indication globale de la fiabilité de l’extraction. En mode strict (défaut), une confiance inférieure à 0.70 provoque un rejet 422 avec le code ocr_confidence_too_low.
Validation intégrée
Chaque conversion inclut automatiquement une validation complète du document produit. Les quatre étapes sont exécutées dans l’ordre :
- PDF/A-3 — conformité ISO 19005-3 via veraPDF
- Structure Factur-X — présence du XML embarqué avec les bons attributs
- XSD — validation structurelle du XML CII
- Schematron — validation des règles métier EN16931
Si la validation échoue, l’API retourne un code 422 avec le détail des erreurs dans le champ validation. Pas besoin d’appeler l’endpoint /validate séparément — la conversion garantit que le document produit est conforme ou signale explicitement pourquoi il ne l’est pas.
Pour comprendre les erreurs Schematron et les déboguer, voir Valider EN16931/Factur-X : XSD vs Schematron, erreurs BR-*.
Coût et quotas
Chaque conversion consomme des unités de quota selon le mode utilisé :
| Mode | Coût |
|---|---|
| Texte natif (extraction PDF) | 5 unités |
| OCR (PDF scanné) | 5 à 15 unités (selon le nombre de pages) |
ERP (invoice_data) | 5 unités |
Le plan Free (10 unités/mois) permet de tester. Les quotas et tarifs détaillés sont sur la page pricing.
Gestion des erreurs
Les erreurs de conversion retournent un code HTTP avec un payload JSON structuré :
| Code HTTP | Erreur | Cause |
|---|---|---|
415 | Unsupported Media Type | Le fichier envoyé n’est pas un PDF |
422 | insufficient_data | Le texte extrait ne contient pas assez d’informations pour construire un XML CII valide |
422 | ocr_confidence_too_low | Le score de confiance OCR est inférieur au seuil (mode strict) |
402 | quota_exceeded | Quota mensuel épuisé |
429 | rate_limit_exceeded | Trop de requêtes simultanées |
503 | Service Unavailable | Service OCR temporairement indisponible |
Exemple de réponse 422 :
{
"error": "insufficient_data",
"message": "Cannot extract enough invoice fields from PDF text",
"missing_fields": ["seller.vatId", "buyer.name"],
"diagnostics": [
{
"field": "seller.vatId",
"reason": "No VAT number pattern found",
"suggestion": "Provide invoice_data with seller.vatId"
}
]
}
La réponse diagnostics indique exactement quels champs manquent et suggère d’utiliser le mode ERP (invoice_data) pour les fournir explicitement. C’est le cas le plus fréquent : un PDF dont la mise en page ne permet pas d’extraire tous les champs obligatoires EN16931.
Idempotence et retries
En production, les appels réseau échouent. Un timeout, une déconnexion, une erreur 503 transitoire — le client doit pouvoir réessayer sans risque de double traitement.
L’API supporte le header Idempotency-Key :
curl -X POST https://api.facturxapi.com/api/v1/convert \
-H "X-API-Key: votre-cle-api" \
-H "Idempotency-Key: fa-2026-042-convert-v1" \
-F "file=@facture.pdf" \
-F 'invoice_data={...}'
Si le serveur a déjà traité une requête avec la même clé d’idempotence, il retourne le résultat en cache au lieu de relancer la conversion. La clé doit être unique par opération logique — typiquement le numéro de facture combiné avec un suffixe de version.
Le pattern recommandé en production — la clé doit être stable pour la même opération logique (même facture, même payload) afin que les retries retournent le résultat en cache :
# Clé stable par facture — même clé à chaque retry = idempotence garantie
idempotency_key = f"convert-{invoice_number}-v1"
response = requests.post(
API_URL,
headers={
"X-API-Key": API_KEY,
"Idempotency-Key": idempotency_key,
},
files={"file": ("facture.pdf", pdf_file, "application/pdf")},
data={"invoice_data": json.dumps(invoice_data)},
timeout=90,
)
Si le payload métier change réellement (correction de montant, ajout d’une ligne), incrémentez la version : convert-FA-2026-042-v2.
Aller plus loin
La conversion est une étape du pipeline. Pour une intégration complète :
- Validation indépendante — Si vous recevez des Factur-X de tiers, validez-les avec l’endpoint
/validateavant intégration. Voir Valider EN16931/Factur-X : XSD vs Schematron. - Extraction XML — Pour extraire le XML d’un Factur-X reçu et l’intégrer dans votre ERP, voir Extraire l’XML d’un PDF Factur-X reçu.
- Mapping des champs ERP — Pour construire le JSON
invoice_datadepuis les champs de votre ERP, la cartographie des Business Terms est dans Champs obligatoires EN16931 : mapping ERP vers XML. - Documentation API — La documentation complète couvre tous les endpoints, formats de réponse et exemples de code.
La conversion produit un document Factur-X prêt à être transmis via une PDP. Le pipeline complet — génération, validation, transmission — est décrit dans le guide technique 2026.