ZATCA integration¶
Saudi Arabia's Zakat, Tax and Customs Authority (ZATCA) requires B2B and B2C invoices to be electronically cleared or reported via the Fatoora platform (ZATCA Phase 2). Invora handles the full lifecycle: CSR generation, compliance verification, production onboarding, invoice signing, QR-code embedding, and clearance/reporting submission.
This guide covers the tenant-side flow: onboarding your organization and tracking submissions. Enabling the ZATCA regulation and setting its base configuration for a tenant is an admin operation handled by the Invora team.
Base URL & auth¶
| Environment | Base URL |
|---|---|
| Production | https://gateway.invora.app |
| Staging | https://stg-gateway.invora.app |
TOKEN=$(curl -s -X POST https://auth.invora.app/oauth/v2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET" \
--data-urlencode "scope=openid urn:zitadel:iam:org:project:id:372376660185448530:aud urn:zitadel:iam:user:resourceowner" \
| jq -r '.access_token')
ZATCA environments¶
| Environment enum | Purpose | Validation |
|---|---|---|
ZATCA_ENVIRONMENT_PHASE2_SANDBOX |
Development | Simulated responses, no real validation |
ZATCA_ENVIRONMENT_PHASE2_SIMULATION |
Pre-production | Real ZATCA validation, no legal effect |
ZATCA_ENVIRONMENT_PHASE2_PRODUCTION |
Live compliance | Legally binding clearance/reporting |
Onboarding¶
Prerequisites¶
- Register at the ZATCA Fatoora portal and generate a one-time password (OTP).
- Configure your organization's tax identity (legal name, VAT number, address) via the Settings self-party — these must match ZATCA's records.
- Ensure ZATCA is enabled for your tenant (contact Invora support).
Start onboarding¶
InitiateOnboarding returns a server-sent stream of progress events — one per onboarding step.
curl -N -X POST https://gateway.invora.app/api/v1/regulations/zatca/onboarding \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "otp": "123456", "environment": "ZATCA_ENVIRONMENT_PHASE2_SIMULATION" }'
{"step": "ZATCA_ONBOARDING_STEP_CSR_GENERATION", "disposition": "ZATCA_DISPOSITION_ISSUED", "isFinal": false}
{"step": "ZATCA_ONBOARDING_STEP_COMPLIANCE_CSID", "disposition": "ZATCA_DISPOSITION_ISSUED", "isFinal": false}
{"step": "ZATCA_ONBOARDING_STEP_PRODUCTION_CSID", "disposition": "ZATCA_DISPOSITION_ISSUED", "isFinal": true}
The three steps run in sequence:
- CSR generation — Invora generates a Certificate Signing Request from your tax identity.
- Compliance CSID — ZATCA issues a compliance certificate; Invora submits a test invoice for validation.
- Production CSID — ZATCA issues the production certificate for live invoicing.
A failed step emits disposition: ZATCA_DISPOSITION_REJECTED (or _ERROR) with structured errors:
{
"step": "ZATCA_ONBOARDING_STEP_COMPLIANCE_CSID",
"disposition": "ZATCA_DISPOSITION_REJECTED",
"message": "Compliance check failed",
"errors": [
{ "code": "COMPLIANCE_CHECK_FAILED", "category": "COMPLIANCE", "message": "Seller address city is required" }
],
"isFinal": true
}
Poll progress¶
If the client disconnected mid-stream, poll the current state:
curl -X GET https://gateway.invora.app/api/v1/regulations/zatca/onboarding/progress \
-H "Authorization: Bearer $TOKEN"
{
"currentStep": "ZATCA_ONBOARDING_STEP_PRODUCTION_CSID",
"lastDisposition": "ZATCA_DISPOSITION_ISSUED",
"completed": true
}
Check integration status¶
curl -X GET https://gateway.invora.app/api/v1/regulations/zatca/integration-status \
-H "Authorization: Bearer $TOKEN"
{
"onboarded": true,
"environment": "ZATCA_ENVIRONMENT_PHASE2_SIMULATION",
"details": {
"complianceCsidExpiry": "2027-01-15T00:00:00Z",
"productionCsidExpiry": "2027-01-15T00:00:00Z",
"isCompliant": true
}
}
Submitting invoices¶
Once onboarded, ZATCA runs automatically when you freeze a document — whether through the Simple surface or the Documents API. On freeze, Invora validates against ZATCA rules, canonicalizes and signs the XML with your CSID, generates the TLV-encoded QR code, and submits for clearance (B2B) or reporting (B2C).
| Invoice | Model | Behavior |
|---|---|---|
| B2B (standard tax invoice) | Pre-clearance | Blocking — ZATCA must clear before the invoice is valid |
| B2C (simplified invoice) | Reporting | Async — reported after issuance |
The model is chosen automatically from the invoice content; no per-call configuration is needed.
Check a document's regulation status¶
curl -X GET https://gateway.invora.app/api/v2/regulations/documents/INV-2026-001 \
-H "Authorization: Bearer $TOKEN"
{
"items": [
{
"regulationId": "zatca",
"submissionStatus": "REGULATION_SUBMISSION_STATUS_ACCEPTED",
"artifacts": [ { "artifactId": "signed-xml" }, { "artifactId": "qr-code" } ]
}
]
}
This returns status and artifact descriptors (no bytes). For the typed ZATCA detail (clearance ID, invoice hash), read the regulation metadata on the document itself.
Download the signed XML or QR code¶
curl -X GET "https://gateway.invora.app/api/v2/regulations/zatca/documents/INV-2026-001/artifact?artifactId=signed-xml" \
-H "Authorization: Bearer $TOKEN"
{
"artifact": "PD94bWwgdmVyc2lvbj0i...",
"contentType": "application/xml",
"artifactHash": "9f2c1a44..."
}
artifact is base64-encoded. Use artifactId=qr-code for the QR image.
Retry a failed submission¶
curl -X POST https://gateway.invora.app/api/v2/regulations/zatca/submissions/INV-2026-001/retry \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{}'
Decode a QR code¶
Decode a ZATCA QR code from a printed or received invoice into structured TLV data (seller, VAT number, totals, signature):
curl -X POST https://gateway.invora.app/api/v1/regulations/zatca/explain-qr \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "input": ["AXZWYW..."] }'
Common issues¶
| Symptom | Cause | Fix |
|---|---|---|
| OTP expired | OTP is short-lived | Generate a fresh OTP from the Fatoora portal |
| Compliance CSID rejected | Seller details don't match ZATCA records | Correct the self-party legal name, VAT number, and address |
| Clearance rejected | Calculation errors or missing fields | Read errors/diagnostics, fix the document, retry |
| QR code invalid | Document altered after signing | Frozen documents are immutable — investigate corruption |
Related¶
- Simple invoicing — freeze an invoice and get the QR code.
- Documents API — regulation metadata, validation profiles, artifacts.
- Zero to invoice (demo) — an end-to-end ZATCA walkthrough.
- Error handling — status codes and structured errors.