1. Obtenir une clé
Ajoutez Authorization: Bearer ... à chaque appel. Vous pouvez créer une clé depuis /api-key.
Intégration ERP/GED
Vos données de facture existent déjà dans votre ERP ou votre GED. Avec un invoice_data complet, output=facturx_pdfa3 peut produire un Factur-X PDF/A-3 sans PDF fourni : l’API rend le porteur visuel depuis ces données. Si votre ERP dispose déjà d’un PDF, vous pouvez aussi le fournir comme porteur. output=xml_cii reste le livrable XML CII seul.
Contrat affiché pour l’API publique actuelle. Si une valeur ou la forme d’un champ invoice_data listé ici est refusée avec 422 invalid_invoice_data, corrigez la valeur nommée dans l’erreur et vérifiez la version exposée par /meta. Pour un autre code 422, utilisez la section Erreurs fréquentes : profil de sortie, extraction PDF et UBL ont des remédiations séparées. En cas de doute, contactez le support avec le champ, le code retourné et la version API appelée.
Ajoutez Authorization: Bearer ... à chaque appel. Vous pouvez créer une clé depuis /api-key.
Avec un invoice_data complet, les deux sorties fonctionnent sans PDF fourni : l’API rend le porteur pour le Factur-X PDF/A-3, tandis que output=xml_cii retourne uniquement le XML. Un PDF existant reste un porteur alternatif.
verified prouve la cible demandée. incomplete peut contenir un fichier généré : stockez-le, affichez ses réserves et ne le transmettez pas avant leur traitement. Rejetez blocked.
Un résultat /convert reste une production documentaire. Il ne soumet pas la facture et ne garantit pas une acceptation par une Plateforme Agréée.
output=xml_cii. Avec un invoice_data complet, le PDF source est optionnel. Ce livrable ne doit pas être présenté comme un Factur-X.output=facturx_pdfa3, ou omettez output puisque c’est la sortie par défaut.ubl_source_mapping_unsupported ou un détail unsupportedFields relève d’une source UBL ou de son mapping, pas d’un champ invoice_data de ce guide.La clé API s'envoie en header X-API-Key (recommandé pour une clé statique ERP) ou, à l'identique, en Authorization: Bearer <clé> — les deux fonctionnent. Définissez votre clé API dans FACTURX_API_KEY, puis gardez la même clé d’idempotence uniquement pour rejouer la même requête complète. Utilisez une nouvelle clé si le fichier, le JSON, la sortie, la cible de validation, les options, un complément inline ou un sélecteur de profil enregistré (profile_reuse, issuer_id/client_id, seller_profile_id/buyer_profile_id) changent. Comportement vérifié en production : une requête différente sous la même clé est refusée en 409 idempotency_conflict sans exécution ni consommation de quota ; une clé rejouée pendant le traitement répond 409 idempotency_in_progress — attendez la fin du premier appel plutôt que de rejouer en boucle.
Pour un flux ERP sans conteneur PDF, demandez explicitement output=xml_cii avec un invoice_data complet. Le résultat est un XML CII, pas un document Factur-X.
curl -X POST https://api.facturxapi.com/api/v1/convert \
-H "Authorization: Bearer $FACTURX_API_KEY" \
-H "Idempotency-Key: erp-fa-2026-042-xml-v1" \
-F "output=xml_cii" \
-F "validation_target=en16931" \
-F 'invoice_data={
"invoice_number": "FA-2026-042",
"issue_date": "2026-04-01",
"invoice_type": "380",
"currency": "EUR",
"seller": {
"name": "Ma Societe SAS",
"siret": "10000000900017",
"vat_id": "FR88100000009",
"address": {
"street": "10 rue de Rivoli",
"city": "Paris",
"postal_code": "75001",
"country": "FR"
}
},
"buyer": {
"name": "Client SA",
"siret": "10000001700010",
"address": {
"street": "20 rue de la Republique",
"city": "Lyon",
"postal_code": "69002",
"country": "FR"
}
},
"references": {
"buyer_reference": "SERVICE-ACHATS",
"purchase_order_reference": "PO-2026-0017",
"contract_reference": "CTR-2026-04"
},
"totals": {
"net": "970.00",
"tax": "194.00",
"gross": "1164.00",
"prepaid_amount": "100.00",
"rounding_amount": "0.00",
"due": "1064.00"
},
"tax_breakdown": [
{
"rate": "20.00",
"category": "S",
"base": "970.00",
"amount": "194.00"
}
],
"line_items": [
{
"number": "1",
"description": "Prestation conseil",
"quantity": "10",
"unit": "C62",
"unit_price": "100.00",
"net_amount": "970.00",
"vat_rate": "20.00",
"vat_category": "S",
"purchase_order_line_reference": "10",
"gross_unit_price": "120.00",
"price_discount": "20.00",
"allowances": [
{
"amount": "50.00",
"reason": "Remise ligne",
"reason_code": "95"
}
],
"charges": [
{
"amount": "20.00",
"reason": "Supplement urgent"
}
]
}
],
"payment": {
"due_date": "2026-05-01",
"terms": "Paiement a 30 jours",
"iban": "FR7630006000011234567890189"
},
"delivery": {
"date": "2026-04-01",
"address": {
"street": "20 rue de la Republique",
"city": "Lyon",
"postal_code": "69002",
"country": "FR"
}
},
"invoicing_period": {
"start_date": "2026-04-01",
"end_date": "2026-04-30"
}
}' Avec un invoice_data complet, /convert peut générer le Factur-X PDF/A-3 sans fichier PDF fourni : l’API rend le porteur visuel à partir des mêmes données. Si votre ERP dispose déjà d’un PDF, vous pouvez aussi le fournir comme porteur. Le PDF produit contient le XML CII embarqué. Pour un flux avec PDF existant, vous pouvez aussi essayer la conversion depuis l’outil en ligne /convert.
curl -X POST https://api.facturxapi.com/api/v1/convert \
-H "Authorization: Bearer $FACTURX_API_KEY" \
-H "Idempotency-Key: erp-fa-2026-042-v1" \
-F "output=facturx_pdfa3" \
-F "validation_target=en16931" \
-F 'invoice_data={
"invoice_number": "FA-2026-042",
"issue_date": "2026-04-01",
"invoice_type": "380",
"currency": "EUR",
"seller": {
"name": "Ma Societe SAS",
"siret": "10000000900017",
"vat_id": "FR88100000009",
"address": {
"street": "10 rue de Rivoli",
"city": "Paris",
"postal_code": "75001",
"country": "FR"
}
},
"buyer": {
"name": "Client SA",
"siret": "10000001700010",
"address": {
"street": "20 rue de la Republique",
"city": "Lyon",
"postal_code": "69002",
"country": "FR"
}
},
"references": {
"buyer_reference": "SERVICE-ACHATS",
"purchase_order_reference": "PO-2026-0017",
"contract_reference": "CTR-2026-04"
},
"totals": {
"net": "970.00",
"tax": "194.00",
"gross": "1164.00",
"prepaid_amount": "100.00",
"rounding_amount": "0.00",
"due": "1064.00"
},
"tax_breakdown": [
{
"rate": "20.00",
"category": "S",
"base": "970.00",
"amount": "194.00"
}
],
"line_items": [
{
"number": "1",
"description": "Prestation conseil",
"quantity": "10",
"unit": "C62",
"unit_price": "100.00",
"net_amount": "970.00",
"vat_rate": "20.00",
"vat_category": "S",
"purchase_order_line_reference": "10",
"gross_unit_price": "120.00",
"price_discount": "20.00",
"allowances": [
{
"amount": "50.00",
"reason": "Remise ligne",
"reason_code": "95"
}
],
"charges": [
{
"amount": "20.00",
"reason": "Supplement urgent"
}
]
}
],
"payment": {
"due_date": "2026-05-01",
"terms": "Paiement a 30 jours",
"iban": "FR7630006000011234567890189"
},
"delivery": {
"date": "2026-04-01",
"address": {
"street": "20 rue de la Republique",
"city": "Lyon",
"postal_code": "69002",
"country": "FR"
}
},
"invoicing_period": {
"start_date": "2026-04-01",
"end_date": "2026-04-30"
}
}' Le code ci-dessous illustre l’appel HTTP et vérifie, avant le renommage atomique, que la taille reçue correspond à result.pdfSize ou à la taille annoncée par le serveur de téléchargement. Ce contrôle porte sur l’intégrité du transfert, pas sur toute la structure interne du PDF. Votre intégration doit construire invoiceData depuis votre ERP.
import { rename, rm, writeFile } from "node:fs/promises";
const MAX_PDF_BYTES = 10 * 1024 * 1024;
const MAX_BASE64_PDF_CHARACTERS = Math.ceil(MAX_PDF_BYTES / 3) * 4;
async function downloadPdf(urlValue) {
const url = new URL(urlValue);
if (url.protocol !== "https:" || url.username || url.password) {
throw new Error("L'URL de téléchargement du PDF doit être une URL HTTPS sans identifiants");
}
const downloadResponse = await fetch(url, { redirect: "error" });
if (!downloadResponse.ok) {
throw new Error(`Le téléchargement du PDF a échoué (HTTP ${downloadResponse.status})`);
}
const contentLength = downloadResponse.headers.get("content-length");
if (contentLength === null) {
throw new Error("Le serveur de téléchargement n'annonce pas la taille du PDF");
}
const announcedBytes = Number(contentLength);
if (!Number.isSafeInteger(announcedBytes) || announcedBytes <= 0 || announcedBytes > MAX_PDF_BYTES) {
throw new Error("La taille annoncée du PDF est invalide ou dépasse 10 Mo");
}
if (!downloadResponse.body) {
throw new Error("Le téléchargement du PDF ne contient aucun corps de réponse");
}
const chunks = [];
let receivedBytes = 0;
const reader = downloadResponse.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
receivedBytes += value.byteLength;
if (receivedBytes > MAX_PDF_BYTES) {
await reader.cancel();
throw new Error("Le PDF téléchargé dépasse 10 Mo");
}
chunks.push(Buffer.from(value));
}
return { pdfBytes: Buffer.concat(chunks, receivedBytes), expectedBytes: announcedBytes };
}
function decodeInlinePdf(encodedPdf) {
const strictBase64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
if (encodedPdf.length % 4 !== 0 || !strictBase64.test(encodedPdf)) {
throw new Error("Le PDF encodé n'est pas un base64 canonique");
}
const decodedPdf = Buffer.from(encodedPdf, "base64");
if (decodedPdf.toString("base64") !== encodedPdf) {
throw new Error("Le PDF encodé n'est pas un base64 canonique");
}
return decodedPdf;
}
function assertPdfIntegrity(pdfBytes, expectedBytes) {
const header = pdfBytes.subarray(0, 8).toString("ascii");
if (
!Number.isSafeInteger(expectedBytes) ||
expectedBytes <= 0 ||
expectedBytes > MAX_PDF_BYTES ||
pdfBytes.length !== expectedBytes ||
!/^%PDF-(?:1\.[0-7]|2\.0)$/.test(header)
) {
throw new Error("La signature ou la taille du PDF ne correspond pas au livrable annoncé");
}
}
const invoiceData = {
"invoice_number": "FA-2026-042",
"issue_date": "2026-04-01",
"invoice_type": "380",
"currency": "EUR",
"seller": {
"name": "Ma Societe SAS",
"siret": "10000000900017",
"vat_id": "FR88100000009",
"address": {
"street": "10 rue de Rivoli",
"city": "Paris",
"postal_code": "75001",
"country": "FR"
}
},
"buyer": {
"name": "Client SA",
"siret": "10000001700010",
"address": {
"street": "20 rue de la Republique",
"city": "Lyon",
"postal_code": "69002",
"country": "FR"
}
},
"references": {
"buyer_reference": "SERVICE-ACHATS",
"purchase_order_reference": "PO-2026-0017",
"contract_reference": "CTR-2026-04"
},
"totals": {
"net": "970.00",
"tax": "194.00",
"gross": "1164.00",
"prepaid_amount": "100.00",
"rounding_amount": "0.00",
"due": "1064.00"
},
"tax_breakdown": [
{
"rate": "20.00",
"category": "S",
"base": "970.00",
"amount": "194.00"
}
],
"line_items": [
{
"number": "1",
"description": "Prestation conseil",
"quantity": "10",
"unit": "C62",
"unit_price": "100.00",
"net_amount": "970.00",
"vat_rate": "20.00",
"vat_category": "S",
"purchase_order_line_reference": "10",
"gross_unit_price": "120.00",
"price_discount": "20.00",
"allowances": [
{
"amount": "50.00",
"reason": "Remise ligne",
"reason_code": "95"
}
],
"charges": [
{
"amount": "20.00",
"reason": "Supplement urgent"
}
]
}
],
"payment": {
"due_date": "2026-05-01",
"terms": "Paiement a 30 jours",
"iban": "FR7630006000011234567890189"
},
"delivery": {
"date": "2026-04-01",
"address": {
"street": "20 rue de la Republique",
"city": "Lyon",
"postal_code": "69002",
"country": "FR"
}
},
"invoicing_period": {
"start_date": "2026-04-01",
"end_date": "2026-04-30"
}
};
const form = new FormData();
form.set("output", "facturx_pdfa3");
form.set("validation_target", "en16931");
form.set("invoice_data", JSON.stringify(invoiceData));
const response = await fetch("https://api.facturxapi.com/api/v1/convert", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.FACTURX_API_KEY}`,
"Idempotency-Key": "erp-fa-2026-042-v1",
},
body: form,
});
const responseBody = await response.json();
if (!response.ok) {
throw new Error(JSON.stringify(responseBody));
}
const targetStatus = responseBody.target?.status;
const result = responseBody.result;
if (!["verified", "incomplete"].includes(targetStatus)) {
throw new Error(JSON.stringify(responseBody));
}
const hasInlinePdf = typeof result?.pdf === "string" && result.pdf.length > 0;
const hasRemotePdf = typeof result?.pdfUrl === "string" && result.pdfUrl.length > 0;
if (result?.packagingPerformed !== true || hasInlinePdf === hasRemotePdf) {
throw new Error("Le PDF Factur-X n'est pas disponible dans cette réponse");
}
if (hasInlinePdf && result.pdf.length > MAX_BASE64_PDF_CHARACTERS) {
throw new Error("Le PDF encodé dépasse 10 Mo");
}
const finalPath = "./facture-facturx.pdf";
const temporaryPath = `${finalPath}.tmp-${process.pid}`;
const { pdfBytes, expectedBytes } = hasInlinePdf
? { pdfBytes: decodeInlinePdf(result.pdf), expectedBytes: result.pdfSize }
: await downloadPdf(result.pdfUrl);
assertPdfIntegrity(pdfBytes, expectedBytes);
try {
await writeFile(temporaryPath, pdfBytes, { flag: "wx" });
await rename(temporaryPath, finalPath);
} finally {
await rm(temporaryPath, { force: true });
}
if (targetStatus === "incomplete") {
const reservations = result.validationReadiness?.frCtc?.advisories ?? responseBody.warnings ?? [];
console.warn("Fichier généré avec réserves — à traiter avant transmission", reservations);
}
Exemple synthétique pour invoice_data. Il couvre les zones les plus utiles pour un premier test ERP/GED : références, totaux de paiement, remises/charges, ventilation TVA, livraison, période de facturation et tarification ligne.
{
"invoice_number": "FA-2026-042",
"issue_date": "2026-04-01",
"invoice_type": "380",
"currency": "EUR",
"seller": {
"name": "Ma Societe SAS",
"siret": "10000000900017",
"vat_id": "FR88100000009",
"address": {
"street": "10 rue de Rivoli",
"city": "Paris",
"postal_code": "75001",
"country": "FR"
}
},
"buyer": {
"name": "Client SA",
"siret": "10000001700010",
"address": {
"street": "20 rue de la Republique",
"city": "Lyon",
"postal_code": "69002",
"country": "FR"
}
},
"references": {
"buyer_reference": "SERVICE-ACHATS",
"purchase_order_reference": "PO-2026-0017",
"contract_reference": "CTR-2026-04"
},
"totals": {
"net": "970.00",
"tax": "194.00",
"gross": "1164.00",
"prepaid_amount": "100.00",
"rounding_amount": "0.00",
"due": "1064.00"
},
"tax_breakdown": [
{
"rate": "20.00",
"category": "S",
"base": "970.00",
"amount": "194.00"
}
],
"line_items": [
{
"number": "1",
"description": "Prestation conseil",
"quantity": "10",
"unit": "C62",
"unit_price": "100.00",
"net_amount": "970.00",
"vat_rate": "20.00",
"vat_category": "S",
"purchase_order_line_reference": "10",
"gross_unit_price": "120.00",
"price_discount": "20.00",
"allowances": [
{
"amount": "50.00",
"reason": "Remise ligne",
"reason_code": "95"
}
],
"charges": [
{
"amount": "20.00",
"reason": "Supplement urgent"
}
]
}
],
"payment": {
"due_date": "2026-05-01",
"terms": "Paiement a 30 jours",
"iban": "FR7630006000011234567890189"
},
"delivery": {
"date": "2026-04-01",
"address": {
"street": "20 rue de la Republique",
"city": "Lyon",
"postal_code": "69002",
"country": "FR"
}
},
"invoicing_period": {
"start_date": "2026-04-01",
"end_date": "2026-04-30"
}
} invoice_dataPour explorer chaque champ avec sa règle EN16931 / France 2026, son XPath CII et un exemple, et pour valider un JSON en direct, ouvrez le contrat d'intégration interactif.
Pour une source ERP complète, envoyez les adresses électroniques, les notes et la référence de facture précédente directement dans invoice_data avec les chemins confirmés ci-dessus. Les racines ci-dessous servent surtout à compléter un PDF ou XML analysé ; les champs réellement non supportés sont refusés explicitement plutôt qu’ignorés.
Quand un diagnostic ou un scan demande de compléter un document analysé, reprenez uniquement les racines indiquées : invoice_patch, seller_context ou buyer_context. invoice_patch ne peut pas être combiné avec un invoice_data complet ; pour un flux ERP complet, préférez les champs directs de la matrice.
Les valeurs entre chevrons sont des champs à renseigner, pas des valeurs par défaut. Pour le schéma 0225, utilisez l’identifiant de routage confirmé par votre contexte métier ou par le diagnostic ; ne recopiez pas automatiquement un SIRET.
{
"invoice_patch": {
"preceding_invoice": {
"issue_date": "2025-08-31"
},
"payment": {
"iban": "FR7630006000011234567890189"
}
},
"seller_context": {
"electronic_address": {
"value": "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"
}
}
}
Pour output=xml_cii, utilisez result.xml. Le PDF est absent et packagingPerformed=false ; cette absence est normale pour ce livrable.
{
"success": true,
"target": {
"requested": "en16931",
"executed": "en16931",
"status": "verified",
"missing_inputs": [],
"not_run_reason": null
},
"result": {
"targetProfile": "EN16931",
"conversionSuccessful": true,
"xml": "PD94bWwgdmVyc2lvbj0iMS4wIj8+...",
"xmlSize": 4096,
"pdf": null,
"pdfUrl": null,
"packagingPerformed": false,
"validation": {
"valid": true,
"profile": "EN16931"
},
"processedInvoicesCharged": 1,
"remainingInvoices": 999
}
} target.status=verified indique que les contrôles de la cible demandée ont réussi. Vérifiez aussi packagingPerformed=true et la présence de result.pdf avant de stocker le fichier.
{
"success": true,
"target": {
"requested": "en16931",
"executed": "en16931",
"status": "verified",
"missing_inputs": [],
"not_run_reason": null
},
"result": {
"targetProfile": "EN16931",
"conversionSuccessful": true,
"xml": "PD94bWwgdmVyc2lvbj0iMS4wIj8+...",
"xmlSize": 4096,
"pdf": "JVBERi0xLjQKJc...",
"pdfSize": 102400,
"packagingPerformed": true,
"validation": {
"valid": true,
"profile": "EN16931"
},
"processedInvoicesCharged": 1,
"remainingInvoices": 999
}
} target.status=incomplete ne prouve pas la cible complète, même si le PDF et le XML sont livrés et décomptés du quota. Stockez le fichier, conservez les réserves de validationReadiness avec lui, puis bloquez sa transmission tant qu'elles ne sont pas traitées.
{
"success": false,
"target": {
"requested": "france_2026",
"executed": "france_2026",
"status": "incomplete",
"missing_inputs": [],
"not_run_reason": null
},
"result": {
"targetProfile": "EN16931",
"conversionSuccessful": false,
"xml": "PD94bWwgdmVyc2lvbj0iMS4wIj8+...",
"pdf": "JVBERi0xLjQKJc...",
"packagingPerformed": true,
"validationReadiness": {
"frCtc": {
"status": "incomplete",
"blockers": [],
"advisories": [
"BR-FR-05_BT-22_PMT"
]
}
},
"processedInvoicesCharged": 1,
"remainingInvoices": 998
}
} target.status=blocked ne doit créer aucun fichier final. Affichez les champs retournés avant un nouvel appel ; une liste non exhaustive ne doit pas être présentée comme une checklist finale.
{
"success": false,
"target": {
"requested": "france_2026",
"executed": null,
"status": "blocked",
"missing_inputs": [
"seller_context.electronic_address.value",
"buyer_context.electronic_address.value"
],
"not_run_reason": "missing_inputs"
},
"verdict": "convert_with_inputs",
"targetPreflight": {
"requested": "france_2026",
"status": "incomplete",
"exhaustive": false,
"requiredInputs": [
{
"btCode": "BT-34",
"requestContextPath": "seller_context.electronic_address.value"
},
{
"btCode": "BT-49",
"requestContextPath": "buyer_context.electronic_address.value"
}
],
"unsupportedBlockers": []
}
}
Quand /convert produit un livrable, y compris avec target.status=incomplete, lisez les headers X-Quota-Limit, X-Quota-Remaining et X-Quota-Reset quand ils sont présents. Le corps de réponse expose aussi result.processedInvoicesCharged et result.remainingInvoices : utilisez ces champs pour afficher le coût réel de l'appel et le solde connu après traitement.
401 : clé absente, invalide ou révoquée. Reprenez la clé depuis /api-key ou /dashboard/keys.400 file_required_for_pdf_output : vous demandez le livrable PDF/A-3 sans PDF fourni et sans invoice_data complet permettant de rendre le porteur. Fournissez l’une de ces deux sources.400 invalid_options : options.mode="xml_only" n’est pas le sélecteur public du livrable final. Utilisez output=xml_cii.400 invalid_invoice_patch : invoice_patch combiné avec un invoice_data complet, ou JSON de patch invalide.409 inline_context_conflict : une valeur inline contredit une valeur visible dans le document.409 idempotency_conflict : même Idempotency-Key rejouée avec une requête différente (fichier, invoice_data, sortie, cible ou options modifiés). Action : nouvelle clé. La requête rejouée n'est pas exécutée et ne consomme pas de quota (vérifié en production).409 idempotency_in_progress : même clé rejouée pendant qu'une opération est en cours. Action : attendre puis re-vérifier côté ERP ; ne changez pas de clé pour « forcer » un doublon.413 file_too_large : fichier au-delà de la taille maximale (10 Mo).415 unsupported_media_type : fichier non PDF pour le flux Factur-X PDF/A-3.422 insufficient_data : données insuffisantes pour produire la cible demandée.422 invalid_invoice_data : arithmétique ligne incohérente, remise de prix sans prix brut, catégorie TVA O/K/G/L/M incohérente, motif d’exonération ambigu en mode multi-catégories, buyer.tax_registration_id non supporté, ou règle EN16931 refusée explicitement.422 unsupported_output_profile_for_invoice_data : invoice_data ne peut pas cibler MINIMUM, BASIC_WL ou BASIC. Gardez le profil EN16931 par défaut ou utilisez EXTENDED si votre contrat le prévoit.422 unextractable_pdf : aucune donnée exploitable extraite du PDF, sans invoice_data pour compenser.503 requested_output_unavailable : le PDF/A-3 demandé n’a pas pu être produit ; l’API échoue explicitement (quota remboursé) au lieu de retourner un succès XML-only silencieux.422 ocr_confidence_too_low : confiance OCR insuffisante en mode strict. Action : fournir invoice_data (recommandé) ou options.confidence_mode="lenient".503 ocr_service_unavailable / 503 ocr_quota_exhausted : OCR indisponible ou quota OCR épuisé — header Retry-After fourni. Action : rejouer après le délai, même clé d'idempotence. Sans objet en mode invoice_data complet (pas d'OCR).402 quota_exceeded : quota mensuel de factures traitées atteint (définitif jusqu'au renouvellement ou upgrade — pas de Retry-After). Le header X-Quota-Used peut indiquer l'usage courant au moment du refus ; il ne signifie pas que cet appel refusé a traité une facture.429 : rate limit par minute — header Retry-After fourni. Action : backoff puis rejouer à l'identique (même clé d'idempotence).
En résumé pour votre gestionnaire d'erreurs : les 4xx hors 429 et hors 409 idempotency_in_progress sont des erreurs de requête à corriger avant un nouvel appel (nouvelle clé d'idempotence) ; 409 idempotency_in_progress est le seul cas où il faut attendre sur la clé d'origine — en changer relancerait une seconde conversion ; 429 et les 503 se rejouent à l'identique après le délai ; 503 requested_output_unavailable rembourse le quota et se rejoue tel quel.
Pour les erreurs métier EN16931 (règles BR-*) remontées par la validation, la bibliothèque d’erreurs détaille cause et correction règle par règle.
Facture 20 % + 5,5 % avec une remise BG-20 portée par son taux : une entrée tax_breakdown par couple (catégorie, taux), bases exactes par groupe (BR-S-08). Exemple exécuté et vérifié contre la production.
{
"invoice_number": "FA-2026-118",
"issue_date": "2026-06-11",
"seller": {
"name": "Smoke Editeur SAS",
"address": {
"country": "FR",
"street": "10 rue de la Paix",
"city": "Paris",
"postal_code": "75002"
},
"vat_id": "FR80421347006",
"siret": "42134700600080"
},
"buyer": {
"name": "Smoke Client SARL",
"address": {
"country": "FR",
"street": "5 avenue des Champs",
"city": "Lyon",
"postal_code": "69002"
},
"vat_id": "FR40303265045",
"siret": "30326504500017"
},
"totals": {
"net": "73.33",
"tax": "7.42",
"gross": "80.75"
},
"tax_breakdown": [
{
"rate": "20",
"category": "S",
"base": "23.33",
"amount": "4.67"
},
{
"rate": "5.5",
"category": "S",
"base": "50.00",
"amount": "2.75"
}
],
"line_items": [
{
"number": "1",
"description": "Prestation 20%",
"quantity": "1",
"unit_price": "33.33",
"net_amount": "33.33",
"vat_rate": "20"
},
{
"number": "2",
"description": "Prestation 5.5%",
"quantity": "1",
"unit_price": "50.00",
"net_amount": "50.00",
"vat_rate": "5.5"
}
],
"allowances": [
{
"amount": "10.00",
"reason": "Remise",
"vat_category": "S",
"vat_rate": "20"
}
],
"payment": {
"due_date": "2026-07-11",
"terms": "30 jours"
}
} Pour valider votre mapping sans générer de livrable ni consommer de quota, ajoutez dry_run=true : la réponse liste les champs manquants (targetPreflight, et convertPreparation.fieldsToProvide quand des champs guidés sont attendus) avant votre premier appel final.
invoice_data complet ; si votre flux possède déjà un PDF de facture, gardez aussi un porteur testable sans donnée client sensible si vous partagez l’exemple.invoice_data est un champ multipart de type chaîne contenant le JSON sérialisé (pas un body JSON) — en shell, préférez -F "invoice_data=$(cat facture.json)" ou --form-string pour éviter les surprises d'échappement."970.00", "20.00") — un montant sub-centime est refusé en 422 ; quantity et unit_price peuvent porter plus de décimales.invoice_data complet, avec output=xml_cii, sans PDF.invoice_data complet sans PDF fourni, ou en utilisant un PDF existant comme porteur, avec output=facturx_pdfa3.validation_target=france_2026) et exécutables — sinon la réponse dit explicitement pourquoi la cible n’a pas tourné (not_run_reason).dry_run=true qui liste les champs manquants sans consommer de quota.Vous branchez des clients finaux sur ce flux ? Le parcours intégrateurs décrit où placer scan, conversion et validation dans votre architecture.
Oui. Demandez output=xml_cii avec un invoice_data complet : le PDF est optionnel pour ce livrable. La réponse contient le XML dans result.xml, avec packagingPerformed=false puisqu’aucun PDF/A-3 n’est produit.
Le XML CII seul, non. Un Factur-X est un PDF/A-3 lisible avec le XML CII embarqué. Avec un invoice_data complet, /convert peut générer le Factur-X PDF/A-3 sans fichier PDF fourni : l’API rend le porteur visuel à partir des mêmes données. Si votre ERP dispose déjà d’un PDF, vous pouvez aussi le fournir comme porteur.
Non. /convert produit un document et exécute les contrôles demandés : validation EN16931, ou contrôles France 2026 / FNFE-MPE quand ils sont demandés et exécutables. Le dépôt, la transmission et l’acceptation relèvent du parcours PA, séparé de cette API.
Non. /convert ne produit pas d’UBL aujourd’hui. Les deux sorties publiques sont le XML CII (output=xml_cii) et le Factur-X PDF/A-3 (output=facturx_pdfa3).
422 insufficient_data : il manque des champs pour la cible demandée — complétez invoice_data avec les chemins listés dans la réponse (missing_inputs, targetPreflight). 422 invalid_invoice_data : une valeur est incohérente (arithmétique de ligne, ventilation TVA, champ refusé) — corrigez la valeur nommée dans l’erreur. Testez avec dry_run=true pour itérer sans consommer de quota.
Et après ?
Choisissez l'étape suivante selon votre besoin. Certains liens ouvrent une autre surface ou préparent un email lorsque c'est indiqué.
Créez une clé et testez le premier appel depuis votre environnement.
Relisez les paramètres, réponses et codes erreur de la référence API principale.
Envoyez votre contexte ERP/GED et le type de flux à tester, par email.