DEV Community

Dmytro Yalanskyi
Dmytro Yalanskyi

Posted on Fully Autonomous

I ran 810 public XRechnung and ZUGFeRD sample files through the official validator

Every e-invoice integration I have seen started the same way: someone downloaded a sample file, opened it next to the spec, and built the generator until its output looked like the sample. For German e-invoices that sample is an XRechnung or ZUGFeRD file from the KoSIT test suite, a FeRD package, a library's test folder, or a "download our XRechnung example" link on some vendor's blog.

So I wanted to know: do those samples actually pass the rules a recipient checks today?

Disclosure: I build NormAPI, a hosted XRechnung/ZUGFeRD API with its own validator. The check below uses the official KoSIT validator, not a tool of mine, and the full results are public (link at the end).

The setup

On 19 October 2026 I collected every sample I could download without a form or an account:

  • the official KoSIT XRechnung test suite (the control group)
  • FeRD's current ZUGFeRD samples, release 2.5.2 from 22 September 2026
  • CEN's EN 16931 examples (ConnectingEurope/eInvoicing-EN16931)
  • the sample folders of older FeRD packages (1.0 to 2.4) as they sit in a public GitHub repo
  • the test resources of Mustangproject and horstoeko/zugferd, and the ZUGFeRD/corpus repo
  • the downloads on 12 vendor and guide sites

That gave 1,396 files. I counted only files their publisher presents as valid: a folder called correct or examples, a README sentence, or a download page calling it a "Beispiel". Deliberately broken test files were kept apart. Identical files (same SHA-256) count once, which leaves 810 distinct sample files.

Each one went through KoSIT validator 1.6.3 with the XRechnung configuration v2026-08-31, the current official rule set; for ZUGFeRD PDFs, the embedded XML.

The numbers

  • 548 of 810 pass.
  • 133 are rejected after the rules ran. 94 of those are ZUGFeRD EXTENDED, which is its own story (below).
  • 102 are rejected without a single rule running: 55 declare a version the configuration no longer carries (XRechnung 1.2 to 2.3, ZUGFeRD 1.0), 41 are ZUGFeRD MINIMUM or BASIC WL, and 6 declare an identifier no version of the standard defines.
  • 27 are Peppol BIS, which the XRechnung configuration does not cover, so I left them unjudged.
  • The control group behaves: all 86 KoSIT test-suite files pass.

The interesting part is why the others fail.

1. The official ZUGFeRD samples get two verdicts

FeRD ships a validation report with every sample in release 2.5.2. On 31 of the 53 invoices, KoSIT agrees with it. On 22 it does not:

  • 19 EXTENDED invoices that FeRD's report calls valid are rejected by the KoSIT configuration. The most common reason is CII-DT-027: a FormattedIssueDateTime, which the EN 16931 rules exclude and FeRD's own report does not object to. The KoSIT configuration checks EXTENDED against the EN 16931 core rules, so anything EXTENDED adds on top can trip them. Sub-invoice lines show up as BR-CO-10 and BR-S-08 sum mismatches.
  • 3 invoices that FeRD's report marks as faulty pass KoSIT.

Neither tool is "wrong" here; they check different things. But if your recipient runs the KoSIT configuration, which German public-sector portals and many invoice-processing tools do, your EXTENDED invoice gets rejected while the official sample next to it says it is fine. If you emit EXTENDED, test with the recipient's validator, not the sample's.

2. The rules move, the samples stay

23 files fail on exactly one rule: CII-SR-465, "only one seller contact point (BT-41)". In the CEN rules it is a warning. KoSIT raised it to an error in release v2026-08-31, published on 2 September 2026. The files are mostly the XRECHNUNG samples from FeRD's 2.3 to 2.4.0 packages (2024 to 2025). They were valid when published and stopped being valid on 2 September, without anyone touching them.

Same pattern, older: several EN 16931 samples give an electronic address with scheme 9958, the former identifier for the Leitweg-ID. It was dropped from the EAS code list in favour of 0204, so those files now fail BR-CL-25.

The takeaway is boring and important: a fixture is a snapshot of a rule set. If your test suite copies samples once and never re-validates them, it slowly starts testing against rules that no longer exist.

3. The identifier is the silent failure

The configuration picks its rules from the profile identifier (cbc:CustomizationID in UBL, GuidelineSpecifiedDocumentContextParameter/ram:ID in CII). If no scenario matches, nothing is checked: the file is rejected with an empty list of rule findings. That is the worst kind of error report, because there is nothing to search for.

Things I found in files offered as valid samples:

  • urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_3.0: the namespace of XRechnung 1.x/2.x with the version number 3.0. No version defines this.
  • urn:cen.eu:EN16931:2017#compliant#urn:zugferd.de:2p0:basic: right idea, but "EN16931" in capitals doesn't match.
  • plenty of xrechnung_2.1, xrechnung_1.2 and ZUGFeRD 1.0, which the current configuration no longer carries.

For reference, the identifiers the current configuration matches:

XRechnung 3.0           urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0
EN 16931 (ZUGFeRD EN16931 profile, plain CII/UBL)
                        urn:cen.eu:en16931:2017
Factur-X/ZUGFeRD BASIC  urn:cen.eu:en16931:2017#compliant#urn:factur-x.eu:1p0:basic
Factur-X/ZUGFeRD EXTENDED
                        urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended
Enter fullscreen mode Exit fullscreen mode

MINIMUM and BASIC WL also match no scenario, and that one is correct: under German rules they are not e-invoices. One sample site labels them exactly that way, which is the right way to publish them.

4. Vendor download pages are mostly fine

The 12 vendor and guide sites offered 57 files between them, and 48 pass. The rest fail in ways that would also fail in production: an empty element (<cbc:EndpointID schemeID="EM"/>, which trips PEPPOL-EN16931-R008), a missing buyer reference (BR-DE-15), the invented identifier above, and an old ZUGFeRD 1.0 file. The study lists them per site.

What I'd do if I were starting an integration today

  1. Take fixtures from the source. For XRechnung, the KoSIT test suite: every file passed, and it is republished with every rule-set release. For ZUGFeRD, the BASIC and EN 16931 samples from FeRD's current package: all 28 pass KoSIT.
  2. Re-validate fixtures on every KoSIT release. Pin the configuration version in CI and treat a new release as a dependency bump: run the whole corpus, read the diff. CII-SR-465 is what happens when you don't.
  3. Assert the scenario, not just the verdict. "No errors" with no matching scenario means "not checked". Make the test fail if the scenario is not the one you expect.
  4. Treat EXTENDED as a bilateral agreement. If you need it, agree it with the recipient and test with their validator.

Data

Every file is one row in a CSV: publisher, URL (commit-pinned for git sources), SHA-256, declared profile, scenario, verdict and rule codes. That includes the deliberately broken files I excluded, so you can check my counting. CC BY 4.0.

If you publish one of these samples and I got something wrong, tell me and I'll correct it, with a date.

This article was created with the help of AI.

Top comments (0)