Skip to content

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.

Three more lookups give the ids a client or a note needs: see Business types, industries and reasons.

Every one returns an object, not an array

The response is a map, not a list. Seven of the eight are keyed by the enum member name:

{ "IN_CASH": { "code": 10, "description": "In cash", "description_ar": "نقداً" },
  "CHEQUE":  { "code": 20, "description": "Cheque",  "description_ar": "شيك" } }
/api/invoices/vat/ is keyed by the category code instead (S, Z, E, O). Use 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/ 41 units of measure
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. Without a parameter it returns all four; ?vat_category_code=S returns one:

{ "S": { "vat_rates": [5.0, 15.0], "reasons": [], "is_free_text": false } }

Standard rated allows 5.0 and 15.0. Zero, exempt and not-subject allow only 0.0, and reasons lists their exemption codes as {"code", "text", "text_ar"}. On an invoice line in any category other than S, vat_exemption_reason_code and vat_exemption_reason_text are required. 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

Each entry is {"category", "code", "description_en", "description_ar"}: note description_en, not description as on the other lists. An unknown category returns an empty object, not an error.

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. On the invoice payload, type_code is an integer too, but payment_means_type_code is a string: send "10", not 10.

Units of measure

41 units, among them PCE piece · KGM kilogram · LTR litre · MTK square metre · TNE tonne · CTN carton · BOX · PALLET. The list is Clix's own; it does not carry every UN/ECE Recommendation 20 code.

Two codes appear under two names each: LTR (LITRE, LITRE_PER_LITRE) and STN (TON_UK, SHORT_TON). Key your cache on code and you get one entry for each.

Arabic labels

Every list carries Arabic except one:

  • Charge codes (UNTDID 7161) have no Arabic field at all.
  • Allowance codes (UNTDID 5189) all carry description_ar today, but the schema allows null.

Fall back to the English description when the Arabic is absent.

Business types, industries and reasons

Creating a client needs a business type and an industry. A credit or debit note may name a reason. Each list has its own endpoint. Unlike the eight above, each returns an object that holds an array.

Field Where the value comes from
business_type on client_data uuid from GET /api/business-types/
industry on client_data id from GET /api/industries/
reason_id on a credit or debit note uuid from GET /api/sellers/reasons/
additional_buyer_id[].type_id on client_data No endpoint. One of TIN, CRN, MOM, MLS, 700, SAG, NAT, GCC, IQA, PAS, OTH.

Business types

GET /api/business-types/. Any valid key; no role needed.

{ "business_types": [
    { "uuid": "…", "type": "B2B",  "type_ar": "شركة إلى شركة",   "code": "1000" },
    { "uuid": "…", "type": "B2C",  "type_ar": "شركة إلى مستهلك", "code": "0100" },
    { "uuid": "…", "type": "Both", "type_ar": "كلاهما",          "code": "1100" } ] }

Send the uuid, not the code. An empty list also carries "message": "No business types found.".

Industries

GET /api/industries/. Any valid key; no role needed. Only active industries are listed.

{ "industries": [
    { "id": "…", "name": "…", "name_ar": "…", "description": "…", "is_active": true } ] }

The UUID is in id here, not uuid.

Reasons

GET /api/sellers/reasons/. Any key; ViewInvoice is enough. Lists your own organisation's reasons, 10 to a page.

{ "reasons": [
    { "uuid": "…", "name": "…", "name_ar": "…",
      "description": "…", "description_ar": "…",
      "reason_type": { "code": 381, "value": "CREDIT_NOTE" },
      "is_active": true, "created_at": "…", "updated_at": "…", "created_by": "…" } ],
  "total": 1, "num_pages": 1, "current_page": 1 }

Inactive reasons are listed too. A note accepts only an active one, so filter: ?is_active=true&reason_type=381 for credit notes, 383 for debit notes. Other parameters: page, page_size, search_text, name, start_date, end_date, and sort (A-Z, the default, Z-A, latest, oldest).

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.