The short answer
BR-47 fails for each cac:TaxSubtotal whose cac:TaxCategory has no cbc:ID, or whose tax category is not under the VAT tax scheme. Add cbc:ID as the first child of cac:TaxCategory, carrying the category of the lines, allowances and charges that the breakdown totals: S in the recorded example.
A breakdown without a code matches no category, so the category rule for the lines it should cover fires as well; here that is BR-S-01.
What the rule checks
The rule runs on every cac:TaxSubtotal inside a cac:TaxTotal. It passes when the subtotal has a cac:TaxCategory whose cac:TaxScheme/cbc:ID reads VAT and that category contains a cbc:ID element.
The tax scheme is trimmed and upper-cased before it is compared, so vat was accepted when tried. Another scheme is not: a breakdown under GST reported this rule, BR-48 and BR-CO-17, because its category and rate were no longer read as VAT at all.
Only the presence of the element is tested. An empty cbc:ID passes this rule, but when tried it was reported by BR-CL-17 as an invalid code, by PEPPOL-EN16931-R008 as an empty element, and by BR-S-01.
Leaving out the whole cac:TaxCategory does not reach this rule. UBL requires one in every subtotal, and when tried the document failed the XSD layer instead.
| Term | Meaning | UBL element |
|---|---|---|
| BG-23 | VAT breakdown | cac:TaxTotal/cac:TaxSubtotal |
| BT-118 | VAT category code | cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:ID |
| - | Tax scheme of the VAT category | cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID |
How an integration ends up here
Possible causes, from the shape of the rule rather than from measured usage:
- The VAT summary is keyed by rate alone, so the export knows 20% but not which category the rate belongs to.
- The category comes from a mapping of internal tax codes, and a code without a mapping is written as nothing instead of stopping the export.
- The tax scheme is filled from a generic tax type such as
GSTorTAXrather than the fixed valueVAT. - The serialiser writes
cac:TaxCategorywith onlycbc:Percentandcac:TaxScheme, following a layout meant for another format.
How to fix it
- For each breakdown, find the VAT category of the items it totals: the
cac:ClassifiedTaxCategory/cbc:IDof its lines and thecac:TaxCategory/cbc:IDof its document-level allowances and charges. - Write that code as
cbc:ID, the first child of the breakdowncac:TaxCategory, beforecbc:Percent. Use one of the permitted codes:S,Z,E,AE,K,G,O,L,MorB. - Set
cac:TaxScheme/cbc:IDinside the same tax category toVAT. - Make an unmapped tax code stop the export, so a breakdown without a category is never sent.
Validate your corrected invoice
Before and after
These are fragments, not complete documents. The complete synthetic documents they come from are linked below.
Fragment of the failing invoice: the Standard rated line is fine, but its breakdown has no category code
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="GBP">25.00</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="GBP">5.00</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:Percent>20</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
<cac:Item>
<cbc:Name>Example service</cbc:Name>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>20</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>Fragment of the corrected invoice: the breakdown is Standard rated, like its line
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="GBP">25.00</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="GBP">5.00</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>20</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>The corrected invoice has <cbc:ID>S</cbc:ID> as the first child of the breakdown cac:TaxCategory; the failing one does not, and nothing else differs. The failing document also reports BR-S-01, because its line is Standard rated and, with the code gone, no breakdown says S. Adding the code clears both. The arithmetic rules stay silent: the rate and amounts are still present, and the Standard rated checks only run on a breakdown coded S.
What the validator reported
- The failing invoice reports BR-47 and BR-S-01. The corrected document passes every layer with no findings.Download the failing XMLDownload the corrected XML
Recorded on phive 12.1.0 / phive-rules-peppol 4.5.6 / Saxon-HE 12.10, the engine behind the free validator, using synthetic data. A recorded result is regression evidence for these documents; it is not a certification.
Where it applies
- Applies to UBL
InvoiceandCreditNote. A credit note with the code removed from its breakdown reported the same two rules when tried. - Reported by the EN 16931 layer; the Peppol layer passes the recorded example.
- The companion rule follows the category of the lines. With the
Eremoved from an exempt breakdown, the findings were this rule andBR-E-01when tried. - The line-level counterpart is
BR-CO-04. Once a breakdown code is present,BR-CL-17checks that it is a permitted one.
Related rules
- BR-S-01 needs a breakdown coded S for Standard rated lines and is reported with this rule
- BR-CL-17 checks that the breakdown category code is one of the permitted codes
- BR-CO-04 is the same requirement for the VAT category on each line
- BR-48 requires the rate beside the category code in the same tax category
- Browse every rule in the reference
- Background: Understanding EN 16931 validation errors
Scope and source
Written for Peppol BIS Billing 3.0.21 (May 2026), EN 16931 1.3.16, as applied to UBL 2.1 Invoice and CreditNote documents. Other profiles, syntaxes and releases can define this identifier differently. Guidance version 2026-09-24.1: source checked 2026-09-24, explanation last updated 2026-09-24.
The official definition of BR-47 carries the normative wording and test. This page is our explanation of it, not a copy.
Guidance does not change the engine verdict. Fixing this finding does not mean the document passes every layer, and validation does not certify legal or tax compliance or transmit a document over Peppol.
