Saudi Arabia only. This module implements the KSA ZATCA "Fatoorah" model (a central clearance/reporting authority). UAE e-invoicing is a separate module (the Peppol "5-corner" model via an accredited service provider) — see your UAE rollout. Each VAT-registered Saudi taxpayer onboards its own company with its own Fatoora OTP; ColorsX74 never holds your OTP or keys.
How it fits together
A draft is not a tax document. An e-invoice is created when you issue an invoice — never when you save a draft. A draft carries a provisional DRFT- reference, posts nothing, and produces no e-invoice, no invoice counter and no hash-chain entry. Issue it and exactly one e-invoice is created, carrying the invoice's final number. Abandon or delete the draft and nothing was ever consumed — so your reported sequence stays gapless.
ZATCA e-invoicing is a gated add-on. When enabled for a company, the module lives under E-Invoicing (ZATCA) in the sidebar (/einvoicing-zatca); its API is served at /api/v1/einvoicing-zatca. It builds each invoice into the mandatory ZATCA UBL 2.1 XML, computes the invoice hash and maintains the per-EGS counter + previous-invoice-hash chain, cryptographically signs it (XAdES) with your company's certificate, and submits it to ZATCA.
Jump to: The two phases · Enabling the module · Onboarding with your Fatoora OTP · Environments & the safety gate · Signing & submitting invoices · Invoice statuses · A missing e-invoice (the catch-up) · Operator notes.
The two ZATCA phases
Phase 1 (Generation). Every invoice carries the mandatory Base64 TLV QR code — shown as a scannable QR image right in the documents table (scan it with any phone camera or the ZATCA app to verify seller, VAT number, KSA-time timestamp, and totals) — and is stored as a structured e-invoice. No ZATCA account is needed — this works the moment you enable the module.
Phase 2 (Integration). Each invoice is rendered as ZATCA UBL 2.1, hash-chained (ICV + previous-invoice-hash), cryptographically signed with your company's CSID certificate, and submitted to ZATCA for Clearance (standard/B2B — cleared before it is shared with the buyer) or Reporting (simplified/B2C — reported within 24 hours; the sale is not blocked). Phase 2 needs your company's CSID, which you obtain by self-onboarding below.
Enabling the module
In E-Invoicing (ZATCA) → Settings: tick Enable ZATCA e-invoicing, choose your Environment (start with Sandbox), and optionally override the seller name / VAT number (otherwise they come from the company record). Save. From this point every sales invoice automatically gets its Phase-1 QR + e-invoice document when it is issued.
Your history is never back-filed. The moment you enable is recorded. Invoices issued before it never get an e-invoice automatically — each one would take the next invoice counter and be reported months late. If you need one for an older invoice, generate it deliberately from the documents list (POST /api/v1/einvoicing-zatca/documents/generate/{invoiceId}). Switching it off and on again loses nothing: an invoice issued while e-invoicing was on still gets its e-invoice, and one issued while it was off never does.
Your company's VAT/TIN must be a 15-digit number that starts and ends with "3" — ZATCA requires this on the certificate request.
Onboarding with your Fatoora OTP (self-service)
To sign and submit (Phase 2) your company needs a CSID (cryptographic stamp identifier) from ZATCA. You obtain it yourself, from your own Fatoora portal:
- Log in to the Fatoora portal (fatoora.zatca.gov.sa) for your VAT-registered company and generate an OTP for onboarding an e-invoicing solution. The OTP is valid for about an hour.
- In the ERP, open E-Invoicing (ZATCA) → Link CSID (Onboarding), paste the OTP, and click Request CSID. The OTP is sent once, used immediately, and never stored or shown again.
- The system runs the full ZATCA flow automatically: it generates a certificate request, fetches your Compliance CSID, runs ZATCA's compliance checks, then fetches your Production CSID. The status timeline shows each step.
- When it reads Onboarded, your company's certificate is active and stored encrypted. The cert thumbprint and expiry are shown on the page.
That's the whole "ready the moment you enter your Fatoora details" step — no involvement from ColorsX74 staff, and your OTP/keys stay with your company.
Environments & the safety gate
ZATCA has three environments, each with its own credentials and base URL:
- Sandbox — open developer testing; accepts dummy data. No legal effect. Use it to try the flow.
- Simulation — pre-production with a real Fatoora simulation OTP. No legal effect.
- Production — real, legally-binding e-invoices.
The hard environment gate. A certificate obtained in Sandbox or Simulation can never be used to submit to Production — the system refuses any mismatch. And the first real Production submission must be explicitly armed by an operator ("Arm live Production submission" on the onboarding screen), so a correctly-onboarded Production company still won't transmit to ZATCA until you turn it on. Onboard in the environment you intend to use.
Signing & submitting invoices
Once a Production CSID is linked, each e-invoice can be submitted to ZATCA (the "Submit to ZATCA" action on the documents list, or automatically as part of your invoice flow):
- Standard / B2B (Clearance). The invoice is cleared by ZATCA before it is shared with the buyer. If ZATCA rejects it, the invoice is not legally issuable — fix and resubmit. A cleared invoice gets ZATCA's returned cleared document.
- Simplified / B2C (Reporting). The point-of-sale receipt is issued immediately with its QR; the invoice is reported to ZATCA within 24 hours. Reporting never blocks the sale.
Submission is idempotent and retry-safe: a transient network failure can be retried without breaking the hash chain (ZATCA de-duplicates on the invoice UUID), and an already-cleared/reported invoice is never re-sent.
Invoice statuses
GeneratedLocal / UblGenerated — Phase-1 QR + UBL produced. Signed — XAdES-signed with your certificate. Submitting — in flight to ZATCA. Cleared — B2B accepted by ZATCA (the legal document). Reported — B2C reported. ClearanceRejected / ReportingRejected — ZATCA rejected it (see the message). SubmissionFailed — transient/blocked (e.g. not armed, wrong environment) — safe to retry once resolved.
A missing e-invoice: the automatic catch-up
An e-invoice is created the instant an invoice is issued. If that step is interrupted — the server restarts at the wrong moment, or e-invoice generation hits an error — the invoice is issued but has no e-invoice. A background catch-up runs every few minutes and creates the missing ones, with the same rules as the normal path:
- Only issued invoices. Drafts and voided invoices are never touched.
- Only invoices issued while e-invoicing was on (see Enabling the module).
- It waits 10 minutes. An invoice is picked up only once it has been unchanged for 10 minutes, so the normal path always gets its chance first. Either way an invoice gets exactly one e-invoice.
- It never disturbs the invoice chain. The catch-up creates the Phase-1 document (QR and snapshot) only. The invoice counter and previous-invoice hash are assigned later, when the document is signed or submitted, and a late document simply takes the next position at the end of the chain. It still carries its own issue date.
- More than 20 at once is held for a person. That many missing e-invoices means something went wrong on a larger scale, so nothing is generated for the company. The platform operator is notified (once a day, with the count and the oldest invoice number) and can release one catch-up run of up to 200 invoices, oldest first, once the list is confirmed to be right. Your administrators see the same notice.
- A bad invoice never blocks the others. If generating one invoice fails, it is recorded in the documents table as Failed with the error, and the catch-up moves on to the next. It is also listed in the Pre-submission check with Generate again: once the cause is fixed, one click creates the e-invoice.
When the catch-up creates documents, your administrators get a notification with the count and the oldest invoice number — at most once a day, and always after a run the platform operator released. Simplified (B2C) documents among them are late for the 24-hour reporting window, so review E-Invoicing (ZATCA) → Documents and submit them.
Operator: the release is POST /api/v1/einvoicing-zatca/admin/companies/{companyId}/catch-up
(SuperAdmin only). It lets the next maintenance run go past the 20-invoice hold once, then the hold applies again; a
release that is not used within 24 hours expires. A company whose e-invoicing is off, or that has no catch-up
boundary, cannot be released (404), and every release is written to that company's audit log.
Operator notes
- One company, one CSID. Each company onboards separately with its own OTP. Switch into the company first, then onboard.
- Secrets stay encrypted. The private key, CSID and secret are encrypted at rest and never displayed, logged, or returned by the API — only the non-secret thumbprint/expiry are shown.
- Certificate renewal. CSIDs expire (≈1 year). The onboarding page shows the expiry; re-onboard before it lapses.
- Credit notes are typed and submitted automatically as the ZATCA credit-note document.
- If generating an e-invoice is declined, the message now says why. Generation can be declined for four different reasons — the invoice is still a draft, the invoice has been voided, e-invoicing is not enabled for the company, or the invoice no longer exists. These used to be reported with a single message naming only the last two, so an operator whose invoice was simply not issued yet went looking for a settings problem that did not exist. Each reason is now named exactly.
- An e-invoice adopts its invoice's real number when the invoice is issued. A document
created for an invoice that was still a draft carries that invoice's provisional
DRFT-…reference. Once the invoice is issued and takes its real number, the e-invoice picks the real number up — as long as nothing has been filed for it yet. If the document has already been signed, chained or submitted, it is never rewritten: a filed record is corrected by a credit note, never by editing it.GET /api/v1/einvoicing-zatca/documents/stale-referenceslists any document whose stored number no longer matches its invoice, and says for each whether it can still be repaired automatically. Check that this list is empty before your first live submission. The e-invoicing page shows the same check as a Pre-submission check panel, with a one-click Adopt the invoice number action for the documents that can still take it — the filed ones are listed without an action, because they are corrected by a credit note. The same list also shows any e-invoice whose generation failed for a reason that may have passed (for example the catch-up's record of an error), with a Generate again action; a refusal under a ZATCA rule (BR-KSA) or a voided invoice's record is never offered, because generating again cannot change it.