When an invoice reaches the Zakat, Tax and Customs Authority and is rejected for a missing field or a badly formatted VAT number, the problem is not the submit button. It started earlier, in the data that left the ERP, the point of sale or the billing platform. That is why preparing invoice data before connecting to ZATCA is not an isolated technical task but an operational decision: it determines whether the business can issue documents that are accepted, auditable and on time.
This article is about the data itself: where each field comes from, who owns it, and how it is normalised and validated before it reaches the Authority. For the integration steps from inventory to go-live, see connecting an ERP to ZATCA.
Integration does not start at the API
It is easy to treat integration as an API project. But an API carries what it receives; it does not fix conflicting customer names, inconsistent unit codes or a credit note missing its reference to the original invoice. Good preparation starts by understanding where every value on the invoice comes from, who owns it and when it becomes final.
In a landscape with an ERP, an online store and points of sale, the same customer may have three definitions and tax may be calculated along three paths. The integration should not inherit that complexity as it is. What you need is a normalisation layer that lets internal systems carry on as they are, while invoices head to the Authority in one stable, monitored representation: one JSON shape, not UBL inside every source system.
Start with a data map
Before writing any mapping, build a clear map between source-system fields and the fields the e-invoice requires. Don't stop at field names: document each field's meaning, rules and owner. A field like customer_id may be a useful internal identifier, but it is not a valid VAT registration number to submit.
The map should cover:
- seller and buyer details, registration numbers and the business's national address;
- issue date and supply date, currency, and order or contract references;
- lines, discounts and charges, taxes and totals;
- how later documents relate to the original invoice, as with credit and debit notes.
Separate mandatory fields from conditional ones. Some data is not required every time but becomes mandatory on a standard tax invoice rather than a simplified one, on a credit note, on an export or on a transaction with special tax treatment.
Name an owner for every field
Ownership stops guesswork:
| Data | Owner |
|---|---|
| Tax rules and exemptions | Finance and tax |
| Customer and supplier data | The data team or the CRM |
| Integration contracts and submission behaviour | IT |
| Day-to-day exception handling | Operations |
If a customer's address is missing, the integration layer should not invent one. It should stop the document before submission and send the reason to whoever owns the fix. With those boundaries a rejection becomes part of the workflow, not a crisis passed between teams with no owner.
Normalise values before you total them
A correct invoice is not just a set of complete fields; its arithmetic has to agree too. That starts with consistent currency, decimal precision, units of measure, tax codes and rounding rules. A small rounding difference between line level and invoice level can produce an amount the Authority will not accept.
Calculate each line's net, then the discount, then the tax, then the total, in the order your business rules define. Don't mix tax calculated at the point of sale with tax recalculated in the integration layer without a stated rule for differences. If the source system is the financial reference, document that and check its output is consistent. If the integration layer calculates tax, define when its result is the reference and when it is only a check.
Complexity tends to appear in invoice-level discounts, tax-inclusive prices, exemptions, and credit and debit notes. Generic handling does not work there: each scenario needs test cases drawn from your actual transactions, not just a simple sales invoice.
Validate before sealing and submission
Early validation costs less than fixing a document that has already landed on the rejection list. Design validation in layers:
- Presence: is the field there at all?
- Format: does the VAT number have the right length and structure?
- Arithmetic: do the totals equal the sum of the lines after discount and tax?
- Business rules: does the tax code agree with the buyer type and the exemption status?
- The Authority's requirements: does the document meet its published rules?
A VAT number may pass the presence check because it is not empty, then fail on format or on its fit with the buyer type. Totals may be arithmetically right yet unacceptable because the currency or tax code does not fit the scenario.
The validation layer should return actionable errors, not a vague "document failed". A useful message ties the problem to a specific field and a specific rule, such as: "The tax code on line 3 does not match the stated exemption." We have collected the most common Fatoora rejection errors and how to prevent them for you to build your rules on.
Separate a document's identity from its status with the Authority
The internal invoice number is essential but not enough to manage the lifecycle. The business needs a stable document identifier in the integration layer, an identifier for the source order or process, and clear references for amending documents. A credit note is not a new invoice; it is a document tied to an earlier invoice, reversing part or all of its effect.
Keep the status at every stage: received, validating, rejected before submission, queued, accepted, rejected by the Authority, or needs attention. Don't treat "submitted" as "accepted"; the regulatory value is achieved on successful clearance or reporting, depending on the document type. These statuses are what let finance close with confidence, give IT visibility of delays and give the auditor a complete record that does not rely on screenshots.
Each legal entity is tied to its own identity and certificate, so no entity's document is sealed with another entity's certificate. More on that in registering the electronic seal certificate and invoicing across several legal entities.
Classify errors before you retry
Retrying is not always right. If the failure is missing data, repeating it solves nothing. If it is a timeout or an outage at the Authority, it can be retried under a defined policy, with a key that prevents a second document. The system should tell three kinds apart:
- Fixable at source: goes back to the field's owner.
- Transient: retried automatically, within limits.
- Needs human review: escalated with a clear reason.
Data quality measures
Watch specific measures: first-time acceptance rate, the most frequent rejection reasons, processing time, the number of stuck documents, certificate expiry dates, and differences between entities or sales channels. A rise in rejections on one channel after a point-of-sale update signals a change in the data, not a connection problem. These measures turn compliance from reaction into a process you can manage.
How this looks in alFatora
With alFatora Connect your system sends one JSON shape, and we carry mapping and validation before sealing and submission:
- Errors tied to the field: every validation error carries the field's path in the document and the code of the rule it broke, so its owner can fix it.
- A failed validation consumes nothing: a document that fails the pre-check takes no number and no counter value, and its idempotency key stays valid for a retry after the fix.
- A status for every document by webhook, including awaiting clearance when the Authority's service is slow.
- The sandbox is free and unlimited, so you can test your real scenarios before production at no cost.
If you are still weighing building this layer yourself against relying on a platform, start with build or buy your ZATCA integration?.
A successful integration is not measured by the number of requests sent, but by the number of documents accepted, the ability to explain every decision and your teams' ability to handle exceptions without disrupting the business. Start with an accurate data map, test the scenarios that actually cause you trouble, and make every rejection an actionable signal.
Frequently asked questions
Why is an invoice rejected when the integration works?
Because an API carries what it receives. Most rejections start in the source data: a badly formatted VAT number, a missing conditional field, or a rounding difference between lines and total.
Who should fix a missing field on an invoice?
The field’s owner: finance for tax rules, the data team for customer details, IT for submission behaviour. The integration layer should never invent a value.
Should every failed invoice be resubmitted?
No. A failure caused by missing data goes back to its source, a transient one such as a timeout is retried with a key that prevents duplicates, and anything else is escalated for human review.
Was this useful? Review alFatora on Google.