A VAT breakdown (BG-23) with VAT Category code (BT-118) 'Exempt from VAT' or 'Zero rated' or 'Not subject to VAT' shall have a VAT exemption (or exception) reason text (BT-120) with one of the values from paragraph 11.2.4 of XML implementation standards on Tax exemption (or exception) reason text - specific to Saudi Arabia.
What does this mean?
The exemption reason in the VAT breakdown is two fields side by side: the code (BT-121) in cbc:TaxExemptionReasonCode, for machines, and the text (BT-120) in cbc:TaxExemptionReason, for whoever reads the document. Both sit inside cac:TaxSubtotal/cac:TaxCategory for category Z, E or O. This rule asks for the text whenever the code is there.
The executable test in the Authority's SDK (R3.4.8) is narrower than its message: if the code element exists, the text element must exist and something must be left in it once spaces are removed. That is all it checks. The "one of the values from paragraph 11.2.4" the message mentions did have a stricter test, comparing the text with the Authority's table; it is in the same file, disabled inside a comment. Write the table's wording anyway: the message asks for it, the disabled test may come back, and we cannot see what the Authority's live server runs.
When it stays silent: a breakdown with no code at all does not reach it — absence has its own codes, BR-KSA-69, BR-KSA-23 and BR-KSA-24. A text without a code does not trigger it either, because the test starts from the code. And a standard-rated S breakdown is outside it altogether.
It is a warning: the invoice is accepted and recorded, with the warning attached. Category O has a place of its own in the table: it sets no text for it, saying instead that the text is free, written by the taxpayer case by case, while the code stays VATEX-SA-OOS.
Why does it happen?
Ordered from the most common to the least — your cause is most likely the first or the second.
- A mapping that writes the code alone because the source system stores nothing else, leaving the text for someone to "add later".
-
A template that always writes the text element and fills it from a lookup that missed — because the code was stored in a different spelling from the key — so
<cbc:TaxExemptionReason/>goes out empty or full of spaces. -
The text written somewhere else: in the document note
cbc:Note, on the line, or printed on the PDF only, while the rule reads nothing but the category block in the breakdown. - A document mixing two categories — zero-rated and exempt — where the text is fetched once for the document and written on the first breakdown only, leaving the second with a code and no text.
How to fix it
3 steps, then send the invoice again.
-
1
Write the code and the text together, from one table
One function that takes the code and returns the pair, and no path in the code that writes one without the other:
<cbc:TaxExemptionReasonCode>VATEX-SA-29</cbc:TaxExemptionReasonCode><cbc:TaxExemptionReason>Financial services mentioned in Article 29 of the VAT Regulations</cbc:TaxExemptionReason>. The two elements follow each other insidecac:TaxCategory, aftercbc:Percentand beforecac:TaxScheme. The value of the code has its own rule: BR-KSA-CL-04. -
2
Take the wording from the Authority's table
The table is paragraph 11.2.4 of the XML Implementation Standard on zatca.gov.sa, with a column for English and one for Arabic. Its English wording in version 1.2 includes:
VATEX-SA-32,Export of goods;VATEX-SA-33,Export of services;VATEX-SA-35,Medicines and medical equipment;VATEX-SA-36,Qualifying metals;VATEX-SA-EDU,Private education to citizen;VATEX-SA-HEA,Private healthcare to citizen; andVATEX-SA-30,Real estate transactions mentioned in Article 30 of the VAT Regulations. Copy it as it stands, and do not shorten it. -
3
Never write an empty element
If your table has no text for the code, stop the document before signing and fix the table, rather than writing the element empty: a document with an empty reason is signed and reported with the gap in it, while a stopped one is fixed in a minute and goes out clean. How to read the response when several codes arrive together is in ZATCA error codes explained.
This code does not block acceptance, so the invoice you have is compliant. Correct the data so the warnings disappear from future invoices.
Does ZATCA Tools prevent it?
Yes. In ZATCA Tools a code is never written without its text: both come from one table in TaxCategories that ties every code to an English text, the VAT breakdown takes them together in the same step in TaxBreakdown, and no code in the table is without a text — so a code never goes out alone and a text element is never empty. Category O always carries VATEX-SA-OOS and its text. When the reason for Z or E is left out, there is neither code nor text, which brings another warning, BR-KSA-69 or BR-KSA-23, not this one. The texts match table 11.2.4 of the XML implementation standard (version 1.2) word for word.
Related codes
BR-KSA-CL-04
Warning
BR-KSA-69
Warning
BR-KSA-23
Warning
BR-KSA-24
Warning
BR-KSA-18
Rejects the invoice
Frequently asked questions
Must the text match the Authority's table word for word? +
Do I write the text in Arabic or in English? +
Medicines and medical equipment | الأدوية والمعدات الطبية.What text does an out-of-scope supply need? +
VATEX-SA-OOS is fixed, but the table leaves the text free: the taxpayer writes it case by case. Write a short sentence saying why the supply is outside the scope, and do not leave it empty.I wrote the reason in the invoice note. Is that not enough? +
cbc:TaxExemptionReason inside the category block of the VAT breakdown. The note, the line and the printed file are all outside what it reads.How does it relate to BR-KSA-CL-04? +
ZATCA Tools builds the signature, the PIH, the counter and the encoding for you, and validates your data before it is sent. Start free, with no credit card.
Start for free