# BR-CL-17: Use a permitted VAT category code in each tax category

Every `cac:TaxCategory/cbc:ID` must be one of `S`, `Z`, `E`, `AE`, `K`, `G`, `O`, `L`, `M` or `B`. Internal tax codes such as `SR` are rejected.

- Layer: EN 16931
- Severity: fatal (the document is invalid)
- Topics: VAT, Code lists
- Official definition: https://docs.peppol.eu/poacc/billing/3.0/rules/ubl-tc434/BR-CL-17/
- Explanation last updated: 2026-09-24

## The short answer

`BR-CL-17` fails for each `cac:TaxCategory/cbc:ID` whose value is not a permitted VAT category code. That element appears in the VAT breakdown and on document-level allowances and charges, and the finding points at the offending `cbc:ID`. Replace the value with the matching code: in the recorded example the breakdown says `SR` where Standard rated is `S`.

Translate every internal tax code to one of the ten permitted codes before export. The codes on lines are checked separately, by `BR-CL-18`.

## What the rule checks

The rule runs on every `cbc:ID` that is a direct child of a `cac:TaxCategory`, wherever that tax category sits. It does not read `cac:ClassifiedTaxCategory` on lines.

The accepted values are `AE`, `L`, `M`, `E`, `S`, `Z`, `G`, `O`, `K` and `B`. Surrounding spaces are ignored, but a space inside the value, the wrong letter case or any other code fails: when tried, `s`, `A` and `20` were all rejected.

An empty `cbc:ID` fails this rule as well, and when tried it also brought `PEPPOL-EN16931-R008` for the empty element.

Membership of the list is all that is checked. Whether the category fits the items is left to the category rules, such as `BR-S-01` and `BR-S-08` for Standard rated.

| Term | Meaning | UBL element |
|---|---|---|
| BT-118 | VAT category code | `cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:ID` |
| BT-95 | Document level allowance VAT category code | `cac:AllowanceCharge[cbc:ChargeIndicator = false]/cac:TaxCategory/cbc:ID` |
| BT-102 | Document level charge VAT category code | `cac:AllowanceCharge[cbc:ChargeIndicator = true]/cac:TaxCategory/cbc:ID` |

## How an integration ends up here

Possible causes, from the shape of the rule rather than from measured usage:

- An accounting system tax code such as `SR`, `T1` or `STD` is written straight into the XML instead of being translated.
- The code is lower-cased somewhere in the pipeline.
- A code is taken from the full UNCL5305 list, which has codes this rule does not accept, such as `A`.
- A rate, such as `20`, is written in the category code field.

## How to fix it

1. Use the finding location to see which tax category holds the bad value: a breakdown under `cac:TaxTotal/cac:TaxSubtotal`, or a root-level `cac:AllowanceCharge`.
2. Map the internal code to its VAT category: `S` Standard rated, `Z` Zero rated, `E` exempt, `AE` reverse charge, `K` intra-community supply, `G` export outside the EU, `O` not subject to VAT, `L` and `M` for the Canary Islands and Ceuta and Melilla taxes, `B` for Italian split payment.
3. Use the same code on the lines, allowances, charges and breakdown for the same supply, so the breakdown still matches the items it totals.
4. Keep the mapping in one table and fail the export on any code it does not contain.

## 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 line is coded S, the breakdown SR

```xml
<cac:TaxSubtotal>
  <cbc:TaxableAmount currencyID="GBP">25.00</cbc:TaxableAmount>
  <cbc:TaxAmount currencyID="GBP">5.00</cbc:TaxAmount>
  <cac:TaxCategory>
    <cbc:ID>SR</cbc:ID>
    <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>
    <!-- tax scheme omitted from this fragment -->
  </cac:ClassifiedTaxCategory>
</cac:Item>
```

Fragment of the corrected invoice: the breakdown uses the Standard rated code S

```xml
<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 failing invoice writes `SR` in the breakdown `cac:TaxCategory/cbc:ID` and the corrected one writes `S`; that is the only difference. The failing document also reports `BR-S-01`: its line is still Standard rated, and a breakdown coded `SR` does not count as a Standard rated breakdown. Correcting the code clears both.

### What the validator reported

- The failing invoice reports **BR-CL-17** and [BR-S-01](https://ironfang.uk/docs/finance/rules/BR-S-01.md). The corrected document passes every layer with no findings.
  - [Download the failing XML](https://ironfang.uk/finance/rule-examples/BR-CL-17-invalid.xml)
  - [Download the corrected XML](https://ironfang.uk/finance/rule-examples/invoice-minimal.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 `Invoice` and `CreditNote`. A credit note with `SR` in its breakdown reported the same two rules when tried.
- On a document-level allowance the effect reaches the amounts. When tried, an allowance coded `SR` was reported by this rule and by `BR-S-08`, because the allowance dropped out of the Standard rated taxable amount.
- The same wrong code on both the line and the breakdown reported only `BR-CL-17` and `BR-CL-18` when tried. Line and breakdown agreed with each other, so no category rule fired, but the document is still rejected.
- Reported by the EN 16931 layer; the Peppol layer passes the recorded example.

## Related rules

- [BR-CL-18 applies the same code list to the VAT category on each line](https://ironfang.uk/docs/finance/rules/BR-CL-18.md)
- [BR-S-01 is reported with this rule when a Standard rated breakdown is miscoded](https://ironfang.uk/docs/finance/rules/BR-S-01.md)
- [BR-47 requires the breakdown category code that this rule checks](https://ironfang.uk/docs/finance/rules/BR-47.md)
- [BR-S-08 fires when a miscoded allowance or charge drops out of the Standard rated taxable amount](https://ironfang.uk/docs/finance/rules/BR-S-08.md)

## 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-CL-17](https://docs.peppol.eu/poacc/billing/3.0/rules/ubl-tc434/BR-CL-17/) 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.

## Links

- [This rule as a web page](https://ironfang.uk/docs/finance/rules/BR-CL-17)
- [Free Peppol invoice validator](https://ironfang.uk/tools/peppol-validator)
- [Rule index](https://ironfang.uk/docs/finance/rules.md)
- [Ironfang Finance API docs](https://ironfang.uk/docs/finance)
- The same rule is available to MCP clients as the tool `finance.rule.get` on https://mcp.ironfang.uk/mcp
