DEV Community

Olivier EBRAHIM
Olivier EBRAHIM

Posted on

Factur-X 2026: Implementation Guide for SMB Construction Developers

Factur-X 2026: Implementation Guide for SMB Construction Developers

The Mandate is Real: What You Need to Know

On January 1, 2026, French SMBs in construction (artisans, BTP contractors, and materials distributors) must legally switch to e-invoicing via Factur-X or similar formats. This isn't optional—it's a regulatory requirement enforced by the French Ministry of Finance.

If you're building or maintaining construction management software, you can't ignore this. Here's what you need to implement, why it matters, and how to approach it without burning your product timeline.

What is Factur-X? (The Two-Minute Version)

Factur-X is a hybrid invoice format combining:

  • PDF/A-3 wrapper (the human-readable side)
  • XML (ZUGFeRD 2.0) embedded metadata (the machine-readable side)

When a subcontractor or materials supplier receives your invoice, their accounting software reads the XML automatically. No manual data entry. No OCR errors. No "why is this invoice format wrong?" support tickets.

For developers: think of it as an API format that happens to live inside a PDF. The burden is front-loaded during implementation, then it's solved forever (usually).

Why SMB Construction Cares (And Why Developers Should Too)

Construction invoicing has real constraints:

  • Fragmented workflows: Invoices generated from job site tablets, site managers' laptops, even voice dictation
  • Subcontractor networks: A general contractor sends invoices to 50+ suppliers; each supplier must read them correctly
  • Regulatory deadlines: Strict date. No extension. No "we're still working on it."

Most construction SMBs use QuickBooks, SAP, or local French accounting software. Many of those packages don't yet support Factur-X parsing. Your job as a developer is to be the bridge.

Implementation: The Realistic Roadmap

Step 1: Choose Your Factur-X Library (Don't Roll Your Own)

Language-specific libraries exist. Use them:

  • Node.js/JavaScript: facturx (npm package), or pdf-lib + xml2js for a lighter approach
  • Python: facturx (PyPI), built by the Factur-X consortium
  • C# / .NET: UblDocument (NuGet)
  • PHP: setasign/fpdi (for PDF assembly) + DOM for XML

These libraries handle:

  • XML schema validation (ZUGFeRD 2.0 compliance)
  • PDF/A-3 conversion (critical for long-term archival)
  • Namespace management (the sneaky XML gotchas)

Rolling your own = 2 months of debugging edge cases. Use a library = 1 week of integration.

Step 2: Map Your Data Model

Factur-X requires specific fields. Audit your invoice object:

invoice = {
  invoice_number: "CHT-2025-0012",
  invoice_date: "2025-05-15",
  due_date: "2025-06-15",

  // Seller
  seller: {
    name: "ABC Construction Artisans",
    siret: "12345678901234",  // Legal ID
    vat_id: "FR12345678901",
    address: "123 Rue de Paris, 75008 Paris"
  },

  // Buyer
  buyer: {
    name: "GC Builders Inc",
    siret: "98765432109876",
    address: "456 Chaussée, Lyon 69000"
  },

  // Line items
  lines: [
    {
      description: "Excavation & site prep",
      quantity: 200,
      unit: "HOUR",  // Must be from UNECE list
      unit_price: 65.00,
      tax_rate: 0.20  // 20% VAT (standard in France)
    }
  ],

  // Totals
  subtotal: 13000.00,
  tax_amount: 2600.00,
  total_due: 15600.00,

  // Payment terms (optional but strongly recommended)
  payment_terms: "NET 30"
}
Enter fullscreen mode Exit fullscreen mode

Critical gotcha: Unit codes must match UNECE standards (HOUR, DAY, PIECE, etc.). Don't invent "man-hour" — use "HOUR".

Step 3: Generate the XML Payload

Using a library, create the ZUGFeRD 2.0 XML. It looks like this (simplified):

<?xml version="1.0" encoding="UTF-8"?>
<rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100">
  <rsm:ExchangedDocumentContext>
    <ram:GuidelineSpecifiedDocumentContextParameter>
      <ram:ID>urn:factur-x.eu:1p0:extended</ram:ID>
    </ram:GuidelineSpecifiedDocumentContextParameter>
  </rsm:ExchangedDocumentContext>

  <rsm:ExchangedDocument>
    <ram:ID>CHT-2025-0012</ram:ID>
    <ram:IssueDateTime>
      <udt:DateTimeString format="102">20250515</udt:DateTimeString>
    </ram:IssueDateTime>
  </rsm:ExchangedDocument>

  <!-- More detailed seller, buyer, line item, payment info... -->
</rsm:CrossIndustryInvoice>
Enter fullscreen mode Exit fullscreen mode

This is verbose and namespace-heavy. Don't hand-craft it—your library generates it from your data model.

Step 4: Embed XML into PDF and Sign

// Pseudo-code (Node.js example)
const fs = require('fs');
const pdf = require('@setasign/fpdi');
const facturx = require('facturx');

// 1. Generate XML
const xmlData = generateZugferd(invoiceData);

// 2. Create PDF/A-3 with embedded XML
const pdfBuffer = await createPdfFromTemplate(invoiceData);
const facturxPdf = facturx.attachXmlToPdf(pdfBuffer, xmlData, {
  standard: 'factur-x',
  version: '1.0'
});

// 3. Optional: Sign with your certificate (recommended for B2B)
const signedPdf = await signPdf(facturxPdf, yourPrivateKey);

// 4. Save
fs.writeFileSync('invoice_CHT-2025-0012.pdf', signedPdf);
Enter fullscreen mode Exit fullscreen mode

Step 5: Validate Before Sending

Use the official Factur-X validator from the FNFE (French national body):

Test with:

  • Edge cases (0% VAT for services, reverse charge, intra-community)
  • Missing optional fields (payment terms, delivery address)
  • Real accounting software (upload to a test Sage account, see if it imports cleanly)

Common Pitfalls (Learned the Hard Way)

  1. Forgetting PDF/A-3 compliance: Your PDF looks fine but fails archival validation. Use verify-pdf-a3 tools.

  2. SIRET mismatch: You embed a different SIRET in the XML than on the PDF. Accounting systems reject it. Sync your invoice object.

  3. Currency codes: Only EUR is safe in France. Don't hardcode; allow multi-currency in your data model but validate on submission.

  4. Timestamp inconsistency: Invoice date is "2025-05-15", but XML says "2025-05-16". Banks get confused. Use UTC everywhere, format on display only.

  5. Testing only in isolation: Generate 10 test invoices, send them to a real accountant or use a staging Sage/QuickBooks account. Catch issues early.

How to Future-Proof Your Code

  • Decouple PDF generation from XML: Store the XML separately so you can re-validate or re-sign later.
  • Version your schema: If you support 2.0 today, tag it. Factur-X 3.0 might land in 2027.
  • Log XML outputs: When a customer says "my bank can't read this invoice," dump the XML to your logs. Debug time: 30 seconds vs. 30 minutes.
  • Expose a test endpoint: Let customers generate test invoices and validate them themselves before going live.

Tools to Simplify Your Life

If you're building construction management software, Anodos has Factur-X baked in—meaning you don't have to build invoicing from scratch. But if you're rolling your own, these resources help:

The Timeline (Firm Deadline: January 1, 2026)

  • Now (May 2025): Audit your system. Do you generate invoices? Are they going to French customers? You're in scope.
  • June–August: Implement XML generation + PDF embedding. 4–6 weeks if using a library.
  • September–October: Test with customers' accounting software (their Sage, QuickBooks, Ciel).
  • November: Go live with a feature flag. Soft launch with beta customers.
  • December: Monitor, fix last-minute issues.
  • January 1, 2026: All French invoices must be Factur-X compliant.

If you're late, fines start: €50–€200 per non-compliant invoice.

Closing Thoughts

Factur-X is boring infrastructure, but it's the law. The good news: it's well-specified, libraries exist, and once you implement it, it's stable.

Don't reinvent the wheel. Use a library. Test early. Ship by November 2025. Sleep soundly on January 1, 2026.


Olivier Ebrahim, founder of Anodos, a French SaaS for construction management with Factur-X built in.

Top comments (0)