Validation & rules

Schematron: how business rules in e-invoices are actually checked

Schematron is an ISO-standardised rule language that uses XPath expressions to check whether the content of an XML document is factually correct. Unlike an XSD, which checks only the shape, Schematron can make statements about relationships — for instance that line totals equal the net amount. Every e-invoicing rule set is written in Schematron.

Also:
ISO Schematron · sch · Schematron-Regel
Reviewed on 2026-08-26.

Why a second layer of checking

An XSD can do a lot: it fixes which elements may appear, in what order, how often, and with what data type. What it cannot do is make a statement about relationships.

"The total net amount must equal the sum of the line net amounts" is not a question of structure. Both fields are present, both are of the right type, both sit in the right place. The invoice can still be wrong.

That is what Schematron is for.

What a rule looks like

Schematron rules are remarkably readable once you know the pattern:

xml
1<rule context="cac:AccountingSupplierParty/cac:Party">
2 <assert test="cac:Contact" flag="fatal" id="DE-R-002">
3 The group "SELLER CONTACT" (BG-6) shall be provided.
4 </assert>
5</rule>

Four parts, and each is useful:

  • context — where in the document the rule applies. Here: at the seller's party.
  • test — the XPath expression that must be true. Here: a cac:Contact must exist.
  • flag — the severity. fatal rejects, warning notes.
  • id — the identifier the violation is reported under.

The text between is the message you see in the result. It names the field in BT/BG notation, because the same rule has to hold for CII and UBL — the element names differ, the BG number does not.

A rule that calculates

xml
1<assert test="xs:decimal(cbc:LineExtensionAmount) =
2 round(10 * 10 * sum(//cac:InvoiceLine/cbc:LineExtensionAmount)) div 100"
3 flag="fatal" id="BR-CO-10">
4 Sum of Invoice line net amount (BT-106) = Σ Invoice line net amount (BT-131).
5</assert>

Here you can see why an XSD would not have sufficed: the test sums across all lines and compares the result with a field somewhere else entirely.

🔵 You can also see why rounding is so often the problem. The expression rounds explicitly to two decimal places. Summing unrounded values and rounding only the result lands a cent off on invoices with many lines — and the rule is fatal.

The rule families

A document is never checked against a single rule set but against a stack. The prefix tells you who wrote the rule:

PrefixOriginExample
BR-core of EN 16931BR-11 country code in the buyer address
BR-CO-arithmetic rules of the standardBR-CO-10 line totals
BR-DE-, DE-R-German add-onsDE-R-001 payment instructions
PEPPOL-the Peppol frameworkrules for BIS Billing
UBL-CR-, CII-SR-syntax-specificcode lists, attributes

A document must pass all that apply. That is why checking against EN 16931 is not enough when the destination is a German public authority.

Reading a message

text
1[BR-DE-2] fatal
2The group "SELLER CONTACT" (BG-6) shall be provided.

Step 1 — severity. fatal? Then that is the reason for rejection. warning? Then the cause is elsewhere.

Step 2 — prefix. BR-DE- says: German add-on rule. Your document may satisfy the European standard perfectly and fail only the national tightening.

Step 3 — BT/BG number. BG-6 is the seller's contact group. Search your system for whatever fills that group — not for the element name.

Step 4 — if needed, read the test. Where the message stays ambiguous, the XPath expression is the binding answer. It says exactly where the check looked.

Why rule sets have versions

Schematron rule sets are maintained, and a new version changes not only rules but occasionally severities. A rule that was a warning last year can be fatal this year.

An uncomfortable but important sentence follows for practice: an invoice that went through is no assurance for the next one. Validate against the version your recipient runs — not the one that was current when you built your integration.

Frequently asked questions

Try it right here

Upload a real e-invoice and see which rules fire. No account needed, and the file does not leave the validation run.

Processed in EU data centers · no persistent storage · GDPR-compliant.

Need this automated?

Generate, validate and parse e-invoices via API — 20 API calls/month free, no credit card.