Mutabiq checks Saudi ZATCA Phase 2 e-invoice XML before you submit it: pre-validation against published ZATCA requirements, not an approval by the authority. This page explains one rule, BR-KSA-61.
Five checks a month on the Free plan. The file is checked in memory and is never stored.
The message ZATCA prints
[BR-KSA-61]-Previous invoice hash (KSA-13) must exist in an invoice.
Also cited in this report
BR-KSA-26
[BR-KSA-26]-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 Mutabiq reports
One entry for each way this rule can be reported.
Reported as
What kind of value was found — empty, not_15_digits and the like — never the value itself.
Invoice carries no previous invoice hash
Reported asreference_missingthe reference is missing
What is wrong
The document carries no cac:AdditionalDocumentReference whose cbc:ID is PIH, so it states no previous invoice hash (KSA-13). SDK 3.4.8 flags the document under BR-KSA-61 as a warning, not a rejection, whose message reads: previous invoice hash (KSA-13) must exist in an invoice; the finding is reported without failing the document by itself. The hash is what links this document to the one issued before it on the same device or system: each invoice carries the digest of its predecessor, so the documents form a chain and a missing or altered one can be detected. Without the reference there is no link. The id is compared after whitespace is normalised but exactly otherwise, so a reference labelled pih in lower case is not the one this rule looks for and reads as absent. This applies to every invoice, standard and simplified alike.
What to check in your ERP
The hash travels in a document reference block of its own: a cac:AdditionalDocumentReference whose cbc:ID is the literal text PIH, holding cac:Attachment/cbc:EmbeddedDocumentBinaryObject with the base64 value inside it. The usual causes are an e-invoicing module configured for Phase 1, which has no chain to maintain and so never writes the block; an export template that writes the ICV and QR reference blocks and was never extended with the PIH one; and a chain that is maintained internally but not exported. Note that the first invoice in a chain is not an exception - it still carries the block, with the value ZATCA's XML Implementation Standard publishes for that case, so an empty chain is not a reason to omit the reference.
Previous invoice hash reference carries no payload element
Reported aspayload_element_missingthe payload element is missing
What is wrong
The document has a cac:AdditionalDocumentReference whose cbc:ID is PIH, but there is no cac:Attachment/cbc:EmbeddedDocumentBinaryObject inside it: the reference block is present and the element that would hold the hash is not. ZATCA flags the document under BR-KSA-61 as a warning, which does not by itself reject the document. Every part of this test is evaluated on the PIH reference itself, so this is a statement about your previous invoice hash and not about some other attachment in the document. You will probably see a second finding, under BR-KSA-26, for this same element; that is expected and is explained there.
What to check in your ERP
Check the shape of the block your export writes: two levels inside the reference, a cac:Attachment and a cbc:EmbeddedDocumentBinaryObject inside that, with the base64 value as the second one's text content. The usual causes are a template that writes the reference and its cbc:ID from one loop and the attachment from another that did not run, a hash written into the reference's own text or into a description element rather than into the attachment, and a nesting level dropped so that the binary object sits directly under the reference. A payload element that is present but empty is a different finding.
Previous invoice hash is empty
Reported aspayload_emptythe payload is empty
What is wrong
The PIH reference carries its cac:Attachment/cbc:EmbeddedDocumentBinaryObject, but the element has no value in it, so the document states no previous invoice hash. BR-KSA-61 requires the element to be present and non-empty, and ZATCA flags the document under it as a warning, which does not by itself reject the document. An empty element is treated exactly as a missing one here. You will probably see a second finding, under BR-KSA-26, for this same element; that is expected and is explained there.
What to check in your ERP
The element is being written and the value behind it is not, so the mapping produces the PIH block unconditionally while the step that computes or retrieves the hash is not running or is returning nothing. Check that the previous document's digest is available at the point the XML is built, and that it is stored between documents rather than recomputed from a record that has already moved on. On the first invoice of a chain the value is not blank either - ZATCA's XML Implementation Standard publishes the value to use in that case, and an empty element is not it.
Previous invoice hash is not declared text/plain
Reported asnot_text_plainnot declared text/plain
What is wrong
The PIH payload element does not carry a mimeCode attribute of text/plain. ZATCA requires that declaration; SDK 3.4.8 flags the document under BR-KSA-61 as a warning, not a rejection, when it is missing: the finding is reported without failing the document by itself. The attribute is compared after whitespace is normalised and exactly otherwise, so text/plain is what is required and application/octet-stream, base64Binary, an empty attribute and no mimeCode attribute at all each fail. Every part of this test is evaluated on the PIH reference itself. You will probably see a second finding, under BR-KSA-26, for this same element; that is expected and is explained there.
What to check in your ERP
The attribute belongs on the element that holds the value, not on the cac:Attachment above it: mimeCode with the value text/plain on cbc:EmbeddedDocumentBinaryObject. Do not reason the value out from what the payload contains - the payload is base64, so application/octet-stream looks the more accurate of the two and draws this note; text/plain is what the checker requires. The usual causes are a serialiser that writes the attachment through a generic binary-attachment helper carrying its own default type, an attribute read from a document-management table, and a template that omits it. The QR attachment in the same document needs the same declaration, so a system that gets one right and the other wrong is normally writing the two blocks from two different pieces of code.
The clause that runs
Quoted from the validation-rule stylesheet ZATCA publishes, at the file and line named below. The executed guard is what the check runs; the reported test is what it prints when it fails, and the two are not always the same.
Executed guard
not((cac:AdditionalDocumentReference[normalize-space(cbc:ID) = 'PIH'] and exists(cac:AdditionalDocumentReference[normalize-space(cbc:ID) = 'PIH']/cac:Attachment/cbc:EmbeddedDocumentBinaryObject) and (cac:AdditionalDocumentReference[normalize-space(cbc:ID) = 'PIH']/cac:Attachment/cbc:EmbeddedDocumentBinaryObject) != '' and (cac:AdditionalDocumentReference[normalize-space(cbc:ID) = 'PIH']/cac:Attachment/cbc:EmbeddedDocumentBinaryObject[normalize-space(@mimeCode) = 'text/plain'])))
Reported test
(cac:AdditionalDocumentReference[normalize-space(cbc:ID) = 'PIH'] and exists(cac:AdditionalDocumentReference/cac:Attachment/cbc:EmbeddedDocumentBinaryObject) and (cac:AdditionalDocumentReference/cac:Attachment/cbc:EmbeddedDocumentBinaryObject) != '' and (cac:AdditionalDocumentReference/cac:Attachment/cbc:EmbeddedDocumentBinaryObject[normalize-space(@mimeCode) = 'text/plain']))
As shipped in ZATCA SDK 3.4.8: the copy quoted here is, byte for byte, the file that release ships. The date in the file name is the name ZATCA gives the file; the rules in it are those of SDK 3.4.8.
A worked example
A synthetic document from the measured test corpus that trips this rule, shown where it differs from one that passed. Mutabiq's report names the class of the value, never the value itself.