Skip to content

Conventions

Response envelope

Every JSON success or error looks like this:

json
{
  "success": true,
  "statusCode": 200,
  "statusMessage": "SUCCESS",
  "results": {},
  "error": null
}

On failure, results is null and error holds the payload (message and optional field errors):

json
{
  "success": false,
  "statusCode": 401,
  "statusMessage": "AUTHENTICATION_REQUIRED",
  "results": null,
  "error": {
    "message": "Authentication required"
  }
}

statusMessage is a stable enum. See Errors. Do not branch on localized message text.

Auth classes

LabelMeaning
PublicNo JWT required. Guests allowed.
CustomerAuthorization: Bearer <accessToken> from OTP verify or refresh. Missing / expired → 401. Refresh once, then treat as logged out.
StaffAuthorization: Bearer <accessToken> from staff login or refresh. Missing / expired → 401.
Staff + orders:readGET fulfillment
Staff + orders:updatePOST fulfillment actions

Customer attach is optional on catalog routes: a JWT, if present, is loaded, but guests can still browse and use a cart.

IDs and slugs

  • Mongo ids are 24-char hex strings.
  • Catalog reads by slug (GET /v1/products/:slug).
  • Mutations that target a product usually take productId.
  • Order paths use :orderId.

Bilingual objects

Admin-shaped documents use:

json
{ "en": "Milk", "ar": "حليب" }

Public catalog cards usually expose a single resolved name string for the request language.

SSE exceptions

These endpoints are not the JSON envelope:

  • GET /v1/notifications/ssetext/event-stream
  • POST /v1/assistant/messagestext/event-stream

Rate limits

The Public API applies Redis-backed rate limits. Assistant list is capped (60/min) and send-message is capped (20/min). Treat 429 + RATE_LIMITED / TOO_MANY_ATTEMPTS as back-off.

Idempotency

Fulfillment POSTs are not advertised as idempotent. Re-scan of an already picked line or a second ready will fail with a domain error. Refresh the order after each action.