API Reference Overview
How the CloudPOS POS app talks to the CloudPOS backend - tenant discovery, base URL, headers, language, store scoping, response envelopes, pagination, error codes and the sign-in flow.
The CloudPOS POS app is a client of the CloudPOS backend (a Laravel API). Every screen in the app reads and writes data through the REST endpoints described in this section. This page explains the rules that apply to every call. The pages that follow describe each endpoint the app uses, grouped by area.
Use this section if you are:
- an owner or IT person checking why a screen shows what it shows,
- a developer building an integration against the same backend,
- the AI assistant answering "which API feeds this screen?".
In all examples, {{server}} means your tenant domain (for example https://<your-company>.cloudposai.com). Values in angle brackets such as <your-tenant-key> and <access-token> are placeholders. Never paste real keys or tokens into tickets or chat.
Base URL and tenant discovery
Each company (tenant) has its own backend domain. The app finds it from the API key the owner gives to staff.
- On first start the app shows the API Key screen.
- The app sends the key to the central discovery service:
POST https://cloudposai.com/api/v1/find-domain
X-Tenant-Key: <your-tenant-key>
Accept: application/json- The service answers with the tenant domain:
{
"status": 200,
"message": "Instance found",
"data": {
"domain": "https://<your-company>.cloudposai.com"
}
}- If the domain has no
http://orhttps://, the app addshttps://. Trailing slashes are removed. - The app saves the key (preference
api_key) and the domain (preferenceapp_url) and uses the domain as{{server}}for every later call.
Notes:
- Discovery succeeds only when the HTTP status is 200 and the body
statusis200. Any other answer shows an error on the API Key screen. - A 5xx answer or a non-JSON body is treated as "service unavailable, try again".
- Reset API Key on the Login screen clears the saved key so the API Key screen appears again.
- Builds can also be compiled with a fixed base URL (
--dart-define BASE_URL=...). The built-in default is used only until a key is discovered.
All endpoints in this reference are relative to {{server}}/api/v1/... unless stated otherwise.
Required headers
Header | When | Value |
|---|---|---|
| Every call to |
|
| Every call after sign-in |
|
| Calls with a JSON body |
|
| Recommended on every call |
|
| Localised calls |
|
Rules:
- The backend identifies the company from
X-Tenant. A missing or unknown key returns 404{"error":"Tenant not found"}from the tenant middleware (sign-in returns its own messages, see below). - Calls made before sign-in (find-domain, sign-in, forgot and reset password) carry the key but no token. Sign-in sends
X-Tenant, find-domain sendsX-Tenant-Key, and forgot/reset password send it in anapi_keyheader. - Some read-only endpoints do not require a token on the server (website settings, general settings, admin settings, master data values, languages, document configs, printer settings). The app still sends the token once signed in.
- The access token is a Laravel Sanctum token returned by sign-in. It stays valid until sign-out revokes it.
- Some ZATCA calls send their
idas a form field instead of JSON. They still send the sameX-TenantandAuthorizationheaders.
Example of a normal authenticated request:
GET {{server}}/api/v1/dashboard?store_id=2
X-Tenant: <your-tenant-key>
Authorization: Bearer <access-token>
Accept: application/json
Accept-Language: enLanguage (locale)
The app's language (English, العربية, മലയാളം, chosen in Settings > Language) is sent on localised requests twice, with the same value:
- as the query parameter
?locale=<code>, and - as the
Accept-Languageheader.
Two endpoints are always called without a locale:
Endpoint | Why |
|---|---|
| The app matches stored unit values (such as |
| Translated product names come from the |
Master data values also return a translations array and a meta.available_languages list, so the app can show labels such as بطاقة for CARD.
Store scoping (store_id)
A company can have several stores (branches). The demo company FUNZCART has متجر النجمة Store (id 2), funzcart2 (id 32) and Funzcart Al Zahara (id 35).
- After sign-in the user picks a store on Select Store. The app saves it as
active_store_id. - Most calls add
store_id=<active store id>as a query parameter (or in the body for POST calls). - A few list endpoints take arrays, for example
store_id[]=2ondaily-sales-close/list. - Switching store from the profile card (Active Store switcher) changes
active_store_id; later calls use the new id. - Reports that offer a Store filter send the picked store instead of the active one.
Response envelopes
Most endpoints use the standard envelope.
Success:
{
"status": "success",
"message": "Roles retrieved successfully",
"data": {}
}Failure:
{
"status": "failed",
"message": "Invalid tenant API key",
"data": []
}data on a failure holds the error details: a string, a list, or a validation map. The app shows message; if that is empty it shows data (when it is text) or the first validation message.
Other shapes you will meet:
Shape | Used by | Example |
|---|---|---|
| Day close ( |
|
|
| see above |
|
| see Settings page |
| Some report permission checks |
|
| Any authenticated route with a missing or revoked token | Laravel default |
Pagination
List endpoints accept page (1-based) and usually per_page. Two response shapes exist.
Laravel paginator inside `data` (orders, invoices, receipts, transactions, non-stock report and others):
{
"status": "success",
"message": "data found",
"data": {
"current_page": 1,
"data": [ "...rows..." ],
"first_page_url": "{{server}}/api/v1/non-stock-report?page=1",
"from": 1,
"last_page": 202,
"last_page_url": "{{server}}/api/v1/non-stock-report?page=202",
"links": [
{ "url": null, "label": "« Previous", "active": false },
{ "url": "{{server}}/api/v1/non-stock-report?page=1", "label": "1", "active": true }
],
"next_page_url": "{{server}}/api/v1/non-stock-report?page=2",
"path": "{{server}}/api/v1/non-stock-report",
"per_page": 20,
"prev_page_url": null,
"to": 20,
"total": 4024
}
}Separate `pagination` block (roles, consumed stocks report, some product and report endpoints):
{
"status": "success",
"message": "Consumed Stocks Report",
"data": {
"data": [ "...rows..." ],
"pagination": {
"current_page": 1,
"last_page": 5,
"per_page": 25,
"total": 125,
"next_page_url": "{{server}}/api/v1/consumed-stocks-report?page=2",
"prev_page_url": null
}
}
}Some newer endpoints use a meta block with the same keys. The app reads all three forms. When no pagination block is present, everything came on one page.
HTTP status codes and error bodies
Code | Meaning | Typical body |
|---|---|---|
200 / 201 | Success (201 for some creates, such as opening a shift) | success envelope |
400 | Required input missing (no |
|
401 | Wrong email or password, or token missing/revoked |
|
403 | Signed in but not allowed: wrong company, no POS role, missing permission |
|
404 | Unknown tenant key, unknown master data code, record not found |
|
409 | Conflict: a draft day close is already open for this store |
|
422 | Validation failed |
|
500 | Server error. Some older order and quotation calls also return validation errors with 500 |
|
Rate limit: the API allows 60 requests per minute per client. Bursts above that return 429 until the minute passes.
Sign-in flow in short
POST https://cloudposai.com/api/v1/find-domainwithX-Tenant-Key(first run only).POST {{server}}/api/v1/user/signinwith{email, password}andX-Tenant.- Save
access_token,user_role,company_id,stores[],time_zone, ZATCA numbers. - Check the company subscription (from the sign-in payload, or
GET company/subscription-status). - User picks a store; the app loads roles (
roles/list), settings and master data for that store and runs the startup sync. - If COMPULSORY_DAY_CLOSE_REGISTER is on, the app calls
daily-sales-close/pending-statusand, if a past business day is still open, asks whether to close it now (Yes opens Day Close, No continues). - Normal work. On Logout,
POST user/signoutrevokes the token.
Details: Authentication and session.
Endpoint index
Area | Page | Main endpoints |
|---|---|---|
Sign-in, sign-out, password, subscription, roles |
| |
Products, categories, units, properties |
| |
Stock |
| |
Cart and orders |
| |
Payments, delivery, discounts |
| |
Customers |
| |
Suppliers and purchases |
| |
Sales returns |
| |
Quotations and proforma |
| |
Invoices, receipts, vouchers, expenses |
| |
Reports and day close |
| |
Dashboard |
| |
Settings and master data |
| |
ZATCA |
| |
Sync and realtime |
|