Skip to Content

How to Connect Odoo to ZATCA Fatoora: A Step-by-Step Setup Guide

From company data to your first cleared invoice, including the step you cannot undo

Short answer: connecting Odoo to ZATCA Phase Two takes roughly a working day if your business data is clean. Almost all of that day goes into preparation and testing. The technical connection itself is a handful of clicks. This guide covers the full sequence, including the one step you cannot undo.

Before you start: the preparation that decides everything

Most failed integrations fail here, not at the technical step. Get these right first and the rest is straightforward.

What you needWhere it has to be exact
VAT registration number15 digits, starting and ending with 3, identical to your ZATCA registration
Commercial registration numberOn the company record in Odoo
National addressBuilding number, additional number, postal code, district, street, city, all of them
Fatoora portal accessCredentials for the account that will generate the OTP codes
Journal decisionWhich sales journals will issue e-invoices, and whether branches need separate ones

Spend an hour verifying this against the ZATCA portal rather than assuming. A single mismatched character between the two systems will block onboarding, and the error message will not tell you which field is wrong.

Saudi company data configuration in Odoo settings
Image needed: the company settings screen in Odoo with the VAT number, CR and national address fields filled in

Step 1: Company data

Open the company record and complete every field above. Two things people commonly miss:

  • The country must be Saudi Arabia on the company address, not left blank or inherited.
  • The company name used in the certificate has a 64-character ceiling. If your full Arabic legal name with the entity type spelled out exceeds it, onboarding will fail. Use a shortened form. It does not have to match your letterhead.

Step 2: Localization and the e-invoicing module

The Saudi localization has to be installed on the company, and the e-invoicing extension enabled on top of it. Installing the localization alone gives you the chart of accounts and the 15 percent VAT setup, but not the ZATCA connection. Both are needed.

If you are on a hosted Odoo instance, confirm the module is actually installed rather than assuming it ships by default, because it does not on every setup.

Step 3: Configure the journal

E-invoicing is configured per sales journal, not per company. Open the journal you will invoice from and you will find a ZATCA section on it.

Decide up front how many journals you need. A single-location business needs one. A business with branches that must be reported separately needs a journal per branch, and each will have its own certificate. Adding branches later is possible but means repeating the whole onboarding for each.

Step 4: Onboarding in simulation mode

This is the technical connection, and it runs in a test environment first. Never start in production.

  1. Generate the certificate request from Odoo. Odoo builds it from the company data you entered in Step 1. If it fails here, the cause is field length or missing data, so go back rather than retrying.
  2. Get an OTP from the Fatoora portal. The code is short-lived and single use, and each journal needs its own. Have Odoo open and ready before you request it.
  3. Enter the OTP in Odoo and complete the request. Odoo exchanges it for a certificate and stores it against the journal.

If onboarding fails partway, do not simply repeat the same steps. A half-configured journal can end up in an inconsistent state that makes retries behave unpredictably. Verify the journal state first.

ZATCA onboarding wizard and OTP entry on an Odoo sales journal
Image needed: the ZATCA onboarding wizard on the journal, at the OTP entry step

Step 5: Customer and tax setup

Two configuration points that decide whether your invoices route correctly:

Company Type on every customer

This single field determines the entire submission route. A customer marked Individual produces a simplified invoice sent for reporting. A customer marked Company produces a standard tax invoice sent for clearance. There is no separate setting on the invoice itself.

Audit your existing customer list before going live. Records imported from an older system almost always have this wrong, and every one of them will fail.

Taxes

The standard 15 percent VAT comes with the localization. If you use retention tax, it must be configured with a negative amount. Set positive, it produces validation failures that look unrelated to tax configuration and are very hard to trace.

Step 6: Test every document type

This is the step people rush, and it is the reason production goes wrong. In simulation mode, successfully submit all four:

  • A standard tax invoice to a customer marked as a Company
  • A simplified invoice to a customer marked as an Individual
  • A credit note referencing an original invoice
  • A debit note referencing an original invoice

If you sell exempt services in health or education, test those too, because the exemption codes require the buyer National ID, not just a VAT number, and you want to discover that now rather than in production.

Step 7: Switch to production

This step cannot be reversed. Once a journal moves from simulation to production there is no way back to testing on that journal. Before you click it, confirm all four document types passed in Step 6.

After switching, issue one real invoice and follow it end to end before resuming normal invoicing. Confirm it appears correctly on the ZATCA portal, not just that Odoo reports success.

The first two weeks

Watch these daily until you are confident:

  • Any invoice not in an accepted state. Resolve it the same day, because invoices are chained in order and one stuck document freezes every invoice created after it on that journal.
  • Rejections clustering at a particular time of day. Usually a timezone problem: Odoo stores UTC while ZATCA validates against Riyadh time, and evening postings can read as future-dated.
  • A single customer failing repeatedly. Check their Company Type before anything else.

Also set a calendar reminder well ahead of your certificate expiry date. Renewal must happen before it lapses. There is no grace period, and an expired certificate stops invoicing completely.

Successfully cleared ZATCA invoice status in Odoo
Image needed: an invoice in Odoo showing a successful ZATCA status after clearance

Point of sale

If you run POS, configure it before opening a session. Incomplete ZATCA settings can block sessions from opening entirely, and validation errors surface to the cashier mid-sale rather than in the back office. Brief your staff on what the dialog means, or your first busy day will produce support calls describing a broken till.

Frequently asked questions

Can I test in production first and switch back?

No. The move to production is one-way. Complete all testing in simulation.

Do branches need separate certificates?

Each invoicing unit needs its own registration and certificate. How many units you need depends on how your journals and points of sale are structured.

What if an invoice is wrong after it was submitted?

It cannot be reset to draft. That restriction is deliberate and matches the requirement that submitted documents are immutable. Issue a credit note referencing the original.

How long does the whole thing take?

With clean data, about a working day including testing. Most projects that run longer are fixing business data, not fighting the integration.

If something goes wrong

Errors during setup or submission are common and almost all of them are known. See our guide to Odoo ZATCA errors and their fixes for the full diagnostic walkthrough.

Back to the complete ZATCA requirements guide

This content is for general information and does not replace the official specifications published by the Zakat, Tax and Customs Authority. Screens and behaviour may differ across Odoo versions.

Odoo ZATCA E-Invoicing Errors: A Complete Troubleshooting Guide
Every failure point in the Saudi e-invoicing integration, and how to resolve each one