If you are preparing for ZATCA Phase 2 in Saudi Arabia, two technical requirements come up in almost every conversation: the XML invoice file and the QR code. They are what turn an ordinary invoice into a compliant electronic invoice — and they are also where most integration projects stall, because they are the parts a human cannot eyeball.
This guide explains both in plain language: what the XML format actually is, what goes inside the QR code, how the two are connected by hashing and cryptographic stamping, and how to check your own output before you send anything to ZATCA. For the wider picture of the mandate itself, start with our pillar guide to ZATCA e-invoicing (Fatoora) and Phase 2.
Why Phase 2 needs a machine-readable file at all
Phase 1 of the Saudi e-invoicing mandate ("Generation") mostly asked businesses to stop issuing handwritten and freely-edited invoices, and to produce them from a compliant electronic system instead. A PDF with the right fields, plus a QR code on simplified invoices, largely satisfied it.
Phase 2 ("Integration") changes the audience. The invoice is no longer just a document for your customer — it is a structured record that the ZATCA platform receives, validates and either clears or accepts. A PDF is opaque to software; a structured XML file is not. So Phase 2 defines a strict file format, a strict set of security fields, and a strict way of exchanging them.
That is the whole idea behind the technical requirements: two systems that have never met must agree, byte for byte, on what an invoice says and prove that nobody altered it in between.
The XML format: UBL 2.1, with a Saudi profile on top
ZATCA did not invent a new file format. Phase 2 invoices are built on UBL 2.1 (Universal Business Language), an international OASIS standard already used for e-invoicing across Europe and Asia. On top of UBL, ZATCA publishes its own implementation standard — effectively a Saudi profile that says which UBL elements are mandatory, which are forbidden, and what values they may carry.
In practice, that means three layers your file has to satisfy:
- Well-formed XML — it parses, the namespaces are declared correctly, and elements appear in the order the schema expects.
- Valid UBL 2.1 — it matches the underlying invoice schema.
- Compliant with the ZATCA business rules — the validation layer that checks seller VAT number format, tax category codes, totals that add up, date formats, and so on.
A file can pass the first two and still be rejected by the third. That is normal, and it is why ZATCA provides a developer portal and validation tooling: you are expected to test against the rules before going live.
What the XML contains
The structure mirrors the invoice you already know, just in a rigid vocabulary. Broadly:
- Document identity — invoice number, issue date and time, the invoice type code that distinguishes a standard tax invoice from a simplified one and an invoice from a credit or debit note, and the currency.
- Seller and buyer parties — legal name, VAT registration number and address fields. What is mandatory for the buyer differs between standard and simplified invoices; see simplified vs standard tax invoices in Saudi Arabia.
- Line items — description, quantity, unit price, discounts, tax category and rate, and the tax amount per line.
- Tax and monetary totals — taxable amount, VAT amount and payable total. These must be internally consistent; a rounding mismatch of a single halalah is a validation error, not a warning.
- Security and sequencing fields — the UUID, the invoice counter value, the hash of the previous invoice, the cryptographic stamp and the QR code, all carried in dedicated extension elements.
Standard invoices may also be shared with the buyer as a PDF/A-3 file with the XML embedded inside it. That gives a human something readable while keeping the machine-readable original attached — a genuinely useful pattern if your customers are not yet automated.
The security chain: UUID, counter, previous hash, cryptographic stamp
The fields that trip people up most are not the commercial ones. They are the four that make an invoice tamper-evident and make your invoice sequence provable.
- UUID — a universally unique identifier for the document, separate from your human-facing invoice number.
- Invoice Counter Value (ICV) — a strictly incrementing counter across the invoices issued by a given solution unit. Gaps and resets are exactly what auditors look for.
- Previous Invoice Hash (PIH) — the hash of the immediately preceding invoice, which chains each document to the one before it. Change an old invoice and every later hash stops matching.
- Cryptographic stamp — a digital signature applied with a certificate you obtain during onboarding, when your system generates a certificate signing request and receives a cryptographic stamp identifier back from ZATCA.
Together these do something a PDF never could: they prove not only that this invoice is unaltered, but that it belongs in a specific position in an unbroken sequence. It is conceptually very close to what Spain is doing with its own anti-fraud rules — our guide to VeriFactu and Spain's electronic invoicing law shows the same chaining idea in a different jurisdiction.
The QR code: a TLV structure, not a link
A ZATCA QR code is frequently misunderstood. It does not contain a URL to a portal page, and it is not free-form text. It is a compact binary structure encoded in TLV format — Tag, Length, Value — and then Base64-encoded before being rendered as the QR image.
Each field is written as a one-byte tag number, a one-byte length, then the value bytes. Concatenate the fields, Base64 the result, and that string is what the QR code carries. The reason for this design is size: TLV is far more compact than JSON or XML, which keeps the QR code small enough to print legibly on a thermal receipt.
The tags
The first five tags are the ones present from Phase 1 onward:
- Tag 1 — Seller name, as registered.
- Tag 2 — Seller VAT registration number.
- Tag 3 — Invoice timestamp, in ISO 8601 date-time format.
- Tag 4 — Invoice total, including VAT.
- Tag 5 — Total VAT amount.
Phase 2 adds cryptographic tags that let a verifier check the invoice offline, without calling any service:
- Tag 6 — Hash of the XML invoice.
- Tag 7 — The digital signature produced with the ECDSA algorithm.
- Tag 8 — The public key corresponding to the signing certificate.
- Tag 9 — The signature of the cryptographic stamp identifier, used where the invoice is one that ZATCA itself stamps.
Because tags 6 to 8 travel inside the QR code, an inspector with a phone can read a printed receipt, recompute the hash and verify the signature against the embedded public key. That is the practical payoff of all the cryptography: verification at the counter, not weeks later in an audit.
Where the QR code must appear
Simplified tax invoices — the B2C receipts you hand to a walk-in customer — must carry the QR code visibly on the printed or displayed document. Standard tax invoices carry the QR code data as well, though the buyer-facing experience is usually the XML or the PDF/A-3 wrapper rather than a code scanned at a till.
One detail worth stressing: the QR code must reflect the final invoice. If you regenerate totals, apply a discount or issue a credit note, you generate a new document with its own hash, counter position and QR code. You never edit an issued invoice.
Clearance vs reporting: what happens to the file
The XML you build does not simply sit in your archive. Its route depends on the invoice type:
- Standard tax invoices (B2B and B2G) go through clearance. You submit the invoice to ZATCA and it is validated and stamped before you share the cleared version with your buyer. An invoice that fails clearance is not a valid tax invoice.
- Simplified tax invoices (B2C) go through reporting. You issue the invoice to the customer immediately and transmit it to ZATCA within 24 hours.
This is the single biggest architectural consequence of Phase 2: your billing system now has a synchronous dependency on an external service for one class of documents and an asynchronous queue for another. It needs retry logic, a way to hold invoices that failed validation, and monitoring. Our breakdown of the Phase 2 integration requirements and rollout waves goes into what that means for onboarding and timing.
How to check your own output before you go live
You can catch the majority of problems yourself, in this order:
- Validate the XML against the schema first. Namespace and ordering errors are cheap to fix and they mask everything downstream.
- Run the business rules. The ZATCA validation tooling reports rule identifiers; treat each one as a test case in your suite rather than a one-off fix.
- Decode your own QR code. Base64-decode the payload and walk the TLV structure byte by byte. Confirm that tag 4 equals the invoice total including VAT and tag 5 equals the VAT total — a surprising number of bugs are simply the wrong total in the wrong tag.
- Recompute the hash from the canonicalized XML and confirm it matches tag 6. If it does not, your canonicalization step is wrong, which is the classic cause of "valid locally, rejected on submission".
- Test the chain. Issue several invoices in a row and verify that the counter increments by exactly one and that each previous-hash field matches the prior document.
- Check the arithmetic. Line totals, taxable base and VAT must reconcile exactly. If you are unsure which rate applies to a given supply, review VAT in Saudi Arabia: rates, registration and returns.
Because the rules, schema versions and validation tooling are updated periodically, always confirm the current specification — and any wave or deadline that applies to your business — on the official ZATCA developer and Fatoora portals before you finalise an implementation.
Common mistakes
- Treating the QR code as text. Encoding a readable string such as "Seller: … VAT: …" produces a scannable code that fails compliance. It must be TLV, then Base64.
- Timestamp format drift. Local date formats, missing time components or careless timezone handling in tag 3 and in the XML issue date and time.
- Resetting the counter. Redeploying a system or spinning up a second instance without preserving the counter and the previous hash breaks the chain.
- Rounding differences. Rounding per line and again at the total, in different directions, produces the one-halalah mismatch that fails validation.
- Arabic text encoding. Seller names in Arabic must be UTF-8 throughout, and the TLV length byte counts bytes, not characters — a very common source of malformed QR payloads.
- Editing an issued invoice. Corrections are made with a credit or debit note, never by modifying the original.
You do not have to build this yourself
Reading the list above, the honest conclusion for most businesses is that ZATCA Phase 2 is not an invoicing feature — it is a cryptography and integration project. Certificate onboarding, canonicalization, hash chaining, TLV encoding, clearance retries and archival all have to be right, and all have to stay right as the specification evolves.
That is exactly what a compliant invoicing platform is for. Facturi generates the UBL 2.1 XML, builds the TLV QR code, maintains the counter and hash chain, and handles clearance and reporting for you — in Arabic or English — so your team issues an invoice and the compliance layer takes care of itself. Try Facturi free and see a compliant Saudi invoice generated end to end.