Automation MCP Server Features Blog Pricing Contact
Sample files XRechnung 3.0

XRechnung sample files

A valid XRechnung 3.0 invoice in each of the two syntaxes, UBL and CII, plus fixtures that break one German rule each. Every file is generated by the InvoiceXML API and validated against the KoSIT rule set before it is published.

XRechnung is not a file format. It is a rule set: the German CIUS of EN 16931, which takes the European core invoice and makes a handful of optional things mandatory. That has one consequence that surprises people building their first integration, and it shapes this page.

Because XRechnung constrains the data model rather than the file, it exists in two syntaxes. The same invoice can be expressed as OASIS UBL or as UN/CEFACT CII, both are equally valid XRechnung, both declare the same specification identifier, and German receiving portals accept either. So there is no ladder of profiles here the way there is in Factur-X. There is one profile in two shapes.

The two syntaxes

Both files below carry the identical invoice and declare the identical BT-24:

urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0

Note the shape of that string if you are migrating from XRechnung 2.x. Version 3.0 moved the registrar segment from xoev-de to xeinkauf.de and dropped the standard: segment that the 1.2 to 2.3 identifiers carried, so a substring match written against the old form will not recognise a 3.0 document. The customization identifier reference has the full history.

Diff them against each other and the mapping is a useful thing to have read once: cac:AccountingSupplierParty against ram:SellerTradeParty, cbc:CustomizationID against ram:GuidelineSpecifiedDocumentContextParameter/ram:ID, and the whole of UBL's flat cac:InvoiceLine against CII's nested ram:IncludedSupplyChainTradeLineItem. Same invoice, same business terms, entirely different element names.

The invoice itself is a three-line services invoice from a German seller to a French buyer, with a document-level discount, a freight charge, a line-level surcharge, an invoicing period, and payment by SEPA credit transfer.

What XRechnung makes mandatory

If you already produce valid EN 16931 invoices, the German rules are the gap you have to close. These are the ones the samples exercise:

RuleRequiresBusiness term
BR-DE-15A buyer reference, which is the Leitweg-ID in public-sector invoicingBT-10
BR-DE-2A seller contact group, with name, phone and email inside itBG-6
BR-DE-1Payment instructionsBG-16
BR-DE-21The specification identifier to match the XRechnung one exactlyBT-24

Plain EN 16931 leaves every one of these optional, which is why a generic EN 16931 check is a poor predictor of German acceptance. It passes documents a German portal will reject, and testing against an EN 16931 sample tells you nothing about whether your XRechnung output gets through. Anything aimed at Germany has to run the KoSIT rule set on top of the European one, which is exactly what the XRechnung validator here does: schema first, then the EN 16931 rules, then XRechnung 3.0.2. The fixtures below fail on that third layer and nowhere else.

Files that are wrong on purpose

Each of these breaks exactly one German rule and nothing else, which is what makes them useful for pinning your error handling. We assert that when generating them: if removing the field knocked out a second BR-DE rule as a side effect, the file would not be published.

BR-DE-15 is published in both syntaxes deliberately. It is the most commonly hit XRechnung rejection, and an integration is only ever built against one syntax, so shipping the fixture in one form would leave half of you without a test file. Comparing the two is also the cheapest way to see that the German rules are defined on business terms rather than on elements: the same rule fires on both, and only the XPath in the error message differs.

All three remain schema-valid, so an XSD check passes them. Only the Schematron layer catches these, which is the point: if your validator returns green on them, it is not running the KoSIT rules and it will not predict what a German portal does with your invoices.

How these files were generated

Both versions come from the same invoice JSON through the same endpoint. Nothing about the invoice changes between them, only which binding you ask for, and UBL is what you get if you do not ask:

curl -X POST https://api.invoicexml.com/v1/create/xrechnung \
  -H "Authorization: Bearer $INVOICEXML_API_KEY" \
  -H "Content-Type: application/json" \
  -d @invoice.json \
  --output xrechnung-ubl.xml

For the CII binding, add one option to the request body:

{
  "invoice": { "invoiceNumber": "DEMO-2026-0042", "...": "..." },
  "options": { "syntax": "cii" }
}

Every file is then run through the validator against the KoSIT rules before publishing, which is how we know the valid ones are valid and each broken one breaks on exactly one rule. To check a file of your own, the XRechnung validator takes an upload directly and reports the rule identifiers, not just a pass or fail.

Why sample files go stale

These were generated on 6 August 2026, against KoSIT XRechnung 3.0.2 and the EN 16931 CII and UBL rules 1.3.16.

XRechnung is the fastest-moving rule set in this family. KoSIT ships twice a year, typically publishing in January and July and putting the release in force the following month, and each one can add rules, tighten existing ones, or mint a new specification identifier that your BT-24 string match has never seen. A file that a German portal accepted last spring can be rejected this autumn without anything in your code changing.

That cadence is the real cost of owning this in-house. The XML is the easy part. Tracking KoSIT twice a year, re-vendoring the Schematron, re-running your corpus and shipping a release before the in-force date, indefinitely, is the part nobody scopes for.

On the InvoiceXML API the current rule set is simply what runs: no SDK to upgrade, no redeploy when KoSIT publishes, no version matrix to reason about. If that trade appeals, start with the free trial, or read how the XRechnung API handles both syntaxes end to end.

Frequently asked questions

Are these XRechnung sample files free to use?

Yes. They are our own output, not a copy of the KoSIT test suite, so there are no licence terms attached. Use them in test suites, CI fixtures, demos or documentation without asking. They contain fictional parties, a deliberately unroutable Leitweg-ID and invented amounts, so they are not valid commercial invoices.

Should I test against the UBL or the CII sample?

Whichever your system produces or receives. Both are equally valid XRechnung, both declare the same specification identifier, and German receiving portals accept either. If you are building on Peppol you will most often see UBL; if you are coming from a ZUGFeRD or Factur-X background you are already producing CII. With no constraint either way, UBL is the reasonable default and the one the InvoiceXML API returns unless you ask for CII.

What is the difference between XRechnung and ZUGFeRD or Factur-X?

XRechnung is a rule set, the German CIUS of EN 16931, and it is plain XML. Factur-X and ZUGFeRD are a container format, a PDF/A-3 with XML embedded inside it. They are not alternatives at the same level: a ZUGFeRD file can carry XRechnung-conformant CII as its payload. XRechnung adds the BR-DE rules that neither Factur-X nor plain EN 16931 enforces.

Why does the sample carry a buyer reference that looks like a Leitweg-ID?

Because BT-10 is mandatory in XRechnung (BR-DE-15) and in German public-sector invoicing it carries the Leitweg-ID that routes the invoice to the right office. Ours is shaped like one so it exercises any code that parses the format, but the 99 prefix is not a valid German Regionalschlüssel, so it can never address a real authority.

Do the BR-DE rules differ between the UBL and CII versions?

No. The German rules are defined on the EN 16931 business terms, not on a syntax, so BR-DE-15 fires on a missing BT-10 whichever syntax carries it. That is why the invalid BR-DE-15 fixture is published in both: the rule is the same, only the XPath in the error differs.