Skip to content

For developers

EN 16931 developer guide: business terms, rules and validation

Last reviewed

EN 16931 defines an invoice as a list of numbered business terms (BT-1, BT-2 …) grouped into business groups (BG-23, BG-25 …), with cardinalities and business rules between them. To produce a compliant file you map each term to one element of a syntax — UBL 2.1 or UN/CEFACT CII — fill every mandatory term, make the totals satisfy the calculation rules (BR-CO-xx), and check the result against the published Schematron before it leaves your system. This guide walks through that in the order a developer meets it.

The model, not the file

The standard's normative part, EN 16931-1, is a semantic data model: it says what an invoice contains and what each element means, without prescribing XML. CEN/TS 16931-2 lists the two syntaxes that must be accepted, UBL 2.1 (Invoice and CreditNote) and UN/CEFACT Cross Industry Invoice (CII) D16B. The syntax bindings in CEN/TS 16931-3 say which element carries which term. Parts 1 and 2 are available free of charge from national standards bodies under an agreement between the European Commission and CEN; the bindings are paid documents, but everything an implementer needs to validate — the Schematron rules — is published openly by the Commission's eInvoicing team on GitHub. For the standard's background, versions and CIUS concept, see EN 16931 explained.

Three consequences shape implementation:

  • Your domain model should carry business terms, not XML paths. A single internal invoice object mapped once to UBL and once to CII is far less work than two hand-written serialisers, and the same object serves XRechnung, ZUGFeRD, Factur-X and Peppol BIS, which are all EN 16931 profiles (CIUS) or extensions. See the formats compared.
  • Cardinality is enforced by Schematron, not only by XSD. An XSD-valid UBL file with no seller name is schema-valid and EN 16931-invalid.
  • The calculation rules are exact to the cent after rounding. Floating-point arithmetic anywhere in the pipeline will eventually produce a BR-CO violation.

The mandatory business terms

The core model has a small mandatory set. Every EN 16931 invoice must carry the following terms; a CIUS such as XRechnung or Peppol BIS adds further requirements on top (buyer reference, seller electronic address, payment means and so on), which are checked by that profile's own rules.

TermNameUBL 2.1 elementCII element (abbreviated)Rule
BT-1Invoice numbercbc:IDram:ExchangedDocument/ram:IDBR-02
BT-2Invoice issue datecbc:IssueDateram:ExchangedDocument/ram:IssueDateTimeBR-03
BT-3Invoice type code (UNTDID 1001, e.g. 380, 381)cbc:InvoiceTypeCoderam:ExchangedDocument/ram:TypeCodeBR-04, BR-CL-01
BT-5Invoice currency code (ISO 4217)cbc:DocumentCurrencyCoderam:InvoiceCurrencyCodeBR-05
BT-24Specification identifier (which CIUS)cbc:CustomizationIDram:GuidelineSpecifiedDocumentContextParameter/ram:IDBR-01
BT-27Seller namecac:AccountingSupplierParty/…/cac:PartyLegalEntity/cbc:RegistrationNameram:SellerTradeParty/ram:NameBR-06
BT-31Seller VAT identifiercac:PartyTaxScheme/cbc:CompanyID (scheme VAT)ram:SellerTradeParty/ram:SpecifiedTaxRegistration/ram:ID[@schemeID='VA']BR-CO-26 (one of BT-29, BT-30, BT-31); BR-CO-09 (country prefix)
BT-35 … BT-40Seller postal address, at least country code (BT-40)cac:PostalAddress/cac:Country/cbc:IdentificationCoderam:PostalTradeAddress/ram:CountryIDBR-08, BR-09
BT-44Buyer namecac:AccountingCustomerParty/…/cbc:RegistrationNameram:BuyerTradeParty/ram:NameBR-07
BT-50 … BT-55Buyer postal address, at least country code (BT-55)cac:PostalAddress/cac:Country/cbc:IdentificationCoderam:PostalTradeAddress/ram:CountryIDBR-10, BR-11
BT-106Sum of invoice line net amountcac:LegalMonetaryTotal/cbc:LineExtensionAmountram:SpecifiedTradeSettlementHeaderMonetarySummation/ram:LineTotalAmountBR-12, BR-CO-10
BT-109Invoice total amount without VATcbc:TaxExclusiveAmountram:TaxBasisTotalAmountBR-13, BR-CO-13
BT-110Invoice total VAT amountcac:TaxTotal/cbc:TaxAmountram:TaxTotalAmountBR-CO-14
BT-112Invoice total amount with VATcbc:TaxInclusiveAmountram:GrandTotalAmountBR-14, BR-CO-15
BT-115Amount due for paymentcbc:PayableAmountram:DuePayableAmountBR-15, BR-CO-16
BG-23VAT breakdown: one group per category and rate, each with BT-116 taxable amount, BT-117 tax amount, BT-118 category code, BT-119 ratecac:TaxTotal/cac:TaxSubtotalram:ApplicableTradeTaxBR-CO-17, BR-CO-18, BR-S/-AE/-E/-Z rules
BG-25Invoice line: BT-126 identifier, BT-129 quantity, BT-130 unit, BT-131 net amount, BT-146 net price, BT-151 VAT category, BT-153 item namecac:InvoiceLineram:IncludedSupplyChainTradeLineItemBR-16, BR-21 … BR-26, BR-CO-04

The full element list, with cardinality and description for every term, is in EN 16931-1 itself and reproduced element by element in the Peppol BIS syntax documentation. The table above is the working minimum, not a substitute for reading it.

The business rules

Rules come in families, identified by prefix. A validator reports the rule identifier, so learn the families and most error messages become self-explanatory.

BR-xx
Structural rules: presence and cardinality. BR-01 "An Invoice shall have a Specification identifier (BT-24)"; BR-02 "An Invoice shall have an Invoice number (BT-1)"; BR-03 the issue date; BR-05 the currency; BR-06 the seller name; BR-07 the buyer name; BR-16 "An Invoice shall have at least one Invoice line (BG-25)".
BR-CO-xx
Conditions and calculations. BR-CO-10: BT-106 = Σ BT-131. BR-CO-13: BT-109 = BT-106 − BT-107 (allowances) + BT-108 (charges). BR-CO-14: BT-110 = Σ BT-117. BR-CO-15: BT-112 = BT-109 + BT-110. BR-CO-16: BT-115 = BT-112 − BT-113 (paid) + BT-114 (rounding). BR-CO-17: BT-117 = BT-116 × BT-119 / 100, rounded to two decimals. BR-CO-04 requires a VAT category code on every line.
BR-S, BR-Z, BR-E, BR-AE, BR-IC, BR-G, BR-O, BR-IG, BR-IP
One family per VAT category code (BT-118/BT-151): S standard rated, Z zero rated, E exempt, AE reverse charge, K intra-Community supply, G export, O not subject to VAT, and the Canary Islands and Ceuta/Melilla taxes. Each family says what the breakdown must contain when a line of that category exists. BR-AE-01: an invoice with a reverse-charge line shall contain exactly one VAT breakdown with category "AE". BR-S-08: for each standard rate, the taxable amount in the breakdown equals the sum of the line net amounts (plus charges, minus allowances) at that rate.
BR-CL-xx
Code-list rules: the type code must come from UNTDID 1001 (BR-CL-01), currency from ISO 4217, country from ISO 3166-1, units from UN/ECE Recommendation 20 and 21, and so on.
UBL-SR-xx, CII-SR-xx
Syntax rules: elements the binding does not use must be absent, and restricted elements must not repeat.

Rule violations have a flag. A fatal failure means the file is not an EN 16931 invoice and a receiver may reject it; a warning means something is unusual but allowed. Treat fatals as build errors.

A minimal UBL invoice

The following file satisfies the core rules for a single standard-rated line at 19 %. Namespaces are the UBL 2.1 ones; CustomizationID names the plain EN 16931 profile. A real XRechnung or Peppol BIS file needs its own CustomizationID, a ProfileID, a buyer reference and payment details in addition.

<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
         xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
         xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
  <cbc:CustomizationID>urn:cen.eu:en16931:2017</cbc:CustomizationID>
  <cbc:ID>RE-2026-0142</cbc:ID>
  <cbc:IssueDate>2026-09-03</cbc:IssueDate>
  <cbc:DueDate>2026-10-03</cbc:DueDate>
  <cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
  <cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
  <cac:AccountingSupplierParty><cac:Party>
    <cac:PostalAddress><cbc:StreetName>Beispielstrasse 1</cbc:StreetName>
      <cbc:CityName>Berlin</cbc:CityName><cbc:PostalZone>10115</cbc:PostalZone>
      <cac:Country><cbc:IdentificationCode>DE</cbc:IdentificationCode></cac:Country>
    </cac:PostalAddress>
    <cac:PartyTaxScheme><cbc:CompanyID>DE123456789</cbc:CompanyID>
      <cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme></cac:PartyTaxScheme>
    <cac:PartyLegalEntity><cbc:RegistrationName>Beispiel GmbH</cbc:RegistrationName></cac:PartyLegalEntity>
  </cac:Party></cac:AccountingSupplierParty>
  <cac:AccountingCustomerParty><cac:Party>
    <cac:PostalAddress><cbc:CityName>Hamburg</cbc:CityName><cbc:PostalZone>20095</cbc:PostalZone>
      <cac:Country><cbc:IdentificationCode>DE</cbc:IdentificationCode></cac:Country>
    </cac:PostalAddress>
    <cac:PartyLegalEntity><cbc:RegistrationName>Kunde AG</cbc:RegistrationName></cac:PartyLegalEntity>
  </cac:Party></cac:AccountingCustomerParty>
  <cac:TaxTotal>
    <cbc:TaxAmount currencyID="EUR">190.00</cbc:TaxAmount>
    <cac:TaxSubtotal>
      <cbc:TaxableAmount currencyID="EUR">1000.00</cbc:TaxableAmount>
      <cbc:TaxAmount currencyID="EUR">190.00</cbc:TaxAmount>
      <cac:TaxCategory><cbc:ID>S</cbc:ID><cbc:Percent>19</cbc:Percent>
        <cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme></cac:TaxCategory>
    </cac:TaxSubtotal>
  </cac:TaxTotal>
  <cac:LegalMonetaryTotal>
    <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount>
    <cbc:TaxExclusiveAmount currencyID="EUR">1000.00</cbc:TaxExclusiveAmount>
    <cbc:TaxInclusiveAmount currencyID="EUR">1190.00</cbc:TaxInclusiveAmount>
    <cbc:PayableAmount currencyID="EUR">1190.00</cbc:PayableAmount>
  </cac:LegalMonetaryTotal>
  <cac:InvoiceLine>
    <cbc:ID>1</cbc:ID>
    <cbc:InvoicedQuantity unitCode="HUR">10</cbc:InvoicedQuantity>
    <cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount>
    <cac:Item><cbc:Name>Consulting</cbc:Name>
      <cac:ClassifiedTaxCategory><cbc:ID>S</cbc:ID><cbc:Percent>19</cbc:Percent>
        <cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme></cac:ClassifiedTaxCategory>
    </cac:Item>
    <cac:Price><cbc:PriceAmount currencyID="EUR">100.00</cbc:PriceAmount></cac:Price>
  </cac:InvoiceLine>
</Invoice>

Walk the totals against the rules: line net 10 × 100.00 = 1000.00 (BT-131); Σ lines = 1000.00 (BR-CO-10); no allowances or charges, so BT-109 = 1000.00 (BR-CO-13); one breakdown, S at 19 %, taxable 1000.00, tax 190.00 (BR-CO-17); BT-110 = 190.00 (BR-CO-14); BT-112 = 1190.00 (BR-CO-15); nothing prepaid, BT-115 = 1190.00 (BR-CO-16).

Validation artefacts and tools

Validate with the same artefacts the receivers use. There is no need to reimplement the rules.

ArtefactMaintainerCoversHow to run
eInvoicing-EN16931 Schematron (UBL and CII), release 1.3.16 of 10 April 2026 at the time of readingEuropean Commission eInvoicing team (ConnectingEurope on GitHub)The EN 16931 core rules: BR, BR-CO, BR-CL, VAT-category families, syntax rulesAny Schematron/XSLT 2.0 processor; the repository also ships compiled XSLT
Peppol BIS Billing 3.0 rules (CEN-EN16931-UBL.sch plus PEPPOL-EN16931-UBL.sch)OpenPeppolThe core rules plus the Peppol CIUS rulesDownloaded from docs.peppol.eu; same processors
XRechnung Schematron and validator configurationKoSITThe core rules plus the German CIUS rules (XRechnung)java -jar validator-*-standalone.jar -s scenarios.xml file.xml; the validator is a generic engine, version 1.6.2 of February 2026, that loads a "scenario" bundle for XRechnung or Peppol
MustangprojectOpen-source community, Apache 2.0Reads, writes and validates ZUGFeRD/Factur-X (CII in PDF/A-3) and XRechnung; checks the PDF/A container as well as the XMLJava library on Maven Central, or the command-line tool; --action validate

A practical pipeline runs XSD first (cheap, catches malformed structure), then the EN 16931 Schematron, then the profile Schematron (XRechnung or Peppol), and, for hybrid formats, a PDF/A-3 check of the container. Wire it into continuous integration with a corpus of your own documents: one per VAT category you support, one with allowances and charges, one credit note, one in a foreign currency with BT-6 set.

Common validation errors and their causes

Most failures come from a handful of causes. Recognising them by rule identifier saves hours.

  • BR-CO-10, BR-CO-13, BR-CO-15 — totals off by 0.01. The line amounts were rounded individually and the totals computed from unrounded values, or vice versa. Decide one rounding point (per line, two decimals, half-up unless the profile says otherwise) and derive every total from the rounded line amounts.
  • BR-CO-17 — breakdown tax amount does not equal taxable × rate. Same cause, applied to the VAT breakdown. Compute BT-117 from BT-116, not by summing line-level VAT.
  • BR-S-08 / BR-AE-08 / BR-E-08 — the breakdown for a category exists but its taxable amount does not match the lines. Usually a document-level allowance or charge was applied to the totals but given no VAT category, or a line's category and the breakdown's category disagree.
  • BR-AE-02 … BR-AE-04 — reverse charge without both parties' identifiers. For category AE the seller VAT ID and the buyer VAT ID (or buyer tax registration) must be present.
  • BR-E-10 / BR-AE-10 — an exemption reason is required. An exempt or reverse-charge breakdown needs a VAT exemption reason text (BT-120) or code (BT-121); a zero-rated breakdown (BR-Z-10), by contrast, must not carry one.
  • BR-CL-xx — a code from the wrong list. "PCE" is not a valid unit; "C62" (one) or "H87" (piece) is. "EURO" is not ISO 4217; "EUR" is. Country codes are two letters, upper case.
  • BR-CO-09 — the seller VAT identifier lacks the country prefix ("123456789" instead of "DE123456789").
  • BR-01 / unrecognised CustomizationID — the profile identifier is missing or names a profile the receiver does not accept. Peppol and XRechnung each require their own exact string.
  • Profile rules (PEPPOL-EN16931-Rxxx, BR-DE-xx) — the core passes but the CIUS fails: missing buyer reference (BR-DE-15), missing seller electronic address, missing payment means, or a Leitweg-ID formatted wrongly. See XRechnung and Peppol.

To check a single file without setting up a toolchain, the free e-invoice checker validates a document against a country's rules and reports the rule identifiers without storing the file.

How KRONENWERK handles this

SUPPORTED KRONENWERK generates the structured document at the moment an invoice is issued and validates it before the number is spent: XRechnung and ZUGFeRD for Germany, checked with the KoSIT Schematron rules and cross-checked with the Mustang library; Factur-X for France; Peppol BIS Billing 3.0 UBL for Belgium; FA(3) for Poland. The VAT category on each line is derived from a stored tax verdict (standard, zero-rated, exempt, reverse charge, outside scope) rather than typed, and a buyer VAT ID is checked against VIES at issuance, so BR-AE and BR-CO-09 failures do not arise from data entry. Incoming XRechnung, ZUGFeRD/Factur-X and UBL files are read into bills. Over the API, POST /invoices/drafts creates a draft and GET /invoices/{number} reads an issued invoice; issuing, and therefore generation and validation, happens in the product. The API does not accept or return raw XML, and it does not validate third-party files — that is what the checker is for. See the e-invoicing product page and the invoicing API.

Frequently asked questions

Do I need to buy the standard to implement it?

EN 16931-1 and CEN/TS 16931-2 are available free of charge through national standards bodies under the Commission–CEN agreement. The syntax bindings (Part 3) are paid, but the published Schematron artefacts and the Peppol and XRechnung documentation cover the mapping in practice.

UBL or CII?

Both are mandatory syntaxes for receivers. Peppol BIS Billing and Belgium use UBL; ZUGFeRD and Factur-X embed CII; XRechnung accepts either. If you must pick one, UBL has the broader network reach and the simpler structure; if you produce hybrid PDFs, you need CII anyway.

Why does my file pass XSD but fail at the receiver?

XSD checks structure, not business rules. Presence of mandatory terms, code lists and the arithmetic between totals are Schematron rules, and receivers run them.

Which rounding does the standard require?

BR-CO-17 says the VAT category tax amount is taxable amount × rate / 100 "rounded to two decimals"; the standard does not otherwise prescribe a rounding algorithm. Keep all amounts at two decimals and derive totals from rounded line amounts.

Is an EN 16931 file automatically a valid XRechnung or Peppol invoice?

No. Those are usage specifications on top of the core: they require additional terms and their own CustomizationID. A core-valid file with the wrong identifier is rejected by both.

Sources

  1. European Commission — Obtaining a copy of the European standard on eInvoicing read on
  2. ConnectingEurope — eInvoicing-EN16931 validation artefacts (Schematron, UBL and CII) read on
  3. OpenPeppol — Peppol BIS Billing 3.0, EN 16931 (TC434) rules for UBL read on
  4. KoSIT — validator (XML validation engine for XRechnung and Peppol scenarios) read on
  5. Mustangproject — open-source Java library for ZUGFeRD, Factur-X and XRechnung read on
  6. KRONENWERK developer documentation read on

Start integrating

Read the quickstart Reference

Read next