# BR-48: Add the VAT rate to each VAT breakdown

Every VAT breakdown needs `cbc:Percent` in its tax category, even when the rate is 0. Only category `O`, not subject to VAT, may leave it out.

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

## The short answer

`BR-48` fails for each `cac:TaxSubtotal` whose VAT tax category has no `cbc:Percent`, unless the category is `O`. Add `cbc:Percent` to the breakdown `cac:TaxCategory`, straight after `cbc:ID`, with the rate as a percentage: `20` for 20%, and `0` for zero rated, exempt and the other categories that charge no VAT.

Category `O` is the single exception. A supply outside the scope of VAT has no rate to state, so its breakdown may omit the element.

## What the rule checks

The rule runs on every `cac:TaxSubtotal` inside a `cac:TaxTotal` and reads the `cac:TaxCategory` whose tax scheme is `VAT`. It passes when that category has a `cbc:Percent` element, or when its `cbc:ID` is `O`.

Zero is a rate like any other. An exempt breakdown without `cbc:Percent` was reported by this rule alone when tried; the fix there is `0`, not leaving the element out.

The exception only permits the rate to be absent. A category `O` breakdown carrying a `cbc:Percent` of 0 was also accepted when tried.

An empty `cbc:Percent` is not a number. When tried it failed the XSD layer, so this rule and the other business rules were never evaluated.

Line rates are not read. With the rate removed from a Standard rated line instead, the findings were `BR-S-05` and `BR-S-08` when tried, not this rule.

| Term | Meaning | UBL element |
|---|---|---|
| BT-119 | VAT category rate | `cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent` |
| BT-118 | VAT category code | `cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:ID` |

## How an integration ends up here

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

- Zero rates are treated as no rate and suppressed, so zero rated and exempt breakdowns lose `cbc:Percent`.
- The VAT summary is grouped by category only and the rate is not carried into the breakdown.
- The exception for `O` is applied to every category that charges no VAT, removing the rate from exempt or reverse-charge breakdowns too.
- The rate is held as text such as `Standard`, and the export drops it when it cannot be written as a number.

## How to fix it

1. For each breakdown, take the rate of the lines, allowances and charges it covers. A breakdown groups items by category and rate, so there is exactly one.
2. Write it to `cbc:Percent` inside the breakdown `cac:TaxCategory`, after `cbc:ID` and before any exemption reason, as a percentage number: `20`, not `0.2` or `20%`.
3. For `Z`, `E`, `AE`, `K` and `G`, write `0`. Leave the element out only for `O`.
4. Recalculate the subtotal `cbc:TaxAmount` from the taxable amount and the rate, since `BR-CO-17` checks the two together.

## 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 breakdown gives no rate

```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>
    <cac:TaxScheme>
      <cbc:ID>VAT</cbc:ID>
    </cac:TaxScheme>
  </cac:TaxCategory>
</cac:TaxSubtotal>
```

Fragment of the corrected invoice: the rate of 20 follows the category code

```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 corrected invoice has `<cbc:Percent>20</cbc:Percent>` after the category code in the breakdown; nothing else differs. The failing document also reports `BR-CO-17` and `BR-S-09`, which both check the 5.00 of tax as taxable amount x rate / 100 and cannot reach 5.00 without a rate. `BR-CO-17` handles a missing rate like a zero one and expects tax that rounds to nothing; `BR-S-09` has no answer at all without a rate. Restoring the rate clears all three.

### What the validator reported

- The failing invoice reports **BR-48**, [BR-CO-17](https://ironfang.uk/docs/finance/rules/BR-CO-17.md) and [BR-S-09](https://ironfang.uk/docs/finance/rules/BR-S-09.md). The corrected document passes every layer with no findings.
  - [Download the failing XML](https://ironfang.uk/finance/rule-examples/BR-48-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 the rate removed from its breakdown reported the same three rules when tried.
- Reported by the EN 16931 layer; the Peppol layer passes the recorded example.
- A breakdown whose tax scheme is not `VAT` has no VAT rate as far as this rule can see: when tried, one under `GST` failed here despite its `cbc:Percent`, alongside `BR-47` and `BR-CO-17`.

## Related rules

- [BR-CO-17 checks the breakdown tax amount against the rate and is reported with this rule](https://ironfang.uk/docs/finance/rules/BR-CO-17.md)
- [BR-S-09 is the Standard rated tax calculation that also needs the rate](https://ironfang.uk/docs/finance/rules/BR-S-09.md)
- [BR-47 requires the category code that decides whether the rate may be left out](https://ironfang.uk/docs/finance/rules/BR-47.md)
- [BR-O-05 goes the other way for lines: a line in category O must not carry a rate](https://ironfang.uk/docs/finance/rules/BR-O-05.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-48](https://docs.peppol.eu/poacc/billing/3.0/rules/ubl-tc434/BR-48/) 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-48)
- [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
