If the invoice contains the previous invoice hash (KSA-13), this hash must be base64 encoded SHA256. The hash shall be computed using the following method as described in the ds:transforms block in the XML Invoice Specifications: 1. Remove the <Invoice><ext:UBLExtensions/> block 2. Remove the <Invoice><cac:AdditionalDocumentReference/> block where <cbc:ID/> = QR 3. Remove the <Invoice><cac:Signature/> block 4. Canonicalize the Invoice using the C14N11 standard 5. Hash the resulting string using SHA256 to a binary object 6. Base64 encode the binary object to generate the digest value For the first invoice, the previous invoice hash is "NWZlY2ViNjZmZmM4NmYzOGQ5NTI3ODZjNmQ2OTZjNzljMmRiYzIzOWRkNGU5MWI0NjcyOWQ3M2EyN2ZiNTdlOQ==", the equivalent for base64 encoded SHA256 of "0" (zero) character.
What does this mean?
Every Phase 2 invoice carries the hash of the invoice before it in field KSA-13, so the invoices form a cryptographically linked chain: no invoice can be removed from the middle without breaking the link. This code says the field is there, but its value is not in the required format.
The required format: a 32-byte SHA-256 digest, Base64 encoded — a 44-character string ending in =. Anything else is rejected: a hex digest (64 characters), the raw binary value, or a truncated string.
Why does it happen?
Ordered from the most common to the least — your cause is most likely the first or the second.
- Emitting the digest as hex instead of Base64 — by far the most common cause. Hex gives 64 characters drawn from 0-9 and a-f, where the rule wants 44 characters of Base64.
- Hashing the XML file after signing, or before removing the three blocks the specification requires you to remove.
- Hashing XML that has not been canonicalised with C14N11 — a single space of difference changes the whole hash.
-
Sending
0or an empty value on the first invoice instead of the Base64 hash of the character 0. - Taking the hash from the wrong field of the Authority response to the previous invoice.
How to fix it
3 steps, then send the invoice again.
-
1
Compute the hash in the six steps the specification sets out
Starting from the XML of the previous invoice: (1) remove the
<ext:UBLExtensions>block. (2) Remove the<cac:AdditionalDocumentReference>block whose<cbc:ID>isQR. (3) Remove the<cac:Signature>block. (4) Canonicalise the result with C14N11. (5) Compute SHA-256 over the resulting text as a binary object. (6) Base64 encode that binary object. -
2
On the first invoice, use the fixed value
The first invoice has nothing before it, so it takes the fixed value the specification sets: the Base64 of the SHA-256 digest of the character 0:
NWZlY2ViNjZmZmM4NmYzOGQ5NTI3ODZjNmQ2OTZjNzljMmRiYzIzOWRkNGU5MWI0NjcyOWQ3M2EyN2ZiNTdlOQ== -
3
Store the hash of every invoice you send, and never recompute it
Compute the hash once, at the moment you send, and save it in your database; then use the saved value on the next invoice. Recomputing it later from a stored file that differs slightly in formatting is what breaks the chain after months of correct operation.
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. ZATCA Tools computes the hash, stores it, and links every invoice to the one before it automatically — there is no PIH field for you to fill in, and no step to forget.
Related codes
Frequently asked questions
Does a rejected invoice belong in the chain? +
What happens if I issue from two devices at the same time? +
Can I verify the hash myself? +
ZATCA Tools builds the signature, the PIH, the counter and the encoding for you, and validates your data before it is sent. Completely free for now, and no credit card.
Start for free