Aller au contenu
Qelvyn
Facturation électronique11 août 2026

Intégrer un validateur Factur-X par API : le tuto technique pour les développeurs

Valider des factures Factur-X par API REST : requêtes cURL, Python et Node, structure du rapport JSON, pipeline de contrôle et intégration CI.

Si votre application génère ou reçoit des factures, la validation ne peut pas être une étape manuelle. Un fichier Factur-X non conforme qui atteint la production, c'est un rejet chez le client, un ticket au support et une équipe comptable qui remonte la chaîne jusqu'à vous. La solution propre est un contrôle systématique par API, appelé à deux endroits : avant chaque émission, et dans la CI pour empêcher une régression du générateur. Voici l'intégration complète, du premier appel au pipeline.

L'architecture cible en une phrase

Chaque fichier produit ou reçu passe par un endpoint de validation qui renvoie un rapport structuré ; le code appelant bloque, alerte ou laisse passer selon la sévérité des erreurs. Les exemples ci-dessous utilisent un domaine fictif, api.exemple-validateur.fr, à remplacer par votre service, qu'il soit interne ou SaaS.

Premier appel : envoyer un fichier

En cURL, un POST multipart suffit :

curl -X POST "https://api.exemple-validateur.fr/v1/validate" \
  -H "Authorization: Bearer $CLE_API" \
  -F "fichier=@facture.pdf"

Réponse type, volontairement simplifiée :

{
  "statut": "invalide",
  "format": "factur-x",
  "profil": "EN 16931",
  "erreurs": [
    {
      "code": "BR-CO-15",
      "severite": "erreur",
      "message": "Le montant TTC ne correspond pas au total HT plus la TVA",
      "chemin": "//ram:SpecifiedTradeSettlementHeaderMonetarySummation"
    }
  ],
  "avertissements": []
}

Les champs qui comptent pour votre logique applicative : le statut global, le profil détecté, et pour chaque erreur un code normalisé (les règles BR de la norme EN 16931), une sévérité et un chemin XPath qui localise le problème dans le XML. C'est ce triplet qui rend l'erreur actionnable, aussi bien pour un développeur que pour un message affiché à l'utilisateur final.

Intégration en Python

import requests

URL_VALIDATION = "https://api.exemple-validateur.fr/v1/validate"

def valider_facture(chemin_fichier: str, cle_api: str) -> dict:
    """Valide un fichier Factur-X et renvoie le rapport complet."""
    with open(chemin_fichier, "rb") as fichier:
        reponse = requests.post(
            URL_VALIDATION,
            headers={"Authorization": f"Bearer {cle_api}"},
            files={"fichier": fichier},
            timeout=30,
        )
    reponse.raise_for_status()
    return reponse.json()

rapport = valider_facture("facture.pdf", "votre_cle_api")

if rapport["statut"] != "valide":
    # On bloque l'émission et on journalise chaque règle violée
    for erreur in rapport["erreurs"]:
        print(f"{erreur['code']} : {erreur['message']}")
    raise ValueError("Facture non conforme, émission annulée")

Intégration en Node.js

// Validation d'une facture avant émission
import { readFile } from "node:fs/promises";

const URL_VALIDATION = "https://api.exemple-validateur.fr/v1/validate";

async function validerFacture(cheminFichier, cleApi) {
  const donnees = new FormData();
  const contenu = await readFile(cheminFichier);
  donnees.append("fichier", new Blob([contenu]), "facture.pdf");

  const reponse = await fetch(URL_VALIDATION, {
    method: "POST",
    headers: { Authorization: `Bearer ${cleApi}` },
    body: donnees,
  });

  if (!reponse.ok) {
    throw new Error(`Échec de l'appel : ${reponse.status}`);
  }
  return reponse.json();
}

const rapport = await validerFacture("facture.pdf", process.env.CLE_API);
if (rapport.statut !== "valide") {
  console.error("Facture rejetée :", rapport.erreurs);
  process.exit(1);
}

Ce que le validateur doit vérifier, et dans quel ordre

Un bon pipeline de validation échoue vite et dans le bon ordre, car chaque étape n'a de sens que si la précédente passe. D'abord la conformité du conteneur PDF/A-3, sans laquelle rien d'autre ne tient. Puis l'extraction du fichier factur-x.xml et le contrôle de son nom et de sa relation d'incorporation. Ensuite la validation XSD, qui garantit une structure exploitable, suivie des règles métier Schematron de la norme EN 16931, où se concentrent la plupart des rejets réels : totaux, ventilation de TVA, référentiels de codes. Enfin les contrôles propres au contexte français : validité des SIREN et SIRET par l'algorithme de Luhn, cohérence de la clé de TVA intracommunautaire, présence des mentions rendues obligatoires par la réforme.

Si vous construisez le service vous-même plutôt que d'en consommer un, les briques open source couvrent l'essentiel : veraPDF pour le conteneur, les schémas et Schematron officiels de la norme (dépôt ConnectingEurope/eInvoicing-EN16931) pour les règles, et Mustangproject comme moteur de validation intégré. Le travail restant est l'orchestration, le rapport JSON homogène et les règles françaises.

Brancher la validation dans la CI

Le générateur de factures est du code, et ce code régresse comme les autres. Un jeu de factures de test validé à chaque commit coûte quelques secondes de pipeline :

# Extrait de workflow GitHub Actions
jobs:
  validation-factures:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Générer les factures de test
        run: python scripts/generer_factures_test.py
      - name: Valider chaque fichier produit
        run: python scripts/valider_lot.py sorties/*.pdf
        env:
          CLE_API: ${{ secrets.CLE_API_VALIDATEUR }}

Le script de lot appelle l'API pour chaque fichier et sort en erreur au premier rapport invalide. Une modification du moteur de génération qui casse un arrondi de TVA est ainsi détectée avant la mise en production, pas par votre plus gros client.

Les bonnes pratiques qui évitent les mauvaises surprises

Quelques règles issues du terrain. Fixez un timeout côté client et gérez le retry avec prudence : la validation est idempotente, revalider un même fichier est sans danger. Traitez les avertissements différemment des erreurs : un avertissement mérite un log, pas un blocage. Pour les gros volumes, préférez un mode asynchrone avec webhook au polling. Et sur le plan de la confidentialité, une facture est une donnée d'affaires sensible : vérifiez la politique de rétention du service appelé, exigez que les fichiers ne soient pas conservés au-delà du traitement, et bannissez les validateurs en ligne gratuits sans contrat pour tout flux de production.

Questions fréquentes

Faut-il valider à l'émission, à la réception, ou les deux ?

Les deux, pour des raisons différentes. À l'émission, pour ne jamais envoyer un fichier rejetable et rester du bon côté de l'obligation légale. À la réception, pour filtrer les anomalies fournisseurs avant qu'elles n'entrent dans votre circuit de paiement.

Une validation locale sans API est-elle envisageable ?

Oui, en embarquant les briques open source directement dans votre application. Le compromis est le coût de maintenance : schémas, Schematron et règles françaises évoluent, et c'est vous qui suivez les versions. L'API centralisée mutualise cette maintenance.

Comment tester l'intégration sans vraies factures ?

Constituez un corpus de fichiers de test : des factures valides dans chaque profil, et des invalides couvrant chaque famille d'erreur (PDF cassé, XML absent, totaux faux, SIREN invalide). Ce corpus devient votre jeu de non-régression, versionné avec le code.

Contrôler chaque facture à la main ne tient pas à l'échelle

Qelvyn conçoit les outils internes que les cabinets comptables et les entreprises utilisent pour valider automatiquement leurs factures Factur-X avant émission ou réception. Si votre contrôle de conformité a dépassé ce qu'un tableur peut gérer, décrivez-nous votre situation et nous vous dirons franchement si un système est rentable.