← Blog How-to 8 min read · 7 September 2026

Your invoice was rejected by ZATCA: how to read the error code

A validation response from the Authority with an error code highlighted next to the invoice field it points at
A rejection names the rule it broke. Read the code before you touch the invoice.

An invoice has come back from the Zakat, Tax and Customs Authority and it did not go through. Somewhere in the response there is a string like BR-KSA-40, and around it a message that is technically accurate and practically useless. This article is about reading that response, because most of the time wasted after a rejection goes on misreading it rather than on the fix.

Where the code actually sits

The Authority answers every submission with a JSON object. The part you want is validationResults, holding two separate arrays:

  • errorMessages — rules the invoice broke. Anything in here means the invoice was not accepted.
  • warningMessages — rules it bent. The invoice was accepted anyway.

Each entry carries a code and a message, and the message usually names the field it is complaining about: BT-31 for the seller VAT number, KSA-13 for the previous invoice hash, and so on. On a rejection some clients wrap the response in an error envelope, so the same object turns up one level down under context. If your integration prints a raw JSON dump with no readable reason, that nesting is why.

Which array a code lands in is not decided per submission. Every rule carries a fixed severity in the Authority's published validation rules: an error rejects the invoice, a warning is returned alongside an acceptance. So a code you have only ever seen as a warning will never suddenly reject you, and one that rejects you will not pass tomorrow. Each code on our error code reference is listed with the severity its rule carries.

Accepted with warnings is accepted

This is the misreading that costs the most. A response can come back with a list of warnings and still be an acceptance: the invoice is registered, it can be handed to the buyer, it belongs in your six-year archive, and its hash carries forward to the next invoice. Nobody needs to reissue it.

Fix them anyway: a warning is nearly always a field your system populates badly, and a stricter rule tends to sit beside the lenient one. Most can wait for your next release. The exception is anything about the hash chain, because those warnings let a broken chain run on unnoticed.

A rejection means the invoice was never registered

The opposite mistake is more expensive. When the Authority rejects an invoice, it does not exist in its records. It was never cleared, never reported, never counted:

  • Do not issue a credit note. A credit note corrects a document that exists. There is nothing to correct.
  • Do not burn the invoice number. Fix the field that failed and resend the same invoice.
  • Do not advance the chain. A rejected invoice contributes no hash. The next invoice still builds on the last one that was genuinely accepted.

And the clock: for a simplified tax invoice, the 24-hour reporting window runs from the moment the invoice was issued — the moment the customer got it — not from the moment the rejection arrived. Two hours spent rejected are two hours of your window. Fix and resend the same day.

Symptom, code, fix

What you seeCodeSeverityWhat to change
Every response carries the same address complaint, including the first invoice, and the address looks complete on the PDFBR-KSA-09, BR-KSA-66WarningAdd the district and building number from the national address; the seller postal code must be 5 digits
Rejected before anything else is checked; your own VAT number is in the messageBR-KSA-40Error15 digits, first and last digit 3, no spaces, Latin digits
Only receipts fail, and only ones where a buyer VAT number was filled inBR-KSA-44ErrorLeave the buyer VAT field empty rather than filling it with anything else
Chain complaints started after a second device or branch began issuingBR-KSA-26, BR-KSA-61WarningOne chain per device: two issuing points must not build on the same previous hash
Only credit notes from the online store fail; manual ones are fineBR-KSA-17ErrorPass the return reason through from the store into the note
Zero-rated or exempt lines fail; standard-rate lines passBR-KSA-18ErrorSend the category letter, not the rate or its name

The severity column is the flag the rule itself carries, and two of these six rows are not rejections at all: those invoices went through, and the response mentioned the field anyway. That does not make them optional — the chain row in particular is the one warning to treat as urgent.

The codes people actually hit

BR-KSA-09 — seller address incomplete. A warning, so the invoices are going through; it simply appears on every response until someone reads one. The rule names six elements: street name, building number, postal code, city, district and country code. The district is the one almost nobody has, because it does not appear on a commercial registration. Take all six from the national address, and write the country as SA. The postal code has its own rule, BR-KSA-66 — five digits — and it is usually raised in the same breath.

BR-KSA-40 and BR-KSA-44 — VAT number shape. Fifteen digits, starting with 3 and ending with 3. BR-KSA-40 is yours, BR-KSA-44 is the buyer's; the message names BT-31 or BT-48 so you can tell which. The usual culprits are the commercial registration number, another government-issued number that is not the VAT registration number, a stray space, or Arabic-Indic digits that look right on screen and fail validation. On a simplified invoice the buyer field is optional — a zero or a dash in it turns an optional field into a rejection. You can confirm a number really belongs to the business with the VAT number lookup.

BR-KSA-26 and BR-KSA-61 — the chain. BR-KSA-61 means the previous invoice hash field is missing; BR-KSA-26 means it is there but not a Base64-encoded SHA-256 digest. Wrong usually means hex instead of Base64: 44 characters ending in =, not 64 characters of hexadecimal. The first invoice does not skip the field — it carries the fixed starting value the specification defines for the first invoice in a chain. Both are warnings, which is the trap: the invoices keep being accepted while the chain quietly diverges, so nobody looks until a second device or branch has been issuing on the same previous hash for weeks. Note also that the chain is not the counter: the invoice counter value is a separate mandatory field with its own rule, BR-KSA-33.

BR-KSA-17 — note issued with no reason. A credit note (type 381) or debit note (383) must carry the reason it was issued, as a data field, not as a printed remark. Quoting the original invoice number does not satisfy it: the reason is this rule, the reference to the original invoice is a separate one, BR-KSA-56, and a note needs both.

BR-KSA-18 — tax category code. The rule lists four accepted values and nothing else: S standard rate, Z zero-rated, E exempt, O out of scope. Not the rate, not a lower-case letter, and not a category code from a European implementation that is absent from those four. Systems ported from a European implementation fail here almost as a rule.

When a code is worth fixing yourself

If you hit one rejection, once, on a system that has otherwise run for a year, read the guide and change the field. That is the whole job, and it costs less than evaluating software. The codes that need more than that are structural — the chain, the certificate, the clearance-versus-reporting split — because they repeat until the design changes. The Phase 2 guide covers that split.

All 135 codes are listed with their official message on our error code reference, and around twenty have a full written guide — the twenty where the official message alone does not tell you what to do. If you would rather read these codes in plain language than in a JSON dump, ZATCA Tools signs and submits your invoices — clearance for standard invoices, 24-hour reporting for simplified ones — gives each branch its own device, certificate and chain, and shows every rejection with its official code and a link to that code's guide. It is free during the launch period: create an account, or read the API reference if you are connecting an existing system.

Frequently asked questions

My invoice came back "accepted with warnings" — do I need to do anything? +
The invoice is accepted and registered with the Authority. You may give it to the buyer, it belongs in your archive, and its hash carries on to the next invoice in the chain. A warning is not a rejection. It is still worth fixing, because a warning almost always points at a field your system is filling wrongly, and a stricter rule usually sits next to it.
Should I issue a credit note to cancel a rejected invoice? +
No. A rejected invoice was never registered with the Authority, so there is nothing to cancel. A credit note corrects a document that exists in the Authority records. Correct the data that failed validation and resend the same invoice, with the same number.
Does a rejected invoice consume an invoice counter or a hash in the chain? +
No. An invoice the Authority rejected was never issued, so its hash does not become the previous invoice hash for the next one. Use the hash of the last invoice that was actually accepted. An invoice accepted with warnings was accepted, and its hash does belong in the chain.
The rejection took two hours to reach me. Does my 24 hours start now? +
No. For a simplified tax invoice the 24-hour reporting window runs from the moment the invoice was issued, not from the moment the Authority answered. Time spent rejected is time spent inside your window, which is why rejections should be fixed the same day rather than queued.
Where do I find the error code in the response? +
Inside validationResults, in one of two arrays: errorMessages or warningMessages. Each entry carries a code and a message. On a rejection, some integrations wrap the whole thing in an error envelope, so the same object appears one level deeper under context.
The message names a field like BT-31 or KSA-13. What are those? +
They are field identifiers from the invoice specification. BT numbers come from the European EN 16931 standard the Saudi format is built on; KSA numbers are fields the Saudi version adds, such as KSA-13 for the previous invoice hash. The identifier tells you exactly which element of the XML to look at.
Can I look up a code you have not written a guide for? +
Yes. All 135 validation codes are listed with their official message on the error code reference. About twenty of them have a full guide, because those are the ones where the message alone does not tell you what to do.
Ready to connect your business?

Connecting is free and takes under five minutes.

Start for free