eliDocs
Country Guides

France

Factur-X e-invoicing requirements for France, with Beliq configuration examples.

France commonly uses Factur-X. It is a hybrid PDF format (technically aligned with ZUGFeRD) that embeds CII D22B XML in a PDF/A-3 document. Beliq targets Factur-X 1.09.2 (jointly published with ZUGFeRD 2.x).

Factur-X is jointly maintained by the FNFE-MPE (France) and FeRD (Germany) and is fully aligned with EN 16931.

Factur-X 1.09.2 profiles

Factur-X 1.09.2 profiles

Profile Beliq value Description Status in Beliq
Minimum minimum Minimal structured data — invoice number, date, and totals only Supported (via Mustangproject — see secondary-source note below)
Basic WL basicwl Header-level data without individual line items Supported
Basic basic Full header and line items Supported (via Mustangproject — see secondary-source note below)
EN 16931 (Comfort) en16931 Full EN 16931 compliance — recommended Supported
Extended extended Additional fields beyond EN 16931, with optional sub-line nesting Supported
EXTENDED-CTC-FR extended-ctc-fr EXTENDED + the French B2B reform CTC overlay (BR-FR-CTC + EXTENDED-CTC-FR Schematron) Supported

minimum and basicwl are reduced profiles (header-only, and without individual line items) and are not complete EN 16931 invoices. Pick them only when the receiver specifically expects that profile; see the format reference for the per-profile compliance breakdown.

For most use cases, en16931 is the recommended profile. Pick extended-ctc-fr when the receiver requires the French B2B reform CTC overlay. The Factur-X format reference carries the per-profile element layout, the CustomizationID URNs and the pinned artefact versions.

MINIMUM and BASIC — secondary source

The FNFE-MPE / FeRD joint pack distributes the BASIC_WL, EN 16931 and EXTENDED Schematron and XSDs via direct download. The MINIMUM and BASIC profile artifacts are gated behind an email-request information package on the FNFE-MPE side. Beliq sources MINIMUM and BASIC Schematron and per-profile XSDs from Mustangproject core-2.26.0 (Apache-2.0) — Mustangproject is listed by FNFE-MPE itself as a recommended downstream consumer and re-publishes the same artifacts at matching upstream tags. The Mustangproject provenance is recorded with SHA-pinned per-file paths.

All five Factur-X 1.09.2 profiles run through the same XSD + Schematron pipeline; documents arriving with a MINIMUM, BASIC, BASIC_WL, EN16931, or EXTENDED CustomizationID are validated against the matching profile rules without operator intervention.

Factur-X sits at Authority-checked, on the builder-round-trip tier: the FNFE-MPE / FeRD Schematron that judges your document is the authority’s own, pinned by version and hash, but FNFE-MPE publishes no per-rule test corpus to check it against. Correctness rests on the authority’s positive sample invoices, round-trips through Beliq’s own generator, and a spec-derived negative fixture cross-checked against the Mustangproject validator. The missing upstream corpus is re-checked quarterly. See how verification works for the tier definitions.

Generating Factur-X invoices

Generating Factur-X invoices

Use standard: "facturx" with output: "pdf":

generate-facturx-pdf.sh
curl -X POST https://api.beliq.eu/v1/generate \
  -H "Authorization: Bearer blq_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "standard": "facturx",
    "profile": "en16931",
    "output": "pdf",
    "invoice": {
      "number": "FA-2026-0123",
      "issueDate": "2026-04-13",
      "dueDate": "2026-05-13",
      "currencyCode": "EUR",
      "seller": {
        "name": "Exemple SARL",
        "vatId": "FR12345678901",
        "email": "facturation@exemple.example",
        "address": {
          "street": "12 Rue de la Paix",
          "city": "Paris",
          "postalCode": "75002",
          "countryCode": "FR"
        }
      },
      "buyer": {
        "name": "Client SAS",
        "vatId": "FR98765432109",
        "address": {
          "street": "8 Avenue des Champs-Élysées",
          "city": "Paris",
          "postalCode": "75008",
          "countryCode": "FR"
        }
      },
      "lines": [
        {
          "description": "Prestation de conseil — Avril 2026",
          "quantity": 5,
          "unitCode": "DAY",
          "unitPrice": 800.00,
          "lineTotal": 4000.00,
          "vatRate": 20,
          "vatCategoryCode": "S"
        }
      ],
      "taxSummary": [
        { "vatCategoryCode": "S", "vatRate": 20, "taxableAmount": 4000.00, "taxAmount": 800.00 }
      ],
      "paymentMeans": {
        "typeCode": "30",
        "iban": "FR7630006000011234567890189",
        "bic": "BNPAFRPPXXX"
      },
      "paymentTerms": "Net 30 jours",
      "totalNetAmount": 4000.00,
      "totalTaxAmount": 800.00,
      "totalGrossAmount": 4800.00
    }
  }' --output facture.pdf
import { writeFile } from 'node:fs/promises';

import { Beliq } from '@beliq/sdk';

const beliq = new Beliq({ apiKey: process.env.BELIQ_API_KEY! });

const generated = await beliq.generate({
  standard: 'facturx',
  profile: 'en16931',
  output: 'pdf',
  invoice: {
    number: 'FA-2026-0123',
    issueDate: '2026-04-13',
    dueDate: '2026-05-13',
    currencyCode: 'EUR',
    seller: {
      name: 'Exemple SARL',
      vatId: 'FR12345678901',
      email: 'facturation@exemple.example',
      address: { street: '12 Rue de la Paix', city: 'Paris', postalCode: '75002', countryCode: 'FR' },
    },
    buyer: {
      name: 'Client SAS',
      vatId: 'FR98765432109',
      address: {
        street: '8 Avenue des Champs-Élysées',
        city: 'Paris',
        postalCode: '75008',
        countryCode: 'FR',
      },
    },
    lines: [
      {
        description: 'Prestation de conseil — Avril 2026',
        quantity: 5,
        unitCode: 'DAY',
        unitPrice: 800.00,
        lineTotal: 4000.00,
        vatRate: 20,
        vatCategoryCode: 'S',
      },
    ],
    taxSummary: [{ vatCategoryCode: 'S', vatRate: 20, taxableAmount: 4000.00, taxAmount: 800.00 }],
    paymentMeans: { typeCode: '30', iban: 'FR7630006000011234567890189', bic: 'BNPAFRPPXXX' },
    paymentTerms: 'Net 30 jours',
    totalNetAmount: 4000.00,
    totalTaxAmount: 800.00,
    totalGrossAmount: 4800.00,
  },
});

await writeFile('facture.pdf', generated.bytes);
import os
from pathlib import Path

from beliq import Beliq

beliq = Beliq(api_key=os.environ["BELIQ_API_KEY"])

generated = beliq.generate(
    standard="facturx",
    profile="en16931",
    output="pdf",
    invoice={
        "number": "FA-2026-0123",
        "issueDate": "2026-04-13",
        "dueDate": "2026-05-13",
        "currencyCode": "EUR",
        "seller": {
            "name": "Exemple SARL",
            "vatId": "FR12345678901",
            "email": "facturation@exemple.example",
            "address": {
                "street": "12 Rue de la Paix",
                "city": "Paris",
                "postalCode": "75002",
                "countryCode": "FR",
            },
        },
        "buyer": {
            "name": "Client SAS",
            "vatId": "FR98765432109",
            "address": {
                "street": "8 Avenue des Champs-Élysées",
                "city": "Paris",
                "postalCode": "75008",
                "countryCode": "FR",
            },
        },
        "lines": [
            {
                "description": "Prestation de conseil — Avril 2026",
                "quantity": 5,
                "unitCode": "DAY",
                "unitPrice": 800.00,
                "lineTotal": 4000.00,
                "vatRate": 20,
                "vatCategoryCode": "S",
            },
        ],
        "taxSummary": [{"vatCategoryCode": "S", "vatRate": 20, "taxableAmount": 4000.00, "taxAmount": 800.00}],
        "paymentMeans": {"typeCode": "30", "iban": "FR7630006000011234567890189", "bic": "BNPAFRPPXXX"},
        "paymentTerms": "Net 30 jours",
        "totalNetAmount": 4000.00,
        "totalTaxAmount": 800.00,
        "totalGrossAmount": 4800.00,
    },
)

Path("facture.pdf").write_bytes(generated.content)

XML output

To generate just the CII XML without the PDF wrapper:

generate-facturx-xml.sh
curl -X POST https://api.beliq.eu/v1/generate \
  -H "Authorization: Bearer blq_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "standard": "facturx",
    "profile": "en16931",
    "output": "xml",
    "invoice": {
      "number": "FA-2026-0123",
      "issueDate": "2026-04-13",
      "dueDate": "2026-05-13",
      "currencyCode": "EUR",
      "seller": {
        "name": "Exemple SARL",
        "vatId": "FR12345678901",
        "address": { "street": "12 Rue de la Paix", "city": "Paris", "postalCode": "75002", "countryCode": "FR" }
      },
      "buyer": {
        "name": "Client SAS",
        "vatId": "FR98765432109",
        "address": { "street": "8 Avenue des Champs-Élysées", "city": "Paris", "postalCode": "75008", "countryCode": "FR" }
      },
      "lines": [
        {
          "description": "Prestation de conseil — Avril 2026",
          "quantity": 5,
          "unitCode": "DAY",
          "unitPrice": 800.00,
          "lineTotal": 4000.00,
          "vatRate": 20,
          "vatCategoryCode": "S"
        }
      ],
      "taxSummary": [
        { "vatCategoryCode": "S", "vatRate": 20, "taxableAmount": 4000.00, "taxAmount": 800.00 }
      ],
      "totalNetAmount": 4000.00,
      "totalTaxAmount": 800.00,
      "totalGrossAmount": 4800.00
    }
  }'
import { Beliq } from '@beliq/sdk';

const beliq = new Beliq({ apiKey: process.env.BELIQ_API_KEY! });

const generated = await beliq.generate({
  standard: 'facturx',
  profile: 'en16931',
  output: 'xml',
  invoice: {
    number: 'FA-2026-0123',
    issueDate: '2026-04-13',
    dueDate: '2026-05-13',
    currencyCode: 'EUR',
    seller: {
      name: 'Exemple SARL',
      vatId: 'FR12345678901',
      address: { street: '12 Rue de la Paix', city: 'Paris', postalCode: '75002', countryCode: 'FR' },
    },
    buyer: {
      name: 'Client SAS',
      vatId: 'FR98765432109',
      address: {
        street: '8 Avenue des Champs-Élysées',
        city: 'Paris',
        postalCode: '75008',
        countryCode: 'FR',
      },
    },
    lines: [
      {
        description: 'Prestation de conseil — Avril 2026',
        quantity: 5,
        unitCode: 'DAY',
        unitPrice: 800.00,
        lineTotal: 4000.00,
        vatRate: 20,
        vatCategoryCode: 'S',
      },
    ],
    taxSummary: [{ vatCategoryCode: 'S', vatRate: 20, taxableAmount: 4000.00, taxAmount: 800.00 }],
    totalNetAmount: 4000.00,
    totalTaxAmount: 800.00,
    totalGrossAmount: 4800.00,
  },
});

// `xml` is the same document curl writes to stdout.
console.log(generated.xml);
import os

from beliq import Beliq

beliq = Beliq(api_key=os.environ["BELIQ_API_KEY"])

generated = beliq.generate(
    standard="facturx",
    profile="en16931",
    output="xml",
    invoice={
        "number": "FA-2026-0123",
        "issueDate": "2026-04-13",
        "dueDate": "2026-05-13",
        "currencyCode": "EUR",
        "seller": {
            "name": "Exemple SARL",
            "vatId": "FR12345678901",
            "address": {
                "street": "12 Rue de la Paix",
                "city": "Paris",
                "postalCode": "75002",
                "countryCode": "FR",
            },
        },
        "buyer": {
            "name": "Client SAS",
            "vatId": "FR98765432109",
            "address": {
                "street": "8 Avenue des Champs-Élysées",
                "city": "Paris",
                "postalCode": "75008",
                "countryCode": "FR",
            },
        },
        "lines": [
            {
                "description": "Prestation de conseil — Avril 2026",
                "quantity": 5,
                "unitCode": "DAY",
                "unitPrice": 800.00,
                "lineTotal": 4000.00,
                "vatRate": 20,
                "vatCategoryCode": "S",
            },
        ],
        "taxSummary": [{"vatCategoryCode": "S", "vatRate": 20, "taxableAmount": 4000.00, "taxAmount": 800.00}],
        "totalNetAmount": 4000.00,
        "totalTaxAmount": 800.00,
        "totalGrossAmount": 4800.00,
    },
)

# `xml` is the same document curl writes to stdout.
print(generated.xml)
French B2B reform (CTC) readiness

French B2B reform (CTC) readiness

France’s B2B e-invoicing reform replaces unstructured invoices with EN 16931-compliant Factur-X documents transported through certified Plateformes Agréées (PA). The current legal timeline (DGFiP / AIFE) is:

DateWhat changes
1 September 2026All businesses must be able to receive; large enterprises and mid-caps (ETI) must issue and e-report.
1 September 2027Small and medium enterprises (PME) and micro-enterprises (TPE) must issue and e-report.

The 1 September 2026 and 2027 calendar was reconfirmed in April 2025; a further delay was rejected. Dates can still move by decree, so re-confirm against DGFiP before relying on them.

Last verified against DGFiP / AIFE and the European Commission eInvoicing country page on 24 August 2026. Official source.

Always check the official impots.gouv.fr e-invoicing portal for the latest dates and the B2B “Spécifications externes” v3.2 for the full technical requirements.

What Beliq covers today

For the French B2B reform, Beliq generates Factur-X 1.09.2 documents that pass the FNFE-MPE BR-FR-CTC Flux 2 Schematron. Concretely:

  • We emit Factur-X 1.09.2 / D22B documents (and PDF/A-3 hybrids) that satisfy the EN 16931 baseline.
  • We validate against FNFE-MPE’s BR-FR-CTC Flux 2 Schematron (france_ctc_schematron, currently 1.4.0.04) on any of three triggers: you send franceCtc=true; the document’s BT-24 CustomizationID is the EXTENDED-CTC-FR one; or its BT-23 BusinessProcess carries a cadre de facturation code from the closed XP Z12-012 list (B1, S1, M1, B2, S2, M2, S3, B4, S4, M4, S5, S6, B7, S7, B8, S8, M8, B9, S9, M9). Any of the twenty turns CTC mode on; S8 / B8 / M8 are the multi-vendor subset, not the whole trigger.
  • We then additionally apply the EXTENDED-CTC-FR overlay, on two triggers of its own: the document is on the Factur-X EXTENDED profile (CII), or it declares the EXTENDED-CTC-FR CustomizationID outright. The second arm is the only route for a UBL document, which carries no Factur-X profile.
  • On generate, facturxProfile: "extended-ctc-fr" turns CTC mode on by itself, so you do not also have to send franceCtc: true.
  • The FR-specific structural fields (SIREN / SIRET on seller and buyer, French VAT codes, Flux 2 process codes, line-level legal IDs and parent-line linkage) are recognised by both the builder and the validator, with rule IDs surfaced in the validation result.
validate-facturx-ctc.sh
# Opt in to the BR-FR-CTC overlay when validating an existing document
curl -X POST 'https://api.beliq.eu/v1/validate?franceCtc=true' \
  -H 'Authorization: Bearer blq_live_abc123...' \
  -H 'Content-Type: application/xml' \
  --data-binary @facture.xml
import { readFile } from 'node:fs/promises';

import { Beliq } from '@beliq/sdk';

const beliq = new Beliq({ apiKey: process.env.BELIQ_API_KEY! });

const result = await beliq.validate(await readFile('facture.xml'), { franceCtc: true });

console.log(result.valid, result.format, result.schematronVersion);
for (const issue of result.errors) console.log(issue.ruleId, issue.message);
import os
from pathlib import Path

from beliq import Beliq

beliq = Beliq(api_key=os.environ["BELIQ_API_KEY"])

result = beliq.validate(Path("facture.xml").read_bytes(), france_ctc=True)

print(result.valid, result.format, result.schematron_version)
for issue in result.errors:
    print(issue.rule_id, issue.message)
// Generate a CTC-enabled invoice
{
  "standard": "facturx",
  "facturxProfile": "extended-ctc-fr",
  "output": "pdf",
  "invoice": {
    /* …seller / buyer with SIREN/SIRET and FR VAT IDs, lines, totals… */
    "franceCtc": true,
    "businessProcessId": "B8"
  }
}

Which French flows need a Plateforme Agréée

The reform’s perimeter is drawn by establishment in France, not by nationality, VAT number, or invoice currency. That splits French traffic into three cases, and only one of them is regulated:

Flow In the reform’s perimeter? How it travels
Seller established in France → buyer established in France Yes Must go through a Plateforme Agréée (PA)
Seller not established in France → French buyer No Ordinary Peppol, BIS Billing 3.0
Seller established in France → foreign buyer No Ordinary Peppol

Because establishment is the test, a foreign company with a French branch is inside the perimeter even though it is not a French company, and a company holding a French VAT number without a French establishment is outside it. Businesses with no French establishment are excluded from French e-invoicing altogether rather than given a later deadline (DGFiP FAQ, 15 June 2026, Q1.6); they may still owe e-reporting where they are liable for French VAT.

For the two unregulated cases, France changes nothing: those are cross-border Peppol invoices, and managed delivery will route them on the peppol network like any other. For the regulated case it will not. POST /v1/send and POST /v1/transmissions refuse a French domestic flow on the peppol network with 409 FRENCH_DOMESTIC_FLOW, and nothing is queued or billed. That is not a verdict on the document — the invoice may be perfectly valid — it is a refusal of the route: a domestic invoice that reaches the buyer over plain Peppol still leaves the seller outside the reform. See transmission errors.

Once the PA lane is live, a second check runs on it, and it is about the document rather than the route. A plateforme agréée refuses an invoice that trips any BR-FR-CTC Flux 2 rule, whatever severity the French rule pack assigns it — and its refusal burns the invoice number, because a French number is unique on (number, supplier SIREN, invoice year) and a rejected invoice must be reissued under a new one. Beliq therefore checks its own verdict before the send: 422 FRANCE_CTC_BLOCKING_FINDINGS lists the rules to correct, and 422 FRANCE_CTC_NOT_JUDGED means the invoice carries neither BT-23 nor the EXTENDED-CTC-FR profile, so nothing judged it against the French rules at all. Note that POST /v1/validate can report valid: true for a document this refuses: the pack currently flags most of these rules as warnings, which is why the validation result carries franceCtcBlockingRuleIds as a separate answer. Clearing the check is not a promise of acceptance — the platform runs its own rule packs and its verdict is its own.

If a PA does reject an invoice, resending the same bytes is refused with 409 FRANCE_INVOICE_NUMBER_BURNED: issue a corrected invoice under a new number instead. The platform’s own duplicate check does not stop the reuse, which is exactly why the guard is on our side.

Treat the refusal as a safeguard, not as a compliance determination. Where we have recorded where a party is established, that is what the check uses, for both the seller and the buyer. Where we have not, it falls back to proxies for establishment rather than the test itself, so the check can miss a case they cannot see: for your own registration, the registered country it carries; for your buyer, the addressing scheme alone, since the country on someone else’s registration is neither the right test nor ours to read on your behalf. You hold the establishment facts; tell us and we will record them. The French domestic route is the partner PA lane described below.

What Beliq does not do (transmission / Annuaire / e-reporting)

Beliq is not a Plateforme Agréée (PA) immatriculée operator and does not (yet) intermediate transmission through the AIFE network. Specifically, the following are out of scope of this release:

  • Plateforme Agréée (PA) immatriculation with AIFE (a separate regulatory and operational program).
  • Operating the AIFE Annuaire (recipient lookup) interface.
  • The CDAR (Cross-Domain Acknowledgement and Response) message envelope used between Plateformes Agréées.
  • E-reporting flows (Flux 8 / 9 / 10) used for transaction reporting to DGFiP.

If your workflow requires a registered Plateforme Agréée (PA) today, please pair Beliq’s compliant Factur-X output with a partner PA for transmission. Managed delivery through a partner PA is in build, targeting Q4 2026 and subject to certification. Reach out if you would like us to track your PA requirements as we expand this work.

French VAT specifics

French VAT specifics

VAT rate Category code Description
20% S Standard rate
10% S Intermediate rate
5.5% S Reduced rate
2.1% S Super-reduced rate
0% Z Zero-rated (e.g. intra-community supplies)
0% AE Reverse charge
0% E Exempt

Use the appropriate vatRate and vatCategoryCode combination on each invoice line.

Parsing Factur-X invoices

Parsing Factur-X invoices

Upload a Factur-X PDF to extract the embedded data:

parse-facturx.sh
curl -X POST https://api.beliq.eu/v1/parse \
  -H "Authorization: Bearer blq_live_abc123..." \
  -H "Content-Type: application/octet-stream" \
  --data-binary @facture.pdf
import { readFile } from 'node:fs/promises';

import { Beliq } from '@beliq/sdk';

const beliq = new Beliq({ apiKey: process.env.BELIQ_API_KEY! });

const parsed = await beliq.parse(await readFile('facture.pdf'));

console.log(parsed.format, parsed.profileDetected);
console.log(parsed.invoice);
import os
from pathlib import Path

from beliq import Beliq

beliq = Beliq(api_key=os.environ["BELIQ_API_KEY"])

parsed = beliq.parse(Path("facture.pdf").read_bytes())

print(parsed.format, parsed.profile_detected)
print(parsed.invoice)

The engine extracts the factur-x.xml attachment from the PDF and returns the normalized JSON invoice data.

Validation

Validation

Beliq validates Factur-X 1.09.2 invoices against:

  1. CII D22B XSD schema (the per-profile XSD subset is used when the document declares a Factur-X CustomizationID).
  2. EN 16931 core Schematron rules (currently v1.3.16).
  3. The Factur-X 1.09.2 profile-specific Schematron (BASIC_WL / EN16931 / EXTENDED), vendored from FNFE-MPE / FeRD.
  4. The FNFE-MPE BR-FR-CTC Flux 2 overlay when franceCtc=true is requested, when BT-24 is the EXTENDED-CTC-FR CustomizationID, or when BT-23 carries any of the twenty XP Z12-012 cadre de facturation codes.
  5. The FNFE-MPE EXTENDED-CTC-FR overlay on top of (4) when the document is on the Factur-X EXTENDED profile or declares the EXTENDED-CTC-FR CustomizationID.

See validation artifacts for the exact versions in use.

Resources

Resources