Factur-X in production: the PDF was the easy half
Factur-X wraps an invoice's data inside its PDF. Embedding the file is one API call; surviving EN 16931's two hundred business rules is the job, validators and all.
General information, not legal advice. Regulatory duties need your own competent advice.
Factur-X is how an invoice becomes machine-readable without changing how it is sent. This month we shipped it on a field operations platform in a regulated compliance industry: every invoice PDF the platform issues now carries its own figures as structured XML inside the file, in the profile the French and German regimes accept. Embedding the XML took a day. Producing XML worth embedding took the rest of the job.
A Factur-X invoice is a PDF/A-3 file with the same invoice embedded inside it as structured XML, so a person reads the page and accounting software reads the data. The format is published jointly by the French and German e-invoicing bodies, and the two names describe one publication: the current release, issued on 4 August 2026, is Factur-X 1.09.2 and ZUGFeRD 2.5.2, identical under either label. What the XML must say is set by EN 16931, the European semantic standard for invoices, whose reference the Commission published in Implementing Decision (EU) 2017/1870.
The deadlines forcing the issue
| Market | What changes | From |
|---|---|---|
| France | Every business in scope must be able to receive e-invoices; large and intermediate firms must also issue them, via a certified platform | 1 September 2026 |
| France | Smaller firms must issue too | 1 September 2027 |
| Germany | Reception of e-invoices mandatory for domestic B2B | In force since 1 January 2025 |
| Germany | Issuance mandatory for businesses above €800k turnover, then for everyone | 1 January 2027, then 1 January 2028 |
| UK | Mandatory e-invoicing for VAT invoices, on Peppol | April 2029 |
The French dates come from the tax authority's own guide to the September 2026 start, and its accepted core formats are three, Factur-X among them, per the AFNOR specification the reform runs on. Germany's reception duty and its list of acceptable formats sit in the Finance Ministry's letter of 15 October 2025, with the issuance phase-in written into section 27(38) of the VAT law. The UK committed to an April 2029 mandate in the November 2025 consultation response and named Peppol as the network in June 2026; the format question stays open until the implementation roadmap at the Budget on 28 October.
A UK platform has no obligation under any of this yet. We built it anyway, for an unexciting reason: business invoices travel as PDFs on email, and that channel is not changing soon. If the same PDF starts carrying its data, nothing changes for the person reading it and everything changes for the software their finance team runs. Reach works the way it does with the EU AI Act and UK companies: the rules follow where the document lands, not where the sender is registered.
The easy half
Our PDFs were already rendered by Gotenberg, the open source document service. It gained file embedding for exactly this in version 8.25.0, late in 2025, and version 8.34.0 added the dedicated Factur-X fields in June: hand the HTML route an invoice XML and a conformance level, and one request returns a PDF/A-3 with the attachment embedded and the metadata set.
To see how much that buys, we posted <not-cii/> as the attachment. HTTP 200, and back came a perfectly valid PDF/A-3 wrapping nonsense, correctly labelled as an e-invoice. The wrapper does not read what it wraps. Every quick demo of Factur-X is a demo of the wrapper, which is why so many teams believe this feature is a day's work. It is, right up until a customer's accounts payable system opens the attachment.
The hard half: two hundred rules about what an invoice may say
The current official validation artefacts for EN 16931 carry 223 distinct business rules, every one of them fatal. Element order is fatal a layer earlier, in the schema. The currency attribute belongs on exactly one of the totals and nowhere else. Dates travel as 102-format strings. An invoice wholly outside the scope of VAT must omit the seller's VAT identifier and carry the company registration number instead. None of this is visible on the PDF, and none of it is negotiable.
So the generation side went into a small stateless service whose whole job is XML: it builds the document with the Akretion-maintained open source factur-x library, validates against the schema, then runs the official rule set over every single document and refuses to return anything that fails. Only XML that survives reaches the PDF service for embedding. Validation as a gate, not a test suite. A rule that runs only in CI protects the build; it does nothing for the invoice generated three months from now, for a customer profile nobody thought to test.
Keeping that in its own container was the other half of the decision. The standard is a moving target, politely: the release we ship went out six days before this post, and the official rule artefacts refresh twice a year. In a container, a spec bump is a rebuild. In the backend, it would be a release with a regulatory deadline attached.
Fail open, loudly
An invoice download must never fail because a compliance sidecar is down. Any generation failure, validation rejection or unrepresentable invoice logs its reason and falls back to the plain PDF the customer would have received last month. The log line is the alarm.
Fail-closed was the tempting design, and we think the wrong one today. Nobody using the platform is under a live mandate yet, and breaking a customer-facing download over a container restart is the wrong end of that trade. The honest caveat: fail-open is only safe while the structured half is optional. The day a user is legally required to send structured invoices, a silent fallback becomes a compliance failure with friendly logging, and the trade flips to fail-closed. We wrote that condition down next to the code rather than trusting anyone to remember it.
The rounding rule nobody warns you about
The unit price is the one amount EN 16931 puts no decimal limit on, and that is not an oversight. We learned why the obvious way, by shipping two-decimal rounding first.
Our line totals are computed from the unrounded unit cost. Round the price to two decimals in the XML and quantity times price starts drifting away from the stored total, and from about ten units the drift clears the tolerance downstream networks allow. Ten units at £33.3333 produced a £0.03 discrepancy and a rejected document. No official rule ties the unit price to the line total, so our own gate could never have caught it; a downstream validator did. The fix is to emit the price at full precision and round nothing that feeds arithmetic. Whole prices still render as 150.00, so no invoice looks stranger for it.
One computation, two documents
A hybrid format has a failure mode all of its own: the page says one thing and the embedded data says another. A tax inspection parses the XML; your customer approved the page. We treat a divergence between the two as the worst bug this feature can have.
So both halves render from one computation: the same stored totals, the same line derivations, the same snapshot of the seller's details taken when the invoice was issued. The snapshot matters more than it looks. Re-export an old invoice after a rebrand or a VAT registration and, without it, you get a different document under the same invoice number. The pull is the same one that makes offline-captured evidence survive an audit: once a record is caught changing after the fact, everything filed beside it becomes suspect.
Two smaller decisions in the same spirit. Before any XML is emitted, the mapping re-derives the totals from the lines and refuses to proceed if the stored figures disagree with the arithmetic. And a paid invoice emits its balance due as zero, because a receipt re-downloaded next quarter and forwarded into an accounts payable system must not queue a second payment. Invoicing is the tail end of the billing plumbing, so it gets the same paranoia we brought to choosing between Stripe Connect charge types.
What we tested it against
Passing your own gate proves consistency, not correctness, so four real exported invoices went through every authority's own tooling.
| Validator | Checks | Result |
|---|---|---|
| veraPDF | PDF/A-3b conformance | Pass, four of four |
| KoSIT validator (Germany) | German e-invoice acceptance | Acceptable, no messages |
| CEN Schematron, current artefacts | The EN 16931 business rules | Zero failures, at warning level too |
| The European Commission's public validator | Schema plus business rules | Success, zero errors |
| The publishers' own Factur-X toolset | Format schema and rules | Valid, one warning |
| Mustang (the German reference tool) | Full document check | Valid, zero failed rules |
The one warning is honest and stays: an empty delivery element the schema itself makes mandatory. The proper fix is a real supply date, which the platform does not capture yet, and populating the field with the issue date would assert a fact we do not know. A warning we can explain beats a value we invented.
What a valid file does not buy you
Document compliance is not transmission compliance. From September, domestic French B2B invoices must travel between certified platforms; producing a compliant file connects you to none of them. The UK's 2029 regime will move structured data over Peppol, not PDFs over email. German public sector buyers want a different profile again. Anyone selling you "mandate compliance" as a file format is selling the wrapper.
What survives every one of those futures is the part that took the work: the mapping from an invoice, with all its VAT edge cases, to the standard's terms, held behind a validation gate. The PDF is packaging for the channel businesses use today. When a transport arrives, the same mapping feeds it.
We have not built any transport, deliberately. The UK has named the network but not the format, and the roadmap lands with the Budget in October. Serialising to a guessed format now risks writing it twice, so we are waiting, and writing the date down. That is a bet on regulator timing, we might lose it, and losing it would cost a few weeks. Guessing wrong the other way costs the same weeks plus a migration.
FAQ
Does a UK business need any of this yet?
There is no UK duty until the April 2029 mandate, per the government's 2025 decision. The practical pull arrives earlier: invoices sent to French and German customers now land in software that expects structured data, and a Factur-X PDF supplies it without changing how you send anything.
What is the difference between Factur-X and ZUGFeRD?
Nothing but the name. The French and German bodies publish the standard jointly, and the current release pairs Factur-X 1.09.2 with ZUGFeRD 2.5.2 as one specification. A file valid under either label is valid under both, which is precisely what makes the format useful across the two regimes.
Which Factur-X profile should software emit?
EN 16931. Germany's guidance excludes the MINIMUM and BASIC WL profiles from what counts as an e-invoice, so emitting the full profile is what keeps a document acceptable in both regimes. We never built a profile switch: every other position on it would select non-compliance.
Does producing Factur-X satisfy the French mandate?
No. The file format is one requirement; from September 2026, domestic French B2B invoices must also travel via a certified platform, and issuers in scope must connect to one. A UK seller sits outside most of the domestic scheme, but French customers' software still expects the structured half.
Ready to build the thing?
Book a free 30-minute call. We'll dig into your idea, your stack and your timeline, and give you an honest read on what it will take to build and launch. You'll leave with a clearer plan whether or not you hire us.