DEV Community

facturata
facturata

Posted on

I built an API that turns JSON into legally-compliant EU e-invoices (Factur-X), because 2026 is coming

If you build software that touches invoices in Europe, the next 24 months are going to be busy. France requires all businesses to receive e-invoices from September 2026. Germany already requires reception since 2025, with issuing phased in 2027-2028. Belgium goes B2B via Peppol in 2026.

The dominant format in France/Germany is Factur-X (identical to ZUGFeRD 2.x): a normal PDF invoice with machine-readable XML (EN 16931 standard) embedded inside. Nice idea. Implementing it correctly is not nice: the XSD is the easy part, then come ~200 schematron business rules (BR-, BR-CO-, BR-S-*...), VAT category logic, and country-specific rulesets on top.

So I built Facturata: one POST with plain invoice JSON, and you get back a compliant Factur-X PDF (or raw CII XML), validated against the official XSD + full schematron before it leaves the server. There is also /validate (check any Factur-X/ZUGFeRD file, including the French 2026 CTC ruleset) and /extract (pull the XML out of a PDF).

Design choices worth mentioning:

  • Deterministic engine, no LLM at runtime. Compliance tooling should not hallucinate.
    • Stateless: documents processed in memory, nothing stored. Makes GDPR conversations short.
    • - Totals and VAT breakdowns computed server-side, so garbage-in cannot produce arithmetically broken invoices (the rules catch that anyway, but better to never emit it).
    • - Built on the excellent open-source Akretion factur-x library + official validation artifacts.
      • There is a free web validator (no signup) at https://facturata.com if you just want to check files, and a free API tier (15 docs/month) on RapidAPI if you want to integrate. Feedback very welcome, especially from anyone wrestling with XRechnung or Peppol quirks.

Top comments (3)

Collapse
 
johnfrandsen profile image
John Frandsen

Genuinely nice architecture choices — deterministic validation, stateless processing, server-side totals recomputation. The "no LLM at runtime" principle is exactly right for compliance tooling; you don't want a model quietly rewriting a VAT category code.

This fits a broader pattern in EU fintech: the regulation mandates a technically reasonable standard, then the compliance burden around it gets heavy enough that small builders can't participate directly without an abstraction layer. Factur-X's ~200 schematron rules are that burden for e-invoicing. On the bank-data side, PSD2 technically gives every developer the right to read account data via standardised APIs — but the eIDAS QWAC certificate (€5–15k/year) plus 6–12 months of AISP authorisation means almost no indie developer can actually use that right without going through a Plaid or Tink. Same shape: the standard is open, the compliance tax isn't.

I maintain open-banking.io (cert-free EU bank data access — different domain, same thesis: absorb the compliance complexity so builders don't have to). Curious whether you've run into the XRechnung vs Factur-X format divergence yet — heard Peppol's transport layer adds its own quirks on top of EN 16931.

Collapse
 
facturata profile image
facturata

Thanks — good question. The divergence is real, and it's mostly in the layers around the EN 16931 core rather than the core itself. XRechnung is pure XML (UBL or CII) with the KoSIT national rules stacked on top (the BR-DE set: mandatory seller contact details, payment instructions, Leitweg-ID for B2G). Factur-X wraps CII XML in a PDF/A-3 with profile levels, and ZUGFeRD's EXTENDED profile steps outside the EN core entirely. So the same economic invoice can pass one ruleset and fail the other; "EN 16931 valid" is necessary but not sufficient in either country.

Germany keeps the hybrid path alive by accepting ZUGFeRD 2.x EN16931-profile as E-Rechnung; France adds its own CTC ruleset instead. And Peppol is a third thing again: BIS Billing 3.0 is another CIUS (UBL plus PEPPOL-R rules) with transport concerns (participant lookup, AS4) that are orthogonal to document validity.

Today we validate the EN core plus the French CTC set; the KoSIT ruleset and UBL syntax are next on the roadmap, precisely because "valid here, rejected there" is where integrators get burned.

Collapse
 
johnfrandsen profile image
John Frandsen

Beautiful — that's the exact fragmentation pattern open banking lives in too. PSD2 RTS sets the floor, but the national transpositions are where you bleed: BaFin's reading of Article 36, the CMA9's SCA exemption scope, Banque de France's AIS licensing quirks. You end up "compliant in one EEA state, rejected in another" for the same reasons Peppol vs ZUGFeRD burns integrators. The pain is identical: build to the directive, ship, then discover 27 edge cases.

Genuinely respect that you're wading into the EN 16931 + KoSIT stack. That's the unglamorous, mostly-thankless work that makes interoperability real — the validators nobody notices are why cross-border e-invoicing works at all.