On this page
The short answer
PEPPOL-EN16931-P0104 fails for each cac:TaxCategory that carries VATEX-EU-G while its cbc:ID is not G. The code says the supply is an export outside the EU, and EN 16931 gives exports a category of their own, G, apart from exempt and zero rated supplies. If the goods leave the EU, set the category to G; if they do not, it is the export code that is wrong.
In the recorded example an invoice to a buyer in New York keeps the export code and the text Export outside the EU but puts its breakdown and its line in category E. The EN 16931 layer accepts that, because an exempt breakdown with a reason is complete; only the Peppol layer sees that the reason contradicts the category.
What the rule checks
The rule picks out a cac:TaxCategory by its exemption code, upper-cased, and passes it only when the category, trimmed, is G. When tried, vatex-eu-g in the exempt breakdown was reported just as the upper-case code was.
The code itself is not trimmed. When tried, VATEX-EU-G with a space before it, or after it, in the same exempt breakdown passed every layer: BR-CL-22 trims the value and accepts it, and this rule no longer recognises it. The padding hides the mismatch without correcting it.
Other wrong categories fail the same way, each with its own rules added. When tried, a Zero rated breakdown with VATEX-EU-G reported this rule and BR-Z-10, which allows no exemption reason there, and a Not subject to VAT breakdown carrying the code reported this rule alone.
Nothing in the check reads the buyer or the delivery address, so the rule cannot tell whether the goods really left the EU. It only holds the code and the category to each other.
| Term | Meaning | UBL element |
|---|---|---|
| BT-121 | VAT exemption reason code | cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:TaxExemptionReasonCode |
| BT-118 | VAT category code | cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:ID |
| BT-151 | Invoiced item VAT category code | cac:InvoiceLine/cac:Item/cac:ClassifiedTaxCategory/cbc:ID |
How an integration ends up here
Possible causes, from the shape of the rule rather than from measured usage:
- Sales outside the EU are booked as exempt because no VAT is charged, while the exemption code comes from a separate export flag.
- In UK VAT terms exports of goods are zero-rated, so they are mapped to
Z, and the EN 16931 export categoryGis never used. - One exemption code is set per customer, so a customer abroad gets
VATEX-EU-Gon every breakdown, including services taxed in another way.
How to fix it
- Check whether the supply is an export of goods leaving the EU, with the evidence your VAT rules require. That decision, not the absence of VAT, chooses the category.
- If it is an export, set
cbc:IDtoGon the breakdown and on every line, allowance and charge it covers, with the rate0, and keepVATEX-EU-G. The corrected example needed no other change: its seller VAT identifier was already there forBR-G-02. - If it is not an export, keep the real category and replace
VATEX-EU-Gwith the code for that treatment, or remove the code and give reason text, asBR-E-10allows on an exempt breakdown. On a Zero rated or Standard rated breakdown, remove the code and give no reason text either:BR-Z-10andBR-S-10allow neither. - Take the category and the exemption code from one tax determination, so a later change to one cannot leave the other behind.
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: an export coded exempt, E, in the breakdown and on the line
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="GBP">25.00</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="GBP">0.00</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>E</cbc:ID>
<cbc:Percent>0</cbc:Percent>
<cbc:TaxExemptionReasonCode>VATEX-EU-G</cbc:TaxExemptionReasonCode>
<cbc:TaxExemptionReason>Export outside the EU</cbc:TaxExemptionReason>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
<!-- totals and the start of the invoice line omitted from this fragment -->
<cac:ClassifiedTaxCategory>
<cbc:ID>E</cbc:ID>
<cbc:Percent>0</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>Fragment of the corrected invoice: the export is category G in both places
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="GBP">25.00</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="GBP">0.00</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>G</cbc:ID>
<cbc:Percent>0</cbc:Percent>
<cbc:TaxExemptionReasonCode>VATEX-EU-G</cbc:TaxExemptionReasonCode>
<cbc:TaxExemptionReason>Export outside the EU</cbc:TaxExemptionReason>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
<!-- totals and the start of the invoice line omitted from this fragment -->
<cac:ClassifiedTaxCategory>
<cbc:ID>G</cbc:ID>
<cbc:Percent>0</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>The failing invoice has E where the corrected one has G, in the breakdown and in the line category; the code VATEX-EU-G, the reason text, the buyer in the United States and the amounts are the same in both. The failing document reports only this Peppol rule, since an exempt breakdown with a reason and a zero rate satisfies every EN 16931 rule.
What the validator reported
- The failing invoice reports PEPPOL-EN16931-P0104. 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. An exempt credit note carryingVATEX-EU-Greported this rule when tried. - Document-level allowances and charges carry a
cac:TaxCategorytoo, so a code there is checked in the same way. The line category is acac:ClassifiedTaxCategory, which this rule does not read. - A Peppol BIS Billing rule on the Peppol layer. On the EN 16931 layer,
BR-CL-22confirms only thatVATEX-EU-Gis a listed code.
Related rules
- BR-G-10 requires an exemption reason on an export breakdown, which VATEX-EU-G provides
- PEPPOL-EN16931-P0106 ties the intra-community code VATEX-EU-IC to category K in the same way
- PEPPOL-EN16931-P0110 catches the opposite mix-up, a works of art margin scheme code in an export breakdown
- BR-Z-10 reports VATEX-EU-G a second time when an export is coded Zero rated
- Browse every rule in the reference
- Background: How Peppol invoice validation actually works
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-28.1: source checked 2026-09-28, explanation last updated 2026-09-28.
The official definition of PEPPOL-EN16931-P0104 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.

