انتقل إلى المحتوى

لم تُترجم هذه الصفحة بعد

تعرض هذه الصفحة النص الإنجليزي حتى تصدر ترجمتها.

Errors and rate limits

Two response shapes

Clix returns errors in one of two shapes depending on which layer refused the call. Handle both.

{ "message": "…" }
{ "detail": "…" }

Middleware — throttling and the fair-use guard — uses detail. Almost everything else uses message. Validation failures put an array in message.

Validation errors are 400, not 422

Clix replaces the framework default. A malformed body returns 400 with a list, one string per error, each prefixed with the path to the field. On the invoice endpoints the path starts with invoice_schema_data:

{ "message": ["invoice_schema_data.form_lines.0.item_name: Value error, Item name should not be less than 4 characters or more than 50 characters"] }

A business rule refused after validation returns 400 with one string in message. Some of those also carry rule_id, the ZATCA business rule that failed.

Status codes

Code Meaning Retry?
400 Validation failed, or a business rule refused it No — fix the request
401 Missing, invalid or revoked key No
403 Permission, inactive org, plan without API access, or fair use No
404 Not found, or not yours No
409 PATCH …/clearance-reporting/ on an invoice ZATCA has already answered, or one already queued No
429 Rate limited Yes, after Retry-After
5xx Server side Yes, with backoff

The 401s and 403s worth distinguishing

Body Cause
{"status":"false","message":"Missing or invalid token (use Authorization: Bearer <token> or accessToken cookie)"} No Authorization header
{"status":"false","message":"Invalid or revoked API key"} Unknown, revoked, or a deactivated organisation
{"status":"false","message":"API access is not included in your current plan."} Plan without API access — checked on every request
{"message":"Your account does not have permission to perform this action."} The key's role or scopes do not cover this endpoint. Some endpoints word it their own way: POST /api/invoices/ says User is not authorized to create invoices
{"message":"Your organization's account is inactive. Please contact support."} Organisation suspended
{"message":"Monthly invoice limit exceeded. You have used your grace period. Please upgrade your plan.", "current_usage": …, "limit": …, "plan_name": …, "upgrade_url": …} POST /api/invoices/ over the plan's invoice allowance plus its 10% grace. It has no code: branch on the status and limit. See Usage and quotas

Machine-readable codes

Most errors carry prose only. These are the stable code values:

code Status Meaning
fair_use_limit 403 The organisation hit its fair-use ceiling for the month
invalid_request 400 Malformed key-management request
not_found 404 No such key
invalid_role, invalid_scopes 400 Bad role or scope at key creation
key_id_unavailable 503 Could not allocate a key id — retry

Branch on code where one exists. Never parse the prose.

A person signed in to the app with several organisations can also get 409 org_context_required. An API key never does: it belongs to one organisation, and Clix ignores X-Active-Org on a key.

Rate limits

A fixed 60-second window. The window starts with your first request and resets 60 seconds later, whatever happened in between.

Caller Limit
An API key 200 requests a minute, for that key
A person signed in to the app 200 requests a minute, shared across all their organisations
No credentials 100 requests a minute per IP address

These are the defaults. Each limit and the window are environment settings that Clix can change, so read X-RateLimit-Limit rather than hard-coding 200.

Each API key has its own count. Two keys in one organisation do not share the 200, and a key that hits its limit does not slow down people using the app.

Responses carry the state:

X-RateLimit-Limit: 200
X-RateLimit-Remaining: 187
X-RateLimit-Reset: 60

On a successful response, X-RateLimit-Reset is always 60, the window length, not the time left. Count down with X-RateLimit-Remaining instead. On a 429, X-RateLimit-Reset and Retry-After both give the seconds actually left in the window.

The headers are missing on the documentation paths (/api/docs, /api/redocs, /api/integrations/docs, the OpenAPI files) and on /health/. Those paths are not rate limited.

The 201st request in a window gets a 429 with Retry-After:

{ "detail": "Rate limit exceeded. Please try again later.",
  "limit": 200, "window": 60, "retry_after": 12 }

Respect Retry-After. Read X-RateLimit-Remaining and slow down before you hit zero rather than after.

Fair use

API calls are not metered against a monthly API quota. What can refuse a call is the fair-use guard on writes (POST, PUT, PATCH and DELETE). A write counts when it succeeds with a 2xx. Clix switches the guard on for the platform or for one organisation; it is off by default.

When it is on, an organisation is refused once its counted writes this month pass its fair-use allowance plus 10%, rounded up. The allowance is set per plan, and support can override it for one organisation. No API response shows it; -1 means unlimited.

The month is the Asia/Riyadh calendar month. Once the guard is on, the refusal looks like this:

{ "code": "fair_use_limit",
  "detail": "Your organisation has reached its fair-use limit for this month. Viewing and downloading still work. Contact support to continue.",
  "current_usage": 1234, "limit": 1000 }

It is a 403, not a 429, and retrying will not clear it. Reads keep working. Talk to support.

Not counted: reads, failed requests, previews, PDF downloads, sample invoices, sign-in and subscription calls, search, and key management.

api_calls_used is always 0

GET /api/usage/current/ returns api_calls_used: 0 and api_calls_limit: 0 permanently, kept so older clients keep parsing. The meaningful figures there are the invoice counters. See Usage and quotas.

What to retry

Retry 429 after Retry-After, and 5xx with exponential backoff.

Do not retry 400, 401, 403, 404, 409.

A 202 from POST /api/invoices/ that says the submission to ZATCA failed is not an error to retry with a new create. The invoice exists; send PATCH /api/invoices/{invoice_id}/clearance-reporting/ for it.

Never blind-retry POST /api/invoices/. Identical payloads are refused for five minutes with a 400, and a timeout does not mean the invoice was not created. Poll first. A cleared invoice cannot be deleted — only credited.

flowchart TD
    A[Response] --> B{Status}
    B -->|429| C[Wait Retry-After, then retry]
    B -->|5xx| D[Retry with exponential backoff]
    B -->|400 401 403 404 409| E[Do not retry: fix the request, key, plan or id]
    B -->|timeout on POST /api/invoices/| F[Poll the invoice list first]
    F -->|exists| G[Use it]
    F -->|absent| H[Retry once]