What BR-E-10 checks
BR-E-10 requires that a VAT breakdown group (BG-23) with category code E, exempt from VAT, contain a VAT exemption reason code (BT-121) or a VAT exemption reason text (BT-120). Exemptions are legal exceptions, insurance, education, healthcare, certain financial services, and the buyer's accounting and the tax authority's audit both need to know which exception applies. The declaration lives in the breakdown group, next to the category code and the 0% rate.
Layer and formats
An en16931 core layer rule, applying to every syntax and profile. It is one of a family: the same reason requirement exists for reverse charge (BR-AE-10), intra-community supply (BR-IC-10), export (BR-G-10) and the out-of-scope category (BR-O-10). If your pipeline handles one of them properly, extend the same mechanism to all.
Why it fails in practice
- Zero VAT modelled as "no VAT data". The source system stores an empty tax field for exempt items, so the export produces a bare E group with nothing to say.
- The wrong category. A genuinely zero-rated supply is coded E instead of Z; suddenly a reason is required that nobody can supply. Correcting the category dissolves the finding.
- The legal text lives outside the breakdown. A note like "exempt under §4 UStG" is printed in the document footer or BT-22 note, invisible to the rule.
How to fix it
State the reason inside the E breakdown group:
<cac:TaxCategory>
<cbc:ID>E</cbc:ID>
<cbc:Percent>0</cbc:Percent>
<cbc:TaxExemptionReason>Exempt insurance services</cbc:TaxExemptionReason>
<cac:TaxScheme><cbc:ID>VAT</cbc:ID></cac:TaxScheme>
</cac:TaxCategory>
In an InvoiceXML create request, set exemptionReasonCode or exemptionReasonText on the matching entry of invoice.vatBreakdowns, or supply the reason with the exempt lines and let the API carry it into the generated breakdown.
The raw rejection vs the InvoiceXML finding
The Schematron report:
<svrl:failed-assert id="BR-E-10" flag="fatal"
location="/*:Invoice[namespace-uri()='...']/cac:TaxTotal[1]/cac:TaxSubtotal[2]">
<svrl:text>[BR-E-10]-A VAT breakdown (BG-23) with VAT Category code
(BT-118) "Exempt from VAT" shall have a VAT exemption reason code
(BT-121) or a VAT exemption reason text (BT-120).</svrl:text>
</svrl:failed-assert>
The InvoiceXML finding for the same failure:
{
"rule": "BR-E-10",
"layer": "en16931",
"line": null,
"message": "The Exempt VAT breakdown is missing a required exemption reason. Add an exemption reason or reason code.",
"btCodes": ["BT-120", "BT-121"],
"fields": null,
"raw": "[BR-E-10] EN16931: A VAT breakdown (BG-23) with VAT Category code (BT-118) \"Exempt from VAT\" shall have a VAT exemption reason code (BT-121) or a VAT exemption reason text (BT-120)."
}
Instead of decoding rule prose and XPaths, your integration shows the message as is and uses the BT codes to route the user to the breakdown inputs. The message table is keyed by rule code, so clients can localize by rule without giving up the mapping.
Check or generate exempt invoices
See all VAT findings for a file in the UBL validator or the XRechnung validator. Generation via POST /v1/create/ubl or POST /v1/create/facturx validates every document against the full rule set before delivery, so an incomplete exemption never reaches your customer. Categories and their rules are covered in EN 16931 explained.