API conventions
Response envelope, error codes, pagination, rate limits, and tenancy.
Learn these once and every endpoint behaves predictably.
Response envelope
{ "success": true, "message": "Human-readable summary", "data": { } }
success— always present; check it before readingdata.data— the payload: a single object, or (for a list) the rows under a resource-named key plus apaginationobject.message— safe to surface to a user; localized by the request’s language.
Errors
Failures set success: false, a message, and a matching HTTP status:
| Status | Meaning |
|---|---|
400 |
Bad request — validation failed. |
401 |
Unauthenticated — missing/expired/revoked key or session. |
403 |
Forbidden — authenticated, but not permitted (e.g. an owner-only action with a scoped key). |
404 |
Not found. |
409 |
Conflict — e.g. a uniqueness violation, or an optimistic-lock mismatch. |
429 |
Rate limited — back off and retry. |
{ "success": false, "message": "A theme with that name already exists" }
Localized messages
Send an X-Locale header (e.g. ar) and human-readable messages come back
localized. Data is not translated by this header — only the message.
Pagination
List endpoints accept limit and a page parameter and return the rows under a
resource-named key with a pagination object:
GET /api/v1/products?limit=20&page=2
{
"success": true,
"data": {
"products": [ /* … */ ],
"pagination": { "page": 2, "limit": 20, "total": 128, "pages": 7 }
}
}
pagination always carries page, limit, total, and pages (the page
count); paginate until page reaches pages.
Rate limits
Requests are rate-limited per client. On 429, honor any Retry-After and back
off exponentially. Design bulk jobs to run within the limit rather than sprinting
into it.
Multi-tenancy
The store is resolved from the request host, and your key is scoped to that store. Isolation is enforced at the database layer (row-level security), so a request can only ever read or write its own store’s rows — there is no tenant id to pass and no way to reach across stores.
Idempotency
For money-affecting writes, send a stable idempotency key on retries where the endpoint supports it, so a network retry can’t double-charge or double-create. Reads are always safe to retry.
Program against the envelope
Write one thin client that checks success and throws on false (see
Getting started). Every call site then deals in
data, and error handling lives in one place.