The short answer
BR-CO-23 fails when a cac:AllowanceCharge inside cac:InvoiceLine (or cac:CreditNoteLine) has cbc:ChargeIndicator of false and carries neither cbc:AllowanceChargeReasonCode nor cbc:AllowanceChargeReason. Give the line discount its reason: a UNTDID 5189 code, a description, or both.
The same allowance is also reported as BR-42, a second EN 16931 rule with an identical test. Adding the reason once satisfies both.
What the rule checks
The rule looks at each cac:AllowanceCharge that is a direct child of a line and reads as an allowance. The allowance is accepted as soon as either reason element exists in it.
Every allowance is judged on its own, so a line with two allowances needs a reason on each, and each line in the document is visited.
The price discount inside cac:Price is a different element and is outside this rule. The recorded price-discount example has no reason on its price-level allowance and validates cleanly.
Line charges are outside it too. The rich invoice has a line charge with reason text and no code, which is enough for its own rule, BR-CO-24.
An element that exists satisfies the rule even with no content. When tried, an empty cbc:AllowanceChargeReason on the line allowance passed BR-CO-23 and was reported by PEPPOL-EN16931-R008 instead.
| Term | Meaning | UBL element |
|---|---|---|
| BG-27 | Invoice line allowances | cac:InvoiceLine/cac:AllowanceCharge[cbc:ChargeIndicator = false] (cac:CreditNoteLine/cac:AllowanceCharge in a credit note) |
| BT-139 | Invoice line allowance reason | cac:InvoiceLine/cac:AllowanceCharge/cbc:AllowanceChargeReason |
| BT-140 | Invoice line allowance reason code | cac:InvoiceLine/cac:AllowanceCharge/cbc:AllowanceChargeReasonCode |
How an integration ends up here
Possible causes, from the shape of the rule rather than from measured usage:
- A customer or volume discount is applied per line by a pricing engine that records the percentage and amount but not the discount type.
- Line allowances are generated from the difference between list price and selling price, which leaves no reason to attach.
- The reason is mapped for document-level allowances, but the line-level mapping was written separately and omits it.
- The reason text is trimmed or sanitised to nothing, and the element is then left out.
How to fix it
- Find the line and the allowance in the finding location, for example
cac:InvoiceLine[1]/cac:AllowanceCharge[1]. The second index counts everycac:AllowanceChargeon that line, charges included. - Carry the discount type through from the pricing record that produced the allowance.
- Where the type matches a UNTDID 5189 entry, write that code in
cbc:AllowanceChargeReasonCode(95for a plain discount). Addcbc:AllowanceChargeReasonwith the description, or send the text alone when no code fits. - Keep the elements in UBL order, straight after
cbc:ChargeIndicatorand beforecbc:MultiplierFactorNumericandcbc:Amount. No amount on the line needs recalculating.
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 5 per cent allowance on line 1 has no reason, the line charge beside it has one
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<!-- note, quantity, line net amount, accounting cost and order line reference omitted from this fragment -->
<cac:AllowanceCharge>
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cbc:MultiplierFactorNumeric>5</cbc:MultiplierFactorNumeric>
<cbc:Amount currencyID="GBP">2.00</cbc:Amount>
<cbc:BaseAmount currencyID="GBP">40.00</cbc:BaseAmount>
</cac:AllowanceCharge>
<cac:AllowanceCharge>
<cbc:ChargeIndicator>true</cbc:ChargeIndicator>
<cbc:AllowanceChargeReason>Example charge</cbc:AllowanceChargeReason>
<cbc:Amount currencyID="GBP">0.01</cbc:Amount>
</cac:AllowanceCharge>
<!-- item and price omitted from this fragment -->
</cac:InvoiceLine>Fragment of the corrected invoice: the line allowance carries reason code 95 and a reason text
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<!-- note, quantity, line net amount, accounting cost and order line reference omitted from this fragment -->
<cac:AllowanceCharge>
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cbc:AllowanceChargeReasonCode>95</cbc:AllowanceChargeReasonCode>
<cbc:AllowanceChargeReason>Example discount</cbc:AllowanceChargeReason>
<cbc:MultiplierFactorNumeric>5</cbc:MultiplierFactorNumeric>
<cbc:Amount currencyID="GBP">2.00</cbc:Amount>
<cbc:BaseAmount currencyID="GBP">40.00</cbc:BaseAmount>
</cac:AllowanceCharge>
<!-- line charge, item and price omitted from this fragment -->
</cac:InvoiceLine>On line 1 the corrected invoice has reason code 95 and the text Example discount on the 5 per cent allowance; the failing invoice has neither, and nothing else changes. The failing document also reports BR-42, which applies the same test as BR-CO-23 to the same allowance, so the one edit clears both. The line charge next to it already had reason text and was not reported.
What the validator reported
- The failing invoice reports
BR-42and BR-CO-23. 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
cac:InvoiceLinein anInvoiceandcac:CreditNoteLinein aCreditNote. The credit note version of the recorded example reportedBR-42andBR-CO-23atcac:CreditNoteLine[1]/cac:AllowanceCharge[1]when tried. - Only the EN 16931 layer reports a missing reason. When a code is present, the Peppol layer checks it against the allowance list as
PEPPOL-EN16931-CL002, and the EN 16931 layer asBR-CL-19. - A line allowance amount is part of the line net amount calculation, but adding a reason changes no figure.
Related rules
- BR-CO-21 is the same requirement for allowances on the whole document
- BR-CL-19 checks a line allowance reason code against the UNTDID 5189 list
- PEPPOL-EN16931-R120 subtracts the line allowance when it checks the line net amount
- PEPPOL-EN16931-R040 checks the line allowance amount against its base amount and percentage
- 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-CO-23 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.
