Skip to content

Postman collection

Two ways in:

  • The ready collection holds every invoice scenario, with request bodies and test scripts that carry ids from one request to the next. Use it to learn the API and to try each document type on the sandbox.
  • The live spec gives you every endpoint and its schema, and nothing more. Use it to stay current: it is generated from the running code.

Sandbox only: TLS ends outside the Kingdom

This sandbox terminates TLS on a server outside Saudi Arabia. Send only test data. Production Clix keeps your data in the Kingdom.

What you need first

  • A sandbox account with API access. API access comes with the Business plan; see Plans. Sign up at sandbox.app.goclix.ai.
  • An API key. An Admin creates it under API Keys and sees it once. See API keys and Authentication. To try a few calls without an account, use the shared demo key on Try the API. The collection is for your own account: every id in it comes from there.
  • An Active ZATCA connection (a device whose device_status is pcsid_generated). See Connect to ZATCA.
  • For credit and debit notes: one active credit-note reason and one active debit-note reason under Invoices → Adjust Reasons, each with a description. See Manage the list of reasons.

Import the ready collection

Download both files:

Then:

  1. In Postman, Import → Files, and pick both.
  2. Select the Clix sandbox environment, top right.
  3. Paste your clix_… key into apiKey. It is marked secret, so Postman does not show or share it.
  4. Send 0 Check → Usage this month. A 200 means the key, the plan and the organisation work.

The collection sends Authorization: Bearer {{apiKey}} on every request. Leave each request on "Inherit auth from parent".

Folder What it does
0 Check Confirms the key, and finds your Active device
1 Reference data The eight code lists, business types, industries and your note reasons
2 Clients and items Creates a Saudi B2B client, a foreign client for export, and a catalogue item; lists clients and items
3 Preview One preview per scenario. It validates and calculates, and stores nothing
4 Issue One create per scenario. Each returns 202 and a location to poll
5 Follow up Polls the invoice and the note, reads and adds comments, downloads the PDF, retries a ZATCA submission

Every request carries a description: what it does, when to use it, and a link to the page here that explains it.

Fill your variables

Send folders 0, 1 and 2 in order and the test scripts fill most variables for you. A script fills a variable only while it is empty, so a value you paste yourself stays.

Variable Where it comes from Filled by
baseUrl https://sandbox.api.goclix.ai/v1. In production, the host that comes with your production key the environment file
apiKey API Keys in the app, shown once you
deviceId, sellerId device_id and seller_id of the first Active device in GET /api/devices/ List devices
businessTypeId uuid of B2B (code 1000) in GET /api/business-types/ Business types
industryId the first id in GET /api/industries/ Industries
reasonId, debitReasonId the first active 381 and 383 reason in GET /api/sellers/reasons/ Credit and debit note reasons
buyerId location of POST /api/onboarding/organizations/clients/, or a uuid from GET /api/buyers/ Create a client
exportBuyerId location of the foreign client you create Create a foreign client
itemId location of POST /api/items/, or a uuid from GET /api/items/ Create an item
invoiceId location of the last tax invoice you issued each tax invoice in 4 Issue
invoiceLineId lines[0].invoice_line_id of that invoice Get the invoice (poll)
noteId location of the last credit or debit note the two notes in 4 Issue

Two more are set before every request and need nothing from you: today, the date in Riyadh, for supply_date; and testVatNumber, a made-up 15-digit VAT number for the test client, because Clix refuses a second client with the same VAT number.

Each body's note reads Postman test {{$timestamp}}. The timestamp makes every send different, so the five-minute duplicate guard does not refuse a repeat.

Try every scenario

Preview first in 3 Preview, then issue the same scenario in 4 Issue, then poll it with 5 Follow up → Get the invoice (poll). A preview answers 201 and stores nothing; a create answers 202 with a location.

Scenario Request in 4 Issue What sets it apart Final status
Standard tax invoice, B2B Standard tax invoice (B2B) transaction_code 0100000, buyer = buyerId CLEARED
Simplified tax invoice, B2C Simplified tax invoice (B2C) transaction_code 0200000, no buyer REPORTED
Export of services Export of services (zero-rated, USD) transaction_code 0100100, buyer = exportBuyerId, every line Z with VATEX-SA-33, USD at 3.75 CLEARED
Foreign currency Foreign-currency invoice (USD) currency USD, exchange_rate 3.75, lines at 15% CLEARED
From the catalogue Standard invoice from the catalogue (lines) lines [{"item_id": "{{itemId}}", "quantity": 1}], form_lines [] CLEARED
Credit note Credit note against invoiceId type_code 381, reason_id, original_invoice_reference ["{{invoiceId}}"], each line's invoice_line_id as the original: CLEARED
Debit note Debit note against invoiceId type_code 383, the same three fields as the original: CLEARED

For an export of goods, change the line to VATEX-SA-32 Export of goods and keep the export evidence for 90 days.

Credit and debit notes need an accepted original. Issue the standard invoice, then poll it with Get the invoice (poll) until it is final; that poll also saves invoiceLineId. The note keeps the original's buyer, device, currency and transaction code. Its id goes to noteId, so a debit note after a credit note still points at the original.

A note answers Why
202 Accepted. Poll it with Get the note (poll)
409 Wait until ZATCA answers for the original invoice The original has no ZATCA answer yet. Poll the original, then send the note again
400 The original invoice was rejected by ZATCA The original is NOT_CLEARED or NOT_REPORTED. A rejected invoice cannot be corrected; issue a new one
400 The note's currency (SAR) must match the original invoice's currency (USD). invoiceId holds the USD invoice. Issue the standard invoice again, or change the note's currency and exchange_rate

The sandbox rejects your own VAT number

ZATCA's sandbox signs with a shared test certificate. An invoice from your own VAT number comes back NOT_CLEARED or NOT_REPORTED with certificate-permissions. That is the sandbox, not your body; see ZATCA rejections. The note requests then answer 400 The original invoice was rejected by ZATCA. Every preview, every create and every status poll still works. On production, from your own Active device, the same bodies are cleared or reported.

Follow up

Request What to expect
Get the invoice (poll) 200. Send it every second or two until zatca_response_status is CLEARED, REPORTED, NOT_CLEARED or NOT_REPORTED. A rejection's reasons are in zatca_response_data.validationResults.errorMessages[]
Comments on the invoice, Add a comment to the invoice 200 and 201. A comment is {"title", "details"} for your own records. It is not a credit or debit note and never reaches ZATCA
Download the PDF/A-3 The PDF with the XML inside, once the invoice is final. Before that, and for a rejected invoice, 404 Invoice PDF not found. Use Send and Download
Retry ZATCA submission 202 and a location, for an invoice that has no ZATCA answer yet. 409 means there is nothing to retry: ZATCA has answered, rejection included, or a retry is in flight. Do not retry a 409

Or import the live spec

The ready collection is a snapshot. The spec is the authority, and it changes when the API does.

  1. Fetch the spec:
    GET https://sandbox.api.goclix.ai/v1/api/integrations/openapi.json
    
    It is public; no key needed.
  2. In Postman: Import → Link and paste that URL, or import the saved file.
  3. Postman builds a collection with every endpoint, its parameters and its schemas.

The spec lists the 24 paths an API key can call. Its servers entry is the gateway URL including /v1, and it declares one security scheme, ApiKeyBearer: HTTP Bearer, which takes a clix_… key.

The public tools are not in this spec. They need no key and sit on a different gateway route.

Re-import when you want to pick up changes. The URL is the source of truth.

Set up the environment

Create a Postman environment with two variables, or reuse the Clix sandbox environment above:

Variable Value
baseUrl Your gateway host including /v1
apiKey clix_A7bQ2xK_…

Set apiKey as a secret variable so it is not stored in plain text or shared with the collection.

Set up authorisation once

On the collection, set Authorization to:

  • Type: Bearer Token
  • Token: {{apiKey}}

Leave every request as "Inherit auth from parent". That way the key lives in one place and cannot be forgotten on a new request.

Check it works

Send GET {{baseUrl}}/api/usage/current/. A 200 with your plan and invoice counters means the environment, the key and the plan are all correct.

A sensible request order

  1. GET /api/usage/current/: confirm the key
  2. GET /api/invoices/vat-categories/: confirm reference data
  3. GET /api/devices/: confirm a signing device exists
  4. POST /api/invoices/preview/: check a payload without creating anything
  5. POST /api/invoices/: create, then poll the location it returns

Two warnings for Postman specifically

Do not use the Runner to retry invoice creation

Identical invoice payloads are rejected for five minutes, and a retry after a timeout can create a duplicate you cannot delete. Poll the location before resending. See Quickstart.

Do not export an environment containing a live key

Postman exports include variable values unless they are marked secret. Share collections, never environments.