Reference data¶
The codes ZATCA expects on an invoice. All are static enumerations served from memory, available to any authenticated key, and safe to cache for a long time.
Every one returns an object, not an array
The response is a map keyed by the enum member name:
{ "IN_CASH": { "code": 10, "description": "In cash", "description_ar": "نقداً" },
"CHEQUE": { "code": 20, "description": "Cheque", "description_ar": "شيك" } }
Object.values() if you want a list. Code that assumes an array will break on every one of these.
The endpoints¶
| Path | What it gives you |
|---|---|
GET /api/invoices/vat-categories/ |
S, Z, E, O |
GET /api/invoices/vat/ |
Valid rates and reasons per category |
GET /api/invoices/vat-exemption-reasons/ |
The 16 VATEX-SA-… codes |
GET /api/invoices/invoice-type-codes/ |
388, 381, 383, 386 |
GET /api/invoices/units-of-measure/ |
UN/ECE Rec 20 units |
GET /api/invoices/payment-means-types/ |
UN/CEFACT 4461 payment means |
GET /api/invoices/invoice-charges-codes/ |
UNTDID 7161 charge reasons |
GET /api/invoices/invoice-allowances-codes/ |
UNTDID 5189 allowance reasons |
VAT categories¶
| Code | Meaning |
|---|---|
S |
Standard rated |
Z |
Zero rated |
E |
Exempt from VAT |
O |
Not subject to VAT |
GET /api/invoices/vat/ tells you what each category permits:
Standard rated allows 5.0 and 15.0. Zero, exempt and not-subject are 0.0 and require a reason. Only O sets is_free_text: true, meaning you supply your own wording instead of picking a code.
An unknown category returns 400 {"message": "Invalid VAT Category"}.
Exemption reasons¶
Sixteen codes, filterable with ?category=Z:
VATEX-SA-32, -33, -34-1 … -34-5, -35, -36, -EDU, -HEA, -MLTRY, -29, -29-7, -30, -OOS
Use the one that matches the actual ground for exemption — it is a tax declaration, not a label.
Invoice type codes¶
| Code | Document |
|---|---|
388 |
Tax invoice |
381 |
Credit note |
383 |
Debit note |
386 |
Advance payment invoice |
Payment means¶
10 in cash · 20 cheque · 23 bank cheque · 25 certified cheque · 26 local cheque · 30 credit transfer · 31 debit transfer · 46 interbank debit transfer · 48 bank card · 49 direct debit · 54 credit card · 55 debit card
code is a number here
For payment means and invoice types the code field is an integer (10, 388), not a string. Compare accordingly.
Units of measure¶
PCE piece · CTN carton · PALLET · BOX · TNE tonne · MTK square metre · LTR litre, and others from UN/ECE Recommendation 20.
Arabic labels¶
Most entries carry description_ar. Two gaps to expect rather than treat as errors:
- Some allowance codes have
description_ar: null. - Charge codes (UNTDID 7161) have no Arabic at all.
Fall back to the English description when the Arabic is absent.
Other lookups¶
These are not on the integration surface but are public — no key required:
| Path | What |
|---|---|
GET /api/buyers/additional-buyer-id-types/ |
TIN, CRN, MOM, MLS, 700, SAG, NAT, GCC, IQA, PAS, OTH |
GET /api/sellers/additional-seller-id-types/ |
CRN, MOM, MLS, SAG, OTH |
GET /api/business-types/ |
1000 B2B, 0100 B2C, 1100 Both |
GET /api/industries/ |
Industry list |
GET /api/plans/ |
Plans and their features |
What does not exist¶
There are no endpoints for countries, cities, regions or currencies. city is free text on an address — validate it your side if you need to.
Caching¶
These change only when ZATCA changes a standard. Fetch at start-up, cache for a day, and you will never notice them.