When your business moves to the second phase of e-invoicing, every invoice becomes a signed, sequenced digital document, linked to the invoices before it by a single hash value. This value, or digital fingerprint, is the point where integration systems stumble most, because any one-byte difference produces a completely different fingerprint that the Fatoora platform rejects. This technical documentation catalogs the hash errors you face when connecting your system to the Zakat, Tax and Customs Authority (ZATCA), and explains for each error its symptom, root cause, and fix.
The content is aimed at developers and technical integration engineers who build or integrate invoicing systems with the Fatoora platform. If you are looking for the full picture of e-invoicing requirements, start from the Qoyod e-invoicing guide, then come back here to diagnose the errors. To understand the three concepts all these errors revolve around, review the documentation for Hashing, and the documentation for the SHA-256 algorithm, and the documentation for Previous Invoice Hash (PIH). This guide is part of a broader series covering Zakat, Tax and Customs Authority errors in e-invoicing.
Most fingerprint-related rejection messages share one symptom: the platform rejects the invoice with an error code pointing to a fingerprint mismatch or a broken chain, while the invoice looks fine to you. The cause is almost always that the fingerprint was computed on data different from what the platform expects, or computed with the wrong encoding, or linked to the wrong link in the chain. Let’s start by understanding the logic of rejection messages, then go through the most common of these errors one by one.
How to read a fingerprint-related rejection message
Before diagnosing any error, you need to read the platform’s response correctly. The Fatoora platform rejects the invoice with a structured response carrying a status field, a list of error messages, and for each error a code, a category, and explanatory text. Fingerprint-related errors usually fall in the hashing category or the sequencing category, and carry codes pointing to the fingerprint, the PIH field, or the invoice counter.
The first step in any diagnosis is to identify the type of error: is the problem in the fingerprint value itself, in its encoding, or in its position within the chain? A value error means you hashed the wrong data. An encoding error means the value is correct but its format is unexpected. A chain error means the fingerprint is correct but points to the wrong invoice. This distinction alone cuts diagnosis time in half, because it directs you straight to the layer that holds the fault.
The second step is to keep a record for each invoice containing three elements: the XML copy after canonicalization, the fingerprint computed on it, and the PIH value sent. On any rejection, you compare these elements against what the platform expects, and quickly find the point of deviation. Without this record, every diagnosis turns into guesswork. Mature integration systems build this record automatically, and link each invoice to the counter number and approval status, making it possible to trace the whole chain at any moment.
The third step is to separate single-invoice errors from chain errors. An invoice may be rejected for an error specific to it alone, such as a wrong fingerprint encoding, so you fix it and resend it without affecting what follows. But a chain error, such as the counter advancing or the linkage breaking, affects every subsequent invoice until the breaking point is fixed. Confusing the two types leads to superficial fixes that leave the root cause standing.
Now that we know how to read a rejection and classify it, let’s move to the errors themselves. We ordered them from the most common in new integration systems to the most subtle, and each error carries its symptom, root cause, and fix steps.
PIH mismatch and a broken hash chain
Every invoice in the second phase carries the previous invoice’s fingerprint in its PIH field. Invoices are thereby linked in a chain, each link depending on the one before it. This linkage is what makes tampering with any old invoice instantly detectable, because it breaks the chain from the point of modification through to the last invoice.
The usual symptom of this error is that the platform accepts the first invoice then rejects the next with a message indicating that the PIH value does not match the fingerprint of the previously approved invoice. The message often appears in this form in the platform’s response:
Invoice 1 + its hash
Invoice 2: PIH = hash 1
Invoice 3: wrong PIH = broken chain
The visible hash chain
The root cause in most cases is that your system read the PIH value from a stale or wrong source. The system may have resent a previously rejected invoice, so its chain counter stayed ahead of the platform’s, or the PIH was computed from the invoice copy before applying canonicalization, or the fingerprint of an invoice other than the actually approved last one was placed.
To fix the error, first verify that the PIH value in the current invoice equals the fingerprint of the last invoice the platform successfully approved, not the last invoice your system tried to send. Rebuild the PIH value from the record of approved invoices only, and remove from the counter any invoice that was rejected. Also verify that the fingerprint you store for each invoice is computed on the XML copy after canonicalization, not before. With Qoyod, this sequencing is managed automatically, so the counter only advances after the platform approves the invoice.
This problem shows up clearly in test environments where invoices are resent repeatedly. Every failed attempt may leave a trace in your system’s state, so it thinks the chain advanced while the platform approved nothing. The systematic solution is that your system should not base the chain’s state on the intent to send, but on the actual approval response from the platform. Treat every invoice as pending until the success response arrives, and only then fix its fingerprint as the PIH for the next invoice.
Also be mindful of multiple issuing devices. Each issuing device has its own independent chain and its own counter. If your system mixes two devices’ chains, it will place in PIH a fingerprint from a different chain, so the platform rejects it. Keep each issuing device’s record completely separate, and never share a PIH value between two chains. Qoyod separates issuing-device chains automatically, so fingerprints never cross between them no matter how many points of sale or branches there are.
Base fingerprint error in the first invoice
The first invoice in the chain has no invoice before it to take a PIH value from. The Zakat, Tax and Customs Authority defines a fixed base value for this case, which is the fingerprint of an empty chain encoded in Base64. Many systems write this value incorrectly, so the platform rejects the very first invoice.
The symptom is that the platform rejects the first invoice immediately, even though the chain has not started yet. The root cause is either placing an empty PIH value, or placing zeros, or placing the base value’s text in hexadecimal format instead of Base64, or truncating part of the value. The correct base value is the fingerprint of the empty string with the SHA-256 algorithm after Base64 encoding, and equals:
To fix the error, place the base value approved by the Authority in the PIH field of the first invoice only, and make sure it is Base64-encoded, not hexadecimal. Also verify there are no extra spaces or lines in the value. After the first invoice is approved, the second invoice takes its actual fingerprint, and the chain continues normally. In Qoyod this value is injected automatically at the first invoice of each issuing device, so you never handle it manually.
You should distinguish between the first invoice in the lifetime of the business and the first invoice in the lifetime of a new issuing device. Each issuing device begins its own chain with the same base value, even if the business has previously issued thousands of invoices on another device. The common mistake is for the system to assume a prior chain exists for a newly added device, so it looks for a previous invoice’s fingerprint that does not exist. Treat the first invoice of each new device as the start of an independent chain with the base value.
Also note that the base value is not a number you invent or generate, but a fixed value specified in the second-phase specifications. Copying it manually from an untrusted source introduces hidden errors, such as replacing a similar-looking character or dropping the encoding marker at its end. Take it from the official specifications or from a trusted library, and test that its length and format match what the platform expects before building your first real invoice.
Computing SHA-256 with the wrong encoding: hex vs Base64
The Authority adopts the SHA-256 algorithm, which produces a fixed-length 256-bit fingerprint for any input. But a single fingerprint is written in more than one encoding, and confusing them is the most common cause of invoice rejection among new developers. The same fingerprint may appear in hexadecimal (64 characters) or in Base64 (about 44 characters), and the platform expects a specific format in each field.
| Standard | hex | Base64 |
|---|---|---|
| Length | 64 characters | ≈44 characters |
| Expected | Per field | Per field |
| Common error | Sending hex instead of Base64 | — |
The two encodings for the same fingerprint
The symptom is that the platform rejects the invoice with a message indicating that the fingerprint is invalid or of an unexpected length, even though its value is mathematically correct. The root cause is sending the fingerprint in hexadecimal encoding in a field expecting Base64, or vice versa. This often happens because the hashing library in the programming language outputs the fingerprint as hex by default, while the PIH field requires Base64 encoding.
To fix the error, determine the encoding each field expects, then convert your fingerprint to it before sending. The practical rule is that the PIH field and the fingerprint field in the QR code use Base64. Don’t rely on matching the text length alone, as the length may be correct while the encoding is wrong. Test your fingerprint by comparing it against a known reference fingerprint for the same input before building the full invoice. Qoyod handles these conversions automatically, outputting each field in the encoding the platform expects.
A common source of this error is the default behavior of hashing libraries. Many languages output the fingerprint as a ready hexadecimal string, so the developer takes it as is and places it in a field expecting Base64. The difference is that hex encoding represents each byte with two characters, while Base64 represents every three bytes with four characters. That is why the same fingerprint appears as 64 hex characters or about 44 characters in Base64. Take the raw bytes of the fingerprint, not its text representation, then encode them in the required format.
A subtler encoding error happens when the hexadecimal text representation itself is Base64-encoded instead of encoding the raw bytes. The result is a value much longer than expected, because it encoded 64 text characters instead of 32 raw bytes. If you see a fingerprint longer than 44 characters in a Base64 field, you likely fell into this error. Verify that the input to Base64 encoding is the binary output of the hash function directly, not its hexadecimal representation.
Canonicalization error before computing the fingerprint
An XML file can be written in multiple formal ways that convey the same meaning but differ in bytes: different leading spaces, a different attribute order, empty lines, a difference in character encoding. Any of these differences changes the resulting fingerprint entirely, because the hash function operates on bytes, not on meaning. Canonicalization is the unification of the XML format before hashing it so that the issuer and the platform obtain the same fingerprint.
The symptom is that the platform rejects the invoice with a fingerprint mismatch message, while its data looks to you exactly identical to what it approved. The root cause is that you computed the fingerprint on XML before applying the approved canonicalization algorithm, or applied a canonicalization different from the one the platform uses. Then the file’s bytes differ on your side from the platform’s, so the two fingerprints differ despite identical visible content.
To fix the error, apply the approved canonicalization algorithm to the XML first, then compute the fingerprint on the canonicalized output, not on the original file. Make sure your system uses the same canonicalization algorithm the Authority specifies, and that Arabic characters are encoded in unified UTF-8 before hashing. Test by comparing your fingerprint against the fingerprint of the same file after passing it through a reference canonicalization tool. In Qoyod the approved canonicalization is applied to every invoice before hashing automatically, so you never face this difference.
One of the most hidden sources of this error is XML libraries that reformat the file when reading or saving it. A library may add pretty leading spaces, sort attributes alphabetically, or change the encoding of special characters. All of that changes the bytes after you computed your fingerprint, so your fingerprint diverges from the platform’s. The rule is to hash the bytes produced directly by the canonicalization step, without passing them through any library that reformats them afterward.
Arabic characters deserve special attention. The seller’s name, address, and line-item descriptions may contain Arabic letters, and any difference in Unicode normalization or in the encoding of these letters changes the file’s bytes. Unify normalization to the form the platform adopts before canonicalization, and make sure the canonicalization output is in UTF-8 encoding without a byte-order mark at its start. The invoice may look visually fine while its bytes differ because of a single Arabic character normalized differently.
Computing the fingerprint on the wrong XML copy
The order of operations in building the invoice is precise: XML is generated, then canonicalized, then its fingerprint is computed, then it is signed, then it is sent. The common error here is computing the fingerprint on the XML copy at the wrong stage of this sequence: before canonicalization, or after adding the signature, instead of computing it on the canonicalized copy before signing.
The symptom is that the platform rejects the invoice with a fingerprint mismatch even though every other field is sound. The root cause is that your system computed the fingerprint on XML after injecting the signature, so the fingerprint came to include the signature data itself, while the platform expects the fingerprint of the content before signing. Or, conversely, computed it on raw XML before canonicalization so the bytes differed.
To fix the error, compute the fingerprint on the XML copy after canonicalization and before adding the signature element. This copy is what the platform hashes when verifying. Review your system’s step sequence and make sure the hashing step immediately precedes the signing step, and that the input to it is the canonicalized output. Record the fingerprint computed at this exact point to match it later during diagnosis. Qoyod applies this order precisely, always hashing the canonicalized copy before signing.
Let Qoyod handle hashing and chaining on your behalf
Qoyod generates the XML, canonicalizes it, computes the fingerprint, links the chain, signs the invoice, and sends it to the Fatoora platform automatically, so you never handle any hash error manually.
Start your free trial and issue compliant invoices with no hash errors
ICV out of sync with PIH
Alongside PIH, every invoice carries a sequential invoice counter called ICV, which increases by one with each invoice from the same issuing device. This counter must move in sync with the hash chain: the invoice carrying ICV number N must carry in its PIH the fingerprint of invoice number N minus one. When the two counters separate, the relationship between them breaks.
| Standard | In sync (sound) | Out of sync (error) |
|---|---|---|
| ICV | Correct sequence 1, 2, 3 | Jump or duplicate |
| PIH | Correct previous hash | Stale/wrong hash |
| Result | Accepted | Rejected |
Syncing the counter with the chain
The symptom is that the platform rejects an invoice with a message indicating an unexpected counter or an inconsistent chain, despite the fingerprint being correct in itself. The root cause is that your system increased the ICV counter for invoices that were not approved, or ran more than one parallel issuing thread on the same device so the two counters raced, or retrieved a PIH from an invoice whose number does not match the current counter.
To fix the error, make increasing ICV and updating PIH a single indivisible operation that only completes after the platform approves the invoice. Prevent parallel issuing on a single issuing device, or dedicate an independent issuing device with a separate counter to each thread. Periodically verify that the current ICV value equals the number of approved invoices, and that PIH matches the fingerprint of the invoice immediately preceding it. Qoyod manages the counter and the chain as a single unit per issuing device, so they never separate.
This error stands out in high-load systems that issue many invoices per second. When two threads try to increase the counter at the same instant, they may read the same value then write it, so an invoice is lost from the sequence or a number is duplicated. The solution is to lock the counter exclusively during the increase, or use an atomic counter that guarantees each read-and-increase is a single non-overlapping operation. Don’t leave the sequence to chance in a multi-threaded environment.
Also be mindful of retrying after a connection drop. If your system sends an invoice then the connection drops before the response arrives, the platform may have actually approved it or rejected it. Blind resending risks advancing the counter twice or duplicating a number. Handle this case by querying the invoice’s status before resending it, and fix the counter on what the platform actually approved. Qoyod handles connection drops and retries safely, checking each invoice’s status before any resend.
How Qoyod helps you
Every error in this guide stems from a single technical detail in the cryptographic layer: the order of operations, the encoding, the synchronization of counters, or the integrity of the chain. Handling them manually requires a deep understanding of the second-phase specifications and consumes your technical team’s time. Qoyod eliminates this layer for you entirely.
Qoyod generates the XML file in its approved format, applies the correct canonicalization, computes the SHA-256 fingerprint in the encoding the platform expects, links the PIH field to the fingerprint of the last approved invoice, syncs the ICV counter with the chain, signs the invoice, and sends it to the Fatoora platform for instant clearance of business-to-business invoices or reporting within 24 hours for consumer invoices. Qoyod also manages the approved Compliance Cryptographic Stamp Identifier (CSID) linked to your account automatically.
Your team needs to write no hash code, and no diagnosing of fingerprint-related rejection messages. You issue your invoice from a simple interface, and Qoyod handles everything beneath it. With 24/7 support all week long, the Qoyod team stays available to help you with any integration question.
Frequently asked questions
What does a PIH mismatch mean when sending the invoice?
It means the PIH value your system placed does not equal the fingerprint of the last invoice the platform successfully approved. Often because your system resent a rejected invoice so its counter advanced, or computed the fingerprint from the wrong XML copy. The fix is to rebuild the PIH from the record of approved invoices only.
What is the correct base value for the PIH field in the first invoice?
It is the fingerprint of the empty string with the SHA-256 algorithm after Base64 encoding, defined by the Zakat, Tax and Customs Authority. The common error is writing it in hexadecimal format or placing zeros. This value applies to the first invoice only, then the second invoice takes the actual fingerprint of the first.
Why does the platform reject my fingerprint even though it is mathematically correct?
Most likely because you sent it in the wrong encoding. A single fingerprint is written in hexadecimal format or in Base64 format, and the platform expects a specific encoding for each field. The PIH field and the QR code fingerprint use Base64. Convert your fingerprint to the required encoding before sending.
Why must canonicalization be applied before computing the fingerprint?
Because XML can be written in multiple formal ways that convey the same meaning but differ in bytes, and the hash function operates on bytes. Canonicalization unifies the format before hashing so that your system and the platform obtain the same fingerprint. Computing the fingerprint before canonicalization produces a fingerprint that does not match what the platform expects.
On which XML copy do I compute the fingerprint?
On the copy after canonicalization and before adding the signature element. Computing the fingerprint after signing makes it include the signature data itself, and computing it before canonicalization changes the bytes. The correct sequence: generate XML, then canonicalize, then hash, then sign, then send.
Do I need to handle hash errors myself when using Qoyod?
No. Qoyod handles generating the XML, canonicalizing it, computing the fingerprint in the correct encoding, linking the PIH field, syncing the ICV counter, signing, and connecting with the Fatoora platform automatically. You do not need to write any hash code or diagnose fingerprint-related rejection messages.