ZATCA Phase Two is not a new invoice format. It is a live external system call in the middle of your billing flow, and if it fails your standard invoice does not get issued.
Most of the guidance written about Saudi Arabia’s e-invoicing programme is written by software vendors, and it describes Phase Two as an upgrade you buy. That framing is comfortable and wrong. The Integration Phase moves the tax authority from a passive recipient of your data to a synchronous participant in your invoicing transaction. For standard tax invoices, ZATCA clears the invoice before your customer ever sees it. That is an availability dependency you have taken on, whether or not anyone in the business chose to describe it that way. This is a reading for the people who have to build and own that dependency, not the ones selling the connector.
What actually changes between Phase One and Phase Two
Phase One, the Generation Phase, required you to produce structured electronic invoices with a QR code and stop issuing handwritten or free-text PDFs. It was largely a local concern: your system generated the invoice and you kept it. Phase Two, the Integration Phase, adds the tax authority into the loop in real time. There are two paths, and they behave differently.
Free · 4 minutes
Is your engineering team shipping safely, or quietly accumulating risk?
Fourteen questions on how work gets from idea to production — cadence, testing, rollback, and the key-person risk in your delivery. Banded finding on screen, full sheet by email.
- Standard tax invoices (broadly B2B and B2G) are cleared. The invoice is submitted to ZATCA and must be cleared before it is shared with the buyer. Clearance is synchronous. No clearance, no valid invoice.
- Simplified tax invoices (broadly B2C) are reported. They are issued to the buyer at the point of sale and reported to ZATCA within 24 hours. Reporting is asynchronous, which gives you a buffer clearance does not.
The distinction matters enormously for architecture. A reporting flow can queue, retry and batch without holding up the sale. A clearance flow sits on the critical path of issuing a B2B invoice. If your point-of-issue for standard invoices cannot tolerate a round-trip to an external service — including its latency, its maintenance windows and its occasional failures — you have a design problem, not a compliance problem.
The waves are a scheduling mechanism, not a reprieve
ZATCA enforces the Integration Phase in waves, defined by past VAT-subject revenue and announced with a target integration date. The thresholds have stepped down steadily. Wave 24, for example, covers taxpayers whose VAT-subject revenue exceeded SAR 375,000 in 2022, 2023 or 2024, with integration required by 30 June 2026. Each wave gets roughly six months’ notice from ZATCA before its date.
Two things follow. First, verify your own wave and date against ZATCA’s official notification rather than a vendor’s summary, because the exact revenue years and cut-off are set per wave and change. Second, if you operate multiple legal entities in the Kingdom, they may fall into different waves. Treating that as a single programme with one deadline, rather than a rolling schedule of onboardings, is how firms end up integrating the same solution three times under three separate deadlines with no shared tooling.
The cryptography is the part people underestimate
A compliant Phase Two invoice is not just well-formed XML. Each standard invoice carries a set of security elements that your system has to generate correctly and in the right order:
- A UUID unique to each invoice.
- A cryptographic stamp applied using a Cryptographic Stamp Identifier (CSID) that you obtain through onboarding.
- A SHA-256 hash that chains each invoice to the previous one, so the sequence is tamper-evident.
- A TLV-encoded QR code carrying the mandated tags, including — for cleared invoices — the ZATCA stamp.
- The document itself in UBL 2.1 XML, or as a PDF/A-3 with the XML embedded.
The hash chain is the detail that quietly reshapes your system. It means invoices are no longer independent records you can generate in any order or regenerate at will. The sequence has state. That has consequences for how you handle failed submissions, credit notes, replays after an outage, and any parallelism in your issuing pipeline. Build the counter naively and a single out-of-order submission poisons the chain.
Onboarding is a certificate lifecycle, not a one-off form
To join Fatoora your solution units are onboarded and issued an X.509 certificate via a Certificate Signing Request against ZATCA’s Compliance and Enablement toolkit. The certificate has a validity period and will need renewal; each solution unit or device that issues invoices needs its own credential and, in practice, its own key management. Treating this as a day-one activation step and forgetting it is how a firm discovers its clearance calls have started failing because a certificate expired unnoticed. The onboarding is the beginning of a credential lifecycle you have to operate, the same discipline you would apply to any material service provider dependency.
Here is the shape of a clearance call once you are onboarded — deliberately minimal, to show where the pieces sit rather than to be dropped into production:
# Clear a standard tax invoice against Fatoora, then attach the returned stamp
# Prereqs: signed UBL 2.1 XML with UUID, CSID stamp and prior-invoice hash already applied
import base64, requests
API = "https://gw-fatoora.zatca.gov.sa/e-invoicing/core/invoices/clearance/single"
def clear_invoice(signed_xml: bytes, invoice_hash: str, uuid: str,
binary_token: str, secret: str) -> dict:
payload = {
"invoiceHash": invoice_hash, # SHA-256, base64; links to prior invoice
"uuid": uuid, # this invoice's UUID
"invoice": base64.b64encode(signed_xml).decode(),
}
resp = requests.post(
API,
json=payload,
headers={
"Accept-Version": "V2",
"Clearance-Status": "1", # 1 = require clearance for standard invoices
"Content-Type": "application/json",
},
auth=(binary_token, secret), # CSID credentials from onboarding
timeout=30, # clearance is on the critical path: bound it
)
resp.raise_for_status()
body = resp.json()
# On success ZATCA returns the cleared, stamped invoice. Persist it and
# regenerate the QR from the cleared document before sharing with the buyer.
if body.get("clearanceStatus") != "CLEARED":
raise RuntimeError(f"Not cleared: {body.get('validationResults')}")
return body
The interesting logic is not in the call. It is in what wraps it: how you queue and retry when clearance is slow or down, how you keep the hash chain intact across those retries, how you fall back for standard invoices when clearance is genuinely unavailable, and how you separate the sandbox and production toolkits so a test certificate never signs a real invoice. The firms that struggle in Saudi Arabia are the ones that built the same real-time discipline into their e-invoicing readiness across the GCC and the ones that did not.
The decision a board should actually take
The question is not which vendor to buy. It is whether real-time tax clearance is a dependency your invoicing architecture can absorb without a single point of failure, and whether you can onboard each entity, in each wave, without rebuilding the integration every time. Answer that as an availability and cryptography problem and the tooling choice follows. Answer it as a format upgrade, and you will meet your first wave’s deadline and be caught out by the second.
Most technology problems are not technology problems. They are control problems.
The systems exist. The investment has been made. The question is whether leadership can understand, direct, evidence, and sustain what those systems produce. Find out where control exists — and where it only appears to.