لم تُترجم هذه الصفحة بعد
تعرض هذه الصفحة النص الإنجليزي حتى تصدر ترجمتها.
Public tools¶
A small set of endpoints under /api/public/ that need no API key and no account. The five tools read nothing from your organisation and store nothing. The shared-link endpoints are the exception: each one reads the single invoice or draft its token points to.
Base URL¶
The public tools are not on the Integration API base URL. The web app calls them through the gateway's v1-3 route:
On the sandbox, GET …/v1/api/public/health/ answers 404 and GET …/v1-3/api/public/health/ answers 200. Checked 2026-09-29.
What is there¶
| Method and path | What it does | Body |
|---|---|---|
POST /api/public/scan-qr-code/ |
Decode the TLV string of a ZATCA invoice QR code | JSON |
POST /api/public/validate-zatca/ |
Run every ZATCA rule against an invoice and report each result | JSON |
POST /api/public/calculate-vat/ |
Work out net, VAT and gross totals per VAT category | JSON |
POST /api/public/parse-invoice/ |
Read the QR tags and UBL data out of a PDF (including PDF/A-3) or a UBL XML file | multipart, field file |
POST /api/public/export/ |
Render an invoice as JSON, CSV, UBL XML or PDF, if it passes every ZATCA rule | JSON |
GET /api/public/health/ |
Liveness | none |
GET /api/public/shared/{token}/ |
View a shared invoice or draft | none |
GET /api/public/shared/{token}/pdf/ |
Download the PDF of a shared invoice or draft | none |
POST /api/public/shared/{token}/otp/ |
Email a 6-digit approval code to the draft's recipient | none |
POST /api/public/shared/{token}/approve/ |
Approve a shared draft | JSON |
Every JSON body is strict: an unknown field is refused with 400. Every successful tool response carries "branding": "Powered by Clix" and an X-Powered-By: Powered by Clix header.
Calculate VAT¶
The body is a list of invoice lines in the same vocabulary as the authenticated invoice endpoints. vat_category_code is one of S, Z, E or O and defaults to S. invoiced_quantity defaults to 1.
curl -s -X POST "$CLIX_PUBLIC/api/public/calculate-vat/" \
-H "Content-Type: application/json" \
-d '{
"lines": [
{ "item_name": "Consulting", "invoiced_quantity": "1",
"item_net_price": "1000.00", "vat_rate": "15", "vat_category_code": "S" }
],
"document_level_allowances": []
}'
200, verified against the sandbox 2026-09-29:
{
"invoice_total_line_net_amount": "1000.00",
"invoice_total_document_level_allowance_amount": "0.00",
"invoice_total_amount_without_vat": "1000.00",
"invoice_total_vat_amount": "150.00",
"invoice_gross_total": "1150.00",
"vat_breakdown": [
{ "vat_category_code": "S", "vat_rate": "15",
"vat_category_taxable_amount": "1000.00", "vat_category_tax_amount": "150.00",
"vat_exemption_reason_code": null, "vat_exemption_reason_text": null }
],
"branding": "Powered by Clix"
}
A document-level allowance must name a (vat_category_code, vat_rate) pair that a line already uses. When allowance_amount is above zero and both reason_code and reason are left out, the calculator uses code 95 (Discount).
Decode a QR code¶
Send the base64 string a QR reader returns from the code on a ZATCA invoice.
curl -s -X POST "$CLIX_PUBLIC/api/public/scan-qr-code/" \
-H "Content-Type: application/json" \
-d '{ "qr_base64": "AQ9UZXN0IFNlbGxlciBMTEMCDzMwMDAwMDAwMDAwMDAwMwMUMjAyNi0wMS0wMVQxMjowMDowMFoEBzExNTAuMDAFBjE1MC4wMA==" }'
200, verified against the sandbox 2026-09-29 with that made-up QR:
{
"tags": {
"seller_name": "Test Seller LLC",
"vat_registration_number": "300000000000003",
"timestamp": "2026-01-01T12:00:00Z",
"invoice_total_with_vat": "1150.00",
"vat_amount": "150.00"
},
"branding": "Powered by Clix"
}
A QR code from a simplified invoice also carries invoice_hash, invoice_hash_signature, egs_public_key and zatca_ca_signature_of_egs_csr. The last three come back as base64. A tag number the decoder does not know comes back as unknown_tag_<n>.
Validate, export and parse¶
validate-zatca and export take a full invoice: ref_num, issue_date, seller and lines are required, and the rest have defaults. export wraps it as {"invoice": {…}, "format": "pdf"}. format is json (the default), csv, ubl-xml or pdf.
validate-zatcaanswers200withtotal_rules,passed,failed,not_applicableand oneresultsrow per rule:rule_id,status(passed,failedornot_applicable) andmessage.exportruns the same rules first. If any rule fails it answers400withdetail,failed_countandviolations, and renders nothing. The CSV, XML and PDF formats come back as a file download. The UBL XML is unsigned.parse-invoiceanswers200withqr_found,tags,invoiceandwarning. A scanned, image-only PDF givesqr_found: false; decode its QR code withscan-qr-codeinstead.
The full request schema, with an example for each endpoint, is in the Clix OpenAPI document at /api/openapi.json, under the Public API tag.
Errors¶
| Status | When | Body |
|---|---|---|
400 |
A field is missing, has the wrong type, or is unknown | {"message": ["qr_base64: Field required"]} |
400 |
The tool refused the input | {"detail": "qr_base64 is not valid base64: Only base64 data is allowed"} |
400 |
An upload is over the size cap | {"detail": "File too large (… bytes). Maximum accepted: 5242880 bytes."} |
404 |
A share token is unknown, expired or revoked | {"message": "This share link is no longer available."} |
429 |
A rate limit is reached | see Limits |
503 |
The rate limiter could not answer | {"detail": "Rate-limit service temporarily unavailable."} |
Schema errors use message with an array, one entry per field, as path: problem. Errors the tool raises itself use detail with a string. The first two rows and the 404 were checked against the sandbox 2026-09-29.
Limits¶
Separate from your API key's limits:
| Caller | Allowance |
|---|---|
| Anonymous | 10 a day, per IP address |
| Signed in, Free plan or a card trial | 100 a month, per organisation |
| Signed in, paid plan | Not metered |
"Signed in" means a Clix web session, sent as the session cookie or as a Bearer token. A clix_… API key is not a session: a call that carries one counts as anonymous.
The daily allowance covers every path under /api/public/, the shared-link endpoints included. A limit returns 429:
{ "detail": "Anonymous daily limit reached (11/10). Sign in for a higher limit, or wait until tomorrow.", "window": "daily", "limit": 10, "current": 11 }
window is daily, monthly or hourly. A daily 429 carries Retry-After: 86400, an hourly one Retry-After: 3600, and a monthly one none. A 503 means the limiter itself could not answer; retry shortly.
Uploads to parse-invoice are capped at 5 MiB.
Shared invoice links¶
A seller makes a share link in the app. The public view of it is GET /api/public/shared/{token}/. A link expires 30 days after it is made. Each link answers at most 60 requests an hour, counted across all four shared-link endpoints; the next one gets 429 with window: "hourly".
A shared draft can be approved without an account. The recipient calls POST /api/public/shared/{token}/otp/ to get a 6-digit code by email, valid for 10 minutes. Asking again within those 10 minutes sends nothing new. Then they call POST /api/public/shared/{token}/approve/ with {"name": "…", "code": "123456"}. Approving an already approved draft returns the first approval unchanged.
Anyone with the link can view that one invoice. Treat a share link as a bearer credential for a single document; do not put it somewhere indexable.
When to use these¶
Use the public tools for one-off checks and for features you offer your own users: scanning a supplier QR code in your app, validating a file before you submit it.
Do not build your integration on them. They are rate-limited for anonymous use, carry no organisation context, and cannot issue anything. Issuing invoices goes through the authenticated API; see Quickstart.