Validate an EN 16931 e-invoice without handing it to anyone
Paste or drop a Factur-X, ZUGFeRD, XRechnung or Peppol invoice XML and get the official European Commission business rule report back. The checker runs in this page, on your machine — your invoice is never uploaded.
Why it exists. Structured e-invoicing has stopped being optional: every business in Germany must be able to receive one since January 2025, with sending phased in during 2027–28, and France follows in 2026–27. So millions of invoices now have to satisfy a rulebook nobody reads, and the validators that do exist mostly want the file uploaded to someone’s server, or want a subscription. This is that rulebook as a static page — the same engine that runs the command-line tool and the Go library, with no account and no upload.
Invoice XML
or drag a file onto the box, or paste below.
the engine starts on your first check
Report
Nothing to report yet. The engine loads once, then every check is local.
What the invoice says
The same bytes read back as an invoice instead of as a rule report: the number, the parties, the amounts and the lines exactly as the document states them. Nothing is recomputed here, so a total that disagrees with its lines stays on the page next to the rule that says so.
The same document read back as an invoice: number, parties, amounts and lines exactly as written.
Converted document
Nothing converted yet. Conversion runs here, in this tab, on the bytes you pasted — nothing is uploaded and nothing is stored.
What is checked
The page runs the same engine as the InvoiceKit Go library and CLI: an XPath 2.0 evaluator and Schematron runner written from scratch, with the official CEF validation artefacts built in. No Java, no XSLT, no server call.
| Syntax | Phases | Examples |
|---|---|---|
| CII — Factur-X, ZUGFeRD, XRechnung | EN16931-model-phase, codelist_phase, syntax_phase |
BR-21 invoice number, BR-CO-10 line total vs document total, BR-DEC-04 VAT category totals |
| UBL — Peppol BIS, OpenPeppol | EN16931model_phase, codelist_phase |
BR-32 payable amount, BR-54 buyer country code, BR-ABC-04 allowance arithmetic |
| Rule set | How it applies | Examples |
|---|---|---|
| XRechnung CIUS 2.6.0 (XRechnung 3.0.2), Germany | Adds national rules on top of EN 16931 | BR-DE-15 buyer reference, BR-DE-21 XRechnung specification identifier, BR-DE-28 tax number format |
| Peppol BIS Billing 3.0, release 3.0.21 | Runs the network's own copy of EN 16931 in place of ours, so the report matches what an access point says | PEPPOL-EN16931-P0100 invoice type code, PEPPOL-EN16931-CL001 media type, PEPPOL-COMMON-R040 GLN check digit |
Accuracy is measured, not claimed. The engine reports zero findings on all 44 documents in the CEF reference sets (29 UBL, 15 CII), and passes 2,165 third-party conformance documents — 1,140 for EN 16931, 494 Peppol, 531 national — in which 953 named rules must fire and 1,064 must not. Mutations tell the other half of the story: on 148 doctored invoices, every expected finding appeared.
Rule coverage is the honest part. Every assert in the rule sets we ship has an
ID, and the same tooling counts how many have actually been seen firing. The
model-phase rules people care about — the BR-* families — are
proven on 184 of 201 UBL and 105 of 200 CII asserts; codelist rules are
complete (22 of 22, and 22 of 23). The rest are syntax and datatype asserts
(UBL-CR-*, UBL-DT-*), proven on 65 of 759 and 44 of
587, where the schema check usually reports the same problem first. Every
unproven ID is listed by name in the repository, and the list shrinks with
each mutation run.
Structural validity against the syntax’s XML schema is reported separately from
the business rules, in the schema section of the report. Factur-X and
ZUGFeRD PDF envelopes are audited on their own page,
the Factur-X checker. What is not checked
here is delivery — an access point's transport rules — and further national
schemes are added one at a time. Conformance checking is not legal or tax
advice.
Run it in your pipeline instead
Browsers are for a quick look. For invoices you generate every day, put the checker where the invoices are made.
Command line
einv validate invoice.xml
einv check dist/invoices/*.pdf
Exit 0 clean, 1 findings that get a document
rejected, 2 unreadable — so it fails a build rather than a customer.
-json for machine output.
Go
go get github.com/dcnws/einvoice
One import, no CGO, no external processes.
einvoice.Validate(data, einvoice.Options{}).
TypeScript
npm install @invoicekit/einvoice
The same WebAssembly module, packaged with types, for Node and the browser.
Status: the CLI, Go module and npm package are built from the engine running
on this page but are not published yet — github.com/dcnws/einvoice
goes public with the hosted API. The page itself is free to use either way.
Frequently asked
What is EN 16931?
The European norm defining the core semantic elements of an electronic invoice — what a seller, a VAT breakdown, a due date or a line allowance have to mean. National and private schemes (Factur-X, ZUGFeRD, XRechnung, Peppol BIS, Norway’s EHF, and others) are constrained subsets of the same model, which is why a single validator can serve all of them.
Is my invoice uploaded, stored or logged?
No. The validator is a WebAssembly binary that runs in this tab. Input never leaves your machine; there is no backend for it to travel to. The page works with the network unplugged after it has loaded.
My document is rejected as “not an EN 16931 invoice”. Why?
The root element must be a CII rsm:CrossIndustryInvoice, an OASIS
UBL Invoice or CreditNote. A Factur-X PDF is not XML —
the Factur-X checker reads the
factur-x.xml attachment out of the PDF for you. Orders, quotations and
credit memos in other formats are not invoices.
Why does it pass here but fail at my access point?
Access points validate XML schema validity first, then EN 16931, then a national or profile layer. Pick the matching layer in the Rule set dropdown: Peppol BIS Billing 3.0 runs the artefacts OpenPEPPOL publishes, XRechnung runs the German CIUS on top of the norm, and both check the XML schema first. Other national layers (EHF, the Nordic profiles, France’s) are not in this build. Compare the failing rule IDs — an id that appears nowhere in the report comes from a layer this build does not carry. If the document is a PDF, the Factur-X checker covers the envelope checks access points run before any of that.
Can you convert my invoice between CII and UBL?
Yes. Convert to the other syntax rewrites a UN/CEFACT CII document as OASIS UBL, or a UBL document as CII, in this tab, and then validates the document it wrote against the rule set in the dropdown — so a German XRechnung turned into UBL for a Peppol supplier is judged by the artefacts that supplier’s access point will run. Both syntaxes are encodings of the same EN 16931 model, so conversion is normally lossless. When the source carries something the model has no term for, the page lists what did not survive instead of handing back a document that quietly dropped it.
Why show the invoice as well as the rules?
Because the expensive mistake is a document that is rejected for one total while it states another. The rule report answers “does this conform”; the table beside it answers “what does it say”, and reading XML to find out is how a mismatch survives all the way to a payment run. The figures shown are the document’s own — this page would rather show you a wrong total than silently fix it, because a corrected total is a new invoice and not your invoice.
Is the tool really free?
Yes, and it needs no account. The engine is open source (MIT, with the European Commission’s EUPL-1.2 rule artefacts embedded unmodified). Paid work is elsewhere: hosted pipelines, batch checking and generation. If that changes anything about this page, it will say so here.