Skip to main content
KreupAI Logo
RESOURCE GUIDEApplies to: Saudi ArabiaRetailOS
KB-014

ZATCA Phase 2 Integration: Onboarding, Clearance and the Errors That Block Invoices

The onboarding sequence, clearance and reporting flows, the XML fields that fail validation, and a step-by-step recovery for an invoice blocked mid-trade.

Author:Bosco Sabu John
10 min read

ZATCA Phase 2 Integration: Onboarding, Clearance and the Errors That Block Invoices

ZATCA Phase 2 integration fails at three points: onboarding, where the CSR or OTP is wrong; validation, where the XML breaks a business rule or the signature does not verify; and the invoice chain, where the counter or previous hash is out of sequence. Errors block the document; warnings do not.

Written for the finance manager or owner of a Saudi SMB whose invoicing system is connected, or about to be, to Fatoora, and for whoever gets called when a till stops printing. After reading it you should be able to tell an outage from a rejection, a warning from an error, and decide in the moment whether the customer can be handed the document. Every technical statement comes from ZATCA's published guidelines and standards, linked at the end; where a list is not published, the gap is flagged rather than filled.

Onboarding: the sequence, in order

Onboarding is per EGS unit, not per company. An EGS (E-Invoice Generation Solution) unit is one instance of an invoicing solution: a till, a back-office ERP instance, a branch server. Each gets its own certificate, so sharing one credential across devices breaks the counter and hash chain.

  1. Check eligibility. ZATCA's Detailed Technical Guidelines state that newly registered VAT taxpayers must wait 2 business days after registration before onboarding. A same-day attempt fails in ways that look like a system fault.
  2. Generate the OTP. Log in to the Fatoora portal with ERAD credentials (TIN or the email registered with ZATCA) and onboard a new solution unit. Codes are valid for 1 hour, up to 100 per request. For a chain, generate in bulk and issue one code per till.
  3. Build and submit the CSR. The unit generates a key pair and a Certificate Signing Request. Mandatory content includes the Common Name, EGS serial number, VAT registration number, and the invoice type (the functionality map) declaring whether the unit issues standard documents, simplified, or both. Get that wrong and step 5 demands documents the unit cannot produce.
  4. Call the Compliance CSID API. Submitting the CSR with the OTP returns a compliance CSID. It is not for live invoices; it exists only to run the compliance checks.
  5. Pass the compliance checks. The unit submits three compliant test documents per declared invoice type: an invoice, a credit note and a debit note. A unit declared for both types submits six, to the Compliance Invoice API.
  6. Call the Production CSID API. ZATCA's certificate authority then issues the production CSID used for the live Clearance and Reporting APIs. Per the Security Features Implementation Standards it uses ECDSA with SHA-256 on a 256-bit key, valid up to 60 months.
  7. Renew before expiry. Renewal repeats the OTP and CSR steps against the renewal endpoint, using the portal's "Renewing Existing Cryptographic Stamp Identifier (CSID)" action. The old CSID is revoked and replaced, so renewal is a cutover. Schedule it outside trading hours.

Authentication is HTTP Basic with Base64(CSID:secret) plus an accept-version header. That gives a fast diagnostic: whole-device failure points at credentials, scattered failure points at data.

Clearance and reporting: two lifecycles

ClearanceReporting
DocumentStandard tax invoice and notes (B2B, B2G)Simplified tax invoice and notes (B2C)
TimingBefore the document reaches the buyerWithin 24 hours of generation
SigningZATCA applies its stamp and QR codeThe seller signs first; signing is mandatory
Valid outcomeCleared document returned, stamped, with QRAcknowledgement only; nothing to print
Rejected outcomeNot a valid tax invoiceInvalid; cancel by credit note and reissue

A standard invoice cannot be handed over until ZATCA returns it, so a B2B counter is coupled to an API response. A simplified invoice is already in the customer's hand when submitted, so a rejection has to be unwound rather than prevented. Article 2(3/B) of the E-Invoicing Implementation Resolution sets the window: simplified invoices and their notes "must be reported to the Authority within a period which must not exceed (24) hours from its generation". The clock runs from generation, not from close of business.

What ZATCA actually validates

Submissions are checked in layers, and the layer that fails tells you who fixes it: schema and syntax (well-formed UBL 2.1 in the required structure); business rules from the XML Implementation Standard, the Data Dictionary and the Resolution; referential checks, described as "additional validations performed" beyond the published rules, including duplicate detection and chain continuity; and cryptographic validation of the signature against a certificate that is valid, unrevoked and bound to this VAT registration.

Rule identifiers run in families: BR for core integrity, BR-CO for conditional and calculation rules, BR-S, BR-Z, BR-E and BR-O for standard-rated, zero-rated, exempt and out-of-scope VAT, BR-CL for code lists, BR-DEC for decimal precision, and the Saudi families BR-KSA, BR-KSA-DEC, BR-KSA-CL, BR-KSA-EN16931 and BR-KSA-F, set out in section 13.3.

The riskiest fields are structural rather than commercial, so nobody in finance reviews them: cbc:UUID, distinct from the invoice number; cbc:InvoiceTypeCode with its name attribute, where positions 1 and 2 carry the document type (01 tax invoice, 02 simplified) and later positions flag subtypes such as self-billing, nominal supply, export and summary; the three cac:AdditionalDocumentReference blocks holding ICV, PIH and QR; and party identification scheme codes from ZATCA's list (CRN, MOM, MLS, SAG, OTH for sellers; NAT, GCC, IQA, PAS, TIN for buyers).

Common validation failure categories

ZATCA returns messages, not a public catalogue of numbered codes. These categories group what actually breaks, with the rule family each surfaces under.

CategoryLikely causeFix
Schema or syntaxElements out of UBL sequence; namespace prefix changed; unescaped character in a trade nameValidate against ZATCA's offline SDK before submitting
Signature does not verifyXML pretty-printed or re-serialised after signing; wrong canonicalisationSign last; transmit the exact signed bytes
Certificate rejectedCompliance CSID used on a live endpoint; expired or revoked production CSID; credential copied from another unitOne credential store per EGS unit; alert on expiry months ahead
Chain break (ICV or PIH)Counter reset by a restore; two processes under one EGS identity; ICV reused after rejectionOne counter per unit, persisted atomically with the last hash
Wrong endpoint for the typeSimplified invoice sent to Clearance; InvoiceTypeCode name not matching the documentDerive the endpoint from the document type in code, not a toggle
VAT arithmetic (BR-CO, BR-DEC)Rounding at line level then again at total level; discounts applied after tax; excess decimalsFollow the standard's arithmetic exactly, including where rounding is permitted
VAT category (BR-S, BR-Z, BR-E, BR-O)Zero-rated or exempt line with no exemption reason code or textMake the exemption reason mandatory in the tax code master
Party identification (BR-KSA-CL)CRN used where a different licence type applies; buyer identification missingMap scheme codes at master-data level, then validate on save
Missing buyer VAT numberWalk-in customer invoiced as B2B; incomplete customer masterBlock a standard invoice where the buyer has no valid VAT registration
TimestampsLocal time where UTC is expected, or the reverse; future-dated issue times from an unsynchronised clockSynchronise device clocks; treat drift as a compliance defect
Duplicate submissionRetry logic resending after a timeout, so ZATCA sees the same UUID twiceIdempotent submission keyed on the UUID; on timeout, query before resending
Note reference missingCredit or debit note without the original Invoice Reference Number or the reasonEnforce the link at the point of refund in the POS

[NEEDS SOURCE: a definitive published list of ZATCA validation error codes and their exact text. ZATCA publishes the business rules in the XML Implementation Standard and a validating SDK, but a numbered error code catalogue was not retrievable from a ZATCA source. Confirm the current message list against the Compliance and Enablement Toolbox SDK output and the Developer Portal.]

Warnings and errors are not the same decision

ZATCA's Detailed Guidelines define three outcomes. An invalid document "has at least one fatal error that results in it not being considered as a valid document". A document accepted with warnings "has no fatal errors but has at least one warning". A valid document has neither.

The Developer Portal Manual documents 200 for success, 202 for accepted with warnings, 303 where clearance is disabled, 400 for a bad request and 500 for a server error. The 202 causes the trouble: on clearance it still returns a stamped document, so the invoice is valid and can be handed over. Warnings are "associated with accepted documents which are still not fully compliant", a defect queue rather than a blocker.

The rule at the counter: only a rejection stops you giving the customer the document. Teams that block on warnings lose sales; teams that ignore them for a year accumulate non-compliant invoices they will have to explain. 303 is neither, and changes the flow rather than failing it, so handle it explicitly.

When clearance is unavailable

Article 7(5) of the Resolution requires taxpayers to "notify the Authority through the means specified by the Authority of any incidents, technical error or emergency matters which hinder the generation of Electronic Invoices", then resume compliant generation "promptly as soon as such incident is fixed". Annex 1 requires solutions to keep operating offline, queueing documents and reporting them once the connection returns.

ZATCA's Detailed Guidelines set behaviour by flow. For B2B, retry clearance for roughly five minutes; if still unavailable, share the uncleared invoice with the buyer, record evidence of the failure, and retry approximately every fifteen minutes. For B2C, report inside the 24-hour window, and if the outage prevents that, notify ZATCA through the malfunction notification service.

Two distinctions decide what you do. Is the failure yours or theirs: a 400 with validation messages fails identically on retry, while a 500, timeout or refused connection is an outage where retry is correct. And is the EGS generating invoices at all: a network outage is not a malfunction of the solution, but a POS that cannot produce a signed XML is, and that is what the notification service is for.

Credit and debit notes

Notes carry the same apparatus as invoices: UUID, hash, counter, stamp, QR. Three rules trip people up.

The note follows the invoice type. A credit note against a simplified invoice is reported; against a standard invoice it must be cleared. A refund at the till and a credit against a corporate account take different paths.

The reference is mandatory. Notes "must be issued with a reference to the original invoice(s) to which they are issued", by Invoice Reference Number. Where one note covers many invoices, ZATCA permits a range, giving the example of "IRN from 001 to IRN 100 issued during the period 1 Jan 2022 to 31 March 2022".

Direction matters. A credit note refunds the buyer or corrects an invoice issued in error. A debit note increases the value of the original invoice or its VAT. Correcting a mispriced invoice upward with a credit note passes technical validation and is still wrong in the ledger.

Recovery procedure: an invoice blocked mid-trade

Assume a Thursday afternoon, a busy counter, failing submissions.

  1. Read the response code first. 400 with validation messages is a document problem. 500, a timeout or a refused connection is availability. 401 or 403 is credentials. Three different answers; guessing wastes the window.
  2. Establish whether it is one device or all. All tills failing points at connectivity or a revoked or expired CSID. One till failing points at that unit's credential, clock or counter state.
  3. Outage, B2C: keep trading. The invoice is valid in the customer's hand. Queue it and let the reporting queue drain, tracking the 24-hour deadline per document, not per day.
  4. Outage, B2B: retry for about five minutes. If clearance is still unavailable, share the uncleared invoice with the buyer, record the failure with timestamps and the response, and let the retry loop run at roughly fifteen-minute intervals.
  5. Rejection: do not resubmit the same document. Correct the data and issue a replacement, which ZATCA says "should include its own new unique hash, UUID, invoice counter value and timestamp".
  6. Protect the chain. An ICV, once used, cannot be reused, even by a rejected document. ZATCA stores the hashes of rejected documents, so the previous invoice hash on your next submission must be the hash of the immediately preceding document, rejected or not. Never roll the counter back.
  7. If a rejected simplified invoice is already with a customer, cancel it with a credit note referencing the original IRN, then issue a corrected invoice. There is no retrospective fix to a document in someone's bag.
  8. If the EGS itself has failed, as opposed to the network, raise the malfunction notification. Failure to notify is one of the violations ZATCA lists as starting with a warning to the establishment.
  9. If the CSID is expired or revoked, re-onboard: OTP, new CSR, compliance checks, production CSID. Queueing cannot work around this one, so certificate expiry monitoring belongs alongside licence renewals.
  10. Reconcile before closing. The Fatoora portal shows twelve months of documents by accepted, accepted with warnings and rejected. Compare that with the day's sales ledger. A gap found on the day is an hour of work; at year end it is a reconstruction.

Where teams get this wrong

One certificate, many tills. Copying a working credential to a second device puts two counters under one identity and breaks the chain within hours. It usually surfaces days later, on the device nobody watches. The same happens when a till is restored from backup: the counter goes backwards and everything after it fails. Recovery is forward, never backward.

Pretty-printing after signing. A logging proxy, an API gateway or a helpful serialiser reformats the XML and invalidates the signature. The document is arithmetically perfect and rejected anyway. If a payload passes ZATCA's offline SDK locally but fails over the wire, something in the path is rewriting it.

Standard invoices for walk-in customers. A cashier picking "tax invoice" instead of "simplified" triggers clearance, which fails on missing buyer identification, and the queue stops moving.

No watchdog on the 24-hour window. A till that quietly stops reporting is invisible in the sales figures. The only signal is an absence of acknowledgements, which nobody sees unless something is watching.

What to automate, and what not to

Automate everything mechanical and stateful: XML generation, signing, the counter and hash chain, idempotent submission and retry, the offline queue, a per-document watchdog against the 24-hour deadline, certificate expiry alerts weeks ahead, and a daily reconciliation between Fatoora's counts and the sales ledger. These are the tasks where people fail and software does not.

Do not automate the judgements. Whether a supply is zero-rated, exempt or out of scope is a VAT decision, and a system that guesses produces documents that clear and are still wrong. The decision to hand an uncleared invoice to a buyer during an outage should not be silent. Cancelling a reported invoice should require a person, because it is a customer conversation as much as a technical one.

Where a system helps

The integration is a few endpoints. The operational load is elsewhere: one certificate per till with its own expiry, one counter and hash chain per unit that must survive restarts and restores, a queue that drains after an outage, and a reconciliation proving nothing was lost. An ERP that models each till as a distinct EGS unit with its own credential and chain state, queues rather than drops when Fatoora is unreachable, and reports accepted, accepted-with-warnings and rejected counts by device turns this into a morning report rather than a daily interruption. See RetailOS for SMB ERP.

FAQ

Can I keep selling if Fatoora is unreachable? Yes for B2C: the simplified invoice is valid when issued and reported within 24 hours. For B2B, ZATCA's guidance is to retry for about five minutes, then share the uncleared invoice with the buyer while recording evidence and continuing to retry roughly every fifteen minutes.

What is the difference between a warning and an error? An error is fatal: the document is not valid and must be corrected and reissued. A warning means the document is accepted and usable but not fully compliant. Only errors stop you giving the customer the invoice.

What happens if a simplified invoice misses the 24-hour window? Report it as soon as you can and keep evidence of why it was late. Late reporting is a failure against Article 2(3/B) of the Implementation Resolution. [NEEDS SOURCE: whether ZATCA applies a specific penalty for late reporting of a simplified invoice, as distinct from failure to report at all, and at what amount.]

Can I correct an invoice that has already been cleared? Not by editing it. Deleting or amending an electronic invoice after issuance is a violation ZATCA states "begins with a fine of SR 10,000". Correct it with a credit or debit note referencing the original Invoice Reference Number.

Related reading: "What Is Fatoora? Saudi Arabia's E-Invoicing Platform, Explained" (KB-013).

Sources