DEV Community

Mousa Hammad
Mousa Hammad

Posted on

ZATCA Wave 25: What Developers Integrating Before 1 February 2027 Need to Know

On 24 July 2026, Saudi Arabia's ZATCA announced the selection criteria for Wave 25 of Phase 2 e-invoicing integration: taxpayers whose taxable turnover exceeded SAR 187,500 in 2022, 2023 or 2024 must integrate their systems with the Fatoora platform no later than 1 February 2027.

That is the lowest threshold in the entire rollout. Waves 1 through 24 walked the number down from SAR 3 billion to SAR 375,000; Wave 25 halves it again. In practice it means Phase 2 now reaches almost every VAT-registered business in the Kingdom - and the developers building for them are meeting the integration for the first time.

If that is you, here is what actually matters, from having shipped one.

Phase 2 is not "add a QR code"

Phase 1 was a printed TLV QR with five fields. Phase 2 is a different animal:

  1. Onboarding - generate a keypair, build a CSR with ZATCA-specific extensions, exchange it for a compliance certificate (CCSID), pass compliance checks, then exchange those for a production certificate (PCSID).
  2. Signing - canonical UBL 2.1 XML, an invoice hash over a precisely-scoped subset of the document, an ECDSA signature on the secp256k1 curve, and a XAdES signature block.
  3. QR - now nine TLV tags, including the invoice hash, your signature, your public key, and the CA's signature over your certificate.
  4. Submission - simplified (B2C) invoices are reported after issue; standard (B2B) invoices are cleared before the buyer gets them.
  5. Chain - every invoice carries a strictly-increasing counter (ICV) and the previous invoice's hash (PIH), making deletions provable.

Budget accordingly. The XML is the easy part.

The part nobody warns you about

ZATCA's real contract lives in its gateway's behavior, not in its documentation. A few examples that each cost me a live rejection to find:

  • Each environment demands a different CSR certificate template. TSTZATCA-Code-Signing for sandbox, PREZATCA-Code-Signing for simulation, ZATCA-Code-Signing for production. Send the wrong one and the gateway replies with a bare Invalid Request - before it reads anything else in your CSR.
  • The device serial belongs in the surname OID (2.5.4.4), not serialNumber (2.5.4.5), because ZATCA's own OpenSSL config wrote SN= and OpenSSL maps that to surname. The semantically correct field is rejected.
  • secp256k1 works in Node and dies in Electron. Electron links BoringSSL, which refuses the curve (UNKNOWN_GROUP). Every test passes on your machine; the packaged desktop app throws at the first signature.
  • The XAdES SignedProperties block must exist in two different byte-shapes - one you hash, one you embed - and its digest is base64 of the hex string, not of the raw bytes.
  • Compliance-green is not reporting-green. /compliance/invoices does not verify the SignedProperties digest at all. You can pass all four compliance documents, collect your production CSID, and fail on your very first real invoice with signed-properties-hashing.

There are ten of these. I wrote them all up, free and in full: github.com/mousah20/zatca-phase2-traps

A realistic plan for the six months you have

Now to October: get onboarding working against the sandbox. Any OTP is accepted there, so there is nothing to wait for. Getting a compliance CSID issued proves your CSR is correct, which is where most of the early time is lost.

October to December: sign and submit all four document types (simplified and standard invoices, credit and debit notes). Then move to the simulation environment with a real Fatoora-portal OTP and, crucially, get an accepted reporting call - not just compliance checks.

December to February: production onboarding with your taxpayer's certificate, then run both systems in parallel briefly before switching over. Leave room for the ICV/PIH chain work: the counter must be reserved inside the same database transaction that writes the invoice, or two concurrent sales take the same number and every invoice after the duplicate is rejected.

Do not leave the integration to January. The gateway's error messages are terse, and each round trip through "reject, guess, retry" costs days.

Shortcuts

If you work in Node.js, the QR encoder alone (all nine Phase-2 tags, byte-correct for Arabic seller names) is MIT-licensed on npm: zatca-qr-tlv.

And if you would rather not spend the six months at all, I packaged the entire integration - onboarding, signing, QR, reporting and clearance - as a production-proven SDK with a live test that onboards a device against ZATCA's sandbox and gets an invoice REPORTED in about two minutes.

Not affiliated with ZATCA. Wave 25 criteria as announced on 24 July 2026; confirm your own wave and deadline through the Fatoora portal.

Top comments (0)