API integration guide
This walkthrough takes an integration from its first request to a completed POS sale. The API validates identity, organisation and store scope, permissions, product prices, stock, and payment details on the server. Treat API responses as the committed state.
1. Set the API origin
The default local listener is http://localhost:8080 and its base path is /api/v1. Set a full base URL in your environment:
export ZIADA_API='http://localhost:8080/api/v1'Production hosts are deployment-specific. Confirm the origin and configured API_BASE_PATH with the API operator. Do not bake credentials or production tokens into client-side application code.
2. Sign in and retain tokens securely
curl -sS "$ZIADA_API/auth/login" \
-H 'Content-Type: application/json' \
-d '{"identifier":"0712345678","password":"<password>"}'Store the returned access_token and refresh_token in a secure server-side credential store. Send the access token on protected requests:
export ZIADA_TOKEN='<access-token>'
curl -sS "$ZIADA_API/accounts/me" \
-H "Authorization: Bearer $ZIADA_TOKEN" \
-H 'Accept: application/json'Access tokens expire after 15 minutes by default. Exchange a refresh token at POST /auth/refresh; replace both stored tokens with the returned pair. Admin accounts enrolled in TOTP complete the challenge at POST /auth/mfa/totp/verify before using the protected API.
For new accounts, follow the registration and OTP flow. OTP delivery depends on the configured SMS provider.
3. Resolve the active store
Read /accounts/me to identify the token’s active store. Store-scoped data is selected from the token, not from a caller-supplied organisation or store query parameter. To change the active store, call POST /accounts/switch-store with {"store_id":"<uuid>"} and use the newly returned token pair.
4. Read products before checkout
curl --get "$ZIADA_API/inventory/products" \
-H "Authorization: Bearer $ZIADA_TOKEN" \
--data-urlencode 'search=maji' \
--data-urlencode 'page=1' \
--data-urlencode 'page_size=20'Use product IDs and current prices returned by the API. The product and inventory guides describe filters, units, wholesale pricing, stock adjustments, and pharmacy batches.
5. Complete a sale
curl -sS -X POST "$ZIADA_API/pos/sale" \
-H "Authorization: Bearer $ZIADA_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"lines": [{"product_id":"<product-uuid>","qty":2}],
"payment_method":"Cash",
"customer_id":"<customer-uuid>",
"notes":"Counter sale"
}'The API recalculates prices and stock during checkout, assigns a transaction number for the active store, and returns the created transaction. For wholesale or mixed-price organisations, read the transaction guide for price_mode, minimum quantities, discounts, and other sale fields. Credit sales can create a customer credit tab. Pharmacy checkout also enforces batch and prescription-register rules where applicable.
6. Check the response and recover carefully
Business responses generally use a JSON envelope with success, message, data, errors, and meta; health probes return direct JSON. A list response may include pagination metadata. For exact status codes and failure shapes, see Responses and security (opens in a new tab).
If a write request times out after it was sent, check the relevant resource or transaction list before retrying. The API does not document a general idempotency-key contract, so a blind retry of a sale can create a second transaction.
Minimal TypeScript client
const apiBase = process.env.ZIADA_API!;
async function apiRequest<T>(path: string, token: string, init: RequestInit = {}) {
const response = await fetch(`${apiBase}${path}`, {
...init,
headers: {
Accept: 'application/json',
...(init.body ? { 'Content-Type': 'application/json' } : {}),
Authorization: `Bearer ${token}`,
...init.headers
}
});
const payload = await response.json();
if (!response.ok || payload.success === false) {
throw new Error(payload.message ?? `ZiadaPOS request failed (${response.status})`);
}
return payload as T;
}
const products = await apiRequest('/inventory/products?page=1&page_size=20', accessToken);Keep this client on a trusted server when it handles long-lived credentials. A browser application should call its own backend rather than expose a refresh token.
For browser-based calls, the API must allow the browser application's origin through its CORS configuration. Swagger UI's Try it out sends requests from the documentation site's origin, so it also requires that origin to be allowed by the API.