WW-Enterprises

W-Enterprises/Insights/ZATCA Phase 2 validation failures

Field notes / 14 August 2026

What actually fails ZATCA Phase 2 validation: a field guide.

The Fatoora documentation tells you what a compliant invoice looks like. It does not tell you which requirements quietly break real systems in production. These six do, in roughly this order of frequency, and each one costs about a week the first time it is met under a deadline.

Written for teams integrating billing, ERP or point-of-sale systems with the ZATCA Clearance and Reporting APIs. Wave 25 covers every taxpayer above SAR 187,500 in revenue, with integration due by 1 February 2027.

First, the mental model that prevents half of these

Phase 1 asked for a structured electronic invoice. Phase 2 asks for something different in kind: a cryptographically chained sequence of documents, where each document proves its own integrity, proves its position in the sequence, and is exchanged with a government API in real time.

Most failed integrations treat the invoice as the unit of work. The Fatoora platform treats the sequence as the unit of work. Every failure below is some version of that mismatch.

1. The hash chain breaks on the first rejection

Every document carries a Previous Document Hash, the hash of the last document the invoicing unit generated. The requirement that surprises everyone: the chain includes documents ZATCA rejected. A rejected document is still a generated document, and the next submission must chain from its hash, not from the last accepted one.

Systems that only persist the hash on success drift out of sequence the first time anything is rejected, and from that point every subsequent submission fails with a chain error. The failure is discovered days after the cause, which is what makes it expensive.

The fix is a persistence rule: write the document hash and counter to durable storage before submission, atomically with generating the document, regardless of what ZATCA later says about it. Generation is the event that advances the chain. Submission is not.

2. The invoice counter does not survive operations

The Invoice Counter Value must be monotonic and can never be reused or reset. That is easy on a whiteboard and hard in operations, because three routine events reset naive implementations:

  • A container redeploy, when the counter lives in process memory or a local file inside the image.
  • A database restore, which rewinds the counter to the backup point while ZATCA's view of the sequence stays where it was.
  • Two instances behind a load balancer sharing one CSID, each maintaining its own idea of the counter.

The fix is structural: one CSID means one writer. Give the counter a single source of truth that survives deploys, either a dedicated database sequence or a transactional row, and never scale the signing path horizontally under a single certificate. If you need throughput, onboard additional devices, each with its own CSID and its own chain.

After a database restore, do not resume from the restored counter. Reconcile against your submission log first, because ZATCA has seen documents your restored database has not.

3. The CSR fails onboarding with an unhelpful error

Onboarding runs through a one-time password from the Fatoora portal, a certificate signing request from your unit, a Compliance CSID, a set of compliance documents, and finally a Production CSID. The step that eats days is the CSR, because its field conventions are specific to ZATCA and a generic OpenSSL recipe produces a request that fails with a generic 400.

The fields that need to be exactly right: the common name convention for the unit, the organisation identifier carrying the VAT registration number in its correct format, the serial number composed as the structured device identity ZATCA expects rather than a random value, and the certificate template name for the environment you are onboarding into. Sandbox, simulation and production are different certificate authorities, and a CSID issued in one is meaningless in the others.

The fix: treat onboarding as code, not as a ceremony. Script the CSR generation with the field conventions pinned, keep the script in the repository, and expect to run it once per device per environment. The compliance check set, three test documents for a standard-only unit, three for simplified-only, six for both, is also scriptable, and should be.

4. The TLV QR code is almost right

Simplified invoices carry a QR code that is a Base64 wrapper around nine tag-length-value fields: seller name, VAT number, timestamp, invoice total with VAT, VAT amount, the XML hash, the ECDSA signature, the public key, and the authority's signature over that key. Three encoding mistakes account for nearly all QR failures:

  • Length prefixes counted in characters instead of bytes. Fine for ASCII, wrong the moment the seller name is Arabic, because UTF-8 Arabic characters are two bytes each. This is the classic one: the QR validates for a test seller called "Test Co" and fails for every real Saudi business name.
  • Fields in the wrong order or with wrong tag numbers. The tags are positional by number, not by name, and validators disagree more politely than the platform does.
  • Re-encoding an already encoded value, usually Base64 applied twice somewhere in a pipeline that passes strings between services.

The fix: build the TLV from raw bytes end to end, never from strings, and keep one golden test: a seller name in Arabic script, with the expected Base64 output pinned in the test suite.

5. The PDF is correct and the XML is not

The rendered PDF a human reads is a by-product. The UBL XML is the invoice. The most expensive failure category is a document that looks perfect, with correct Arabic in the PDF layer, while the XML carries mis-encoded, transliterated or missing Arabic in the fields ZATCA actually validates.

It is expensive because it is discovered last. Every visual review passes. The client signs off. Then clearance rejects the document, or worse, reporting accepts a simplified invoice whose QR does not match its own XML hash because the two were generated from different intermediate representations.

The fix is architectural: one canonical data structure feeds both the XML and the rendered document, and the hash in the QR is computed from the exact bytes submitted, not from a parallel copy. If the words "we generate the PDF from one object and the XML from another" appear anywhere in a design, that design fails in production. The same discipline applies to bilingual rendering generally, covered in the Arabic PDF field guide.

6. The clock is wrong, so the signature is wrong

The issuance timestamp is bound into the signature and into the QR code. A server drifting even modestly produces documents that fail validation or, more confusingly, documents rejected for being issued in the future. The prohibited-functionality list also bans any user-adjustable clock at issuance, so "fix it by hand" is not a compliant answer.

The fix: NTP on the signing host, monitored, with an alert on drift. It is one line of infrastructure and it removes an entire category of intermittent failure that otherwise gets misdiagnosed as a signing bug.

The checklist

Design decisionFailure it prevents
Persist hash and counter atomically at generation, not at acceptanceChain breaks after the first rejection
One CSID, one writer; a durable sequence for the counterCounter resets on redeploy, restore or scale-out
Reconcile the counter against the submission log after any restoreSequence divergence ZATCA can see and you cannot
Scripted CSR with ZATCA field conventions pinned, per environmentDays lost to generic 400s at onboarding
TLV built from bytes, with an Arabic-name golden testQR codes that pass in English and fail in Arabic
One canonical object feeding both XML and PDF; hash the submitted bytesDocuments that look right and validate wrong
NTP on the signing host, monitoredIntermittent signature and timestamp failures

Why this matters right now

Wave 25 pulls every taxpayer above SAR 187,500 in revenue into the integration phase by 1 February 2027, and because that threshold equals the voluntary VAT registration threshold, it is effectively the last rung of the ladder. The businesses integrating over the next six months are the smallest and least engineering-resourced ZATCA has ever onboarded, which means these six failures are about to be met by thousands of teams for the first time, most of them inside ERP and accounting products rather than custom builds.

If you are carrying clients through this, the difference between a smooth wave and a painful one is whether the integration was designed around the sequence, or around the invoice.

Integrating, or fixing an integration that is failing?

This practice does ZATCA Phase 2 integration and remediation: Fatoora onboarding, chain and counter repair, QR and XML correction, delivered directly or as subcontract capacity under your own name. The full service page is here.

Requirements described here reflect ZATCA's published resolutions and technical guidelines as checked in August 2026. ZATCA is the authoritative source and requirements change. Nothing on this page is tax or legal advice.