Base URL is your deployed origin (e.g. https://your-domain.com). All JSON bodies use Content-Type: application/json unless noted.
Dashboard APIs use a cookie session (cod.sid). From JavaScript, send credentials: "include" on fetch requests.
| Method | Path | Description |
|---|---|---|
| POST | /login |
Body: { "username", "password" }. Returns { success, redirect: "/embedded" } on success; sets session cookie. |
| POST | /logout |
Destroys session. Returns { success: true }. |
If WEBHOOK_SECRET is set in the environment, each request must send the secret as Authorization: Bearer <secret> or X-Api-Key: <secret>.
Required body: customer.phone (string). Typical payload also includes customer (name, email, city, address, …), product, total, utm, notes, optional id (otherwise generated).
Multi-tenant routing: each stored order has business_id (nullable). The server resolves it from (in order) business_id, business_slug, hostname of page_url, or hostname of shop (e.g. store.myshopify.com) against the hosts list configured per business in the admin UI. Unmatched orders stay business_id: null (admin-only visibility).
{
"id": "optional-stable-id",
"page_url": "https://your-store.com/products/item",
"shop": "optional.myshopify.com",
"business_slug": "optional-if-you-use-slugs",
"customer": {
"name": "Jane",
"phone": "+212600000000",
"city": "Casablanca",
"address": "…"
},
"product": { "name": "Item", "price": 199, "quantity": 1 },
"total": 199,
"currency": "MAD",
"utm": { "source": "facebook" }
}
Admin sees all businesses and unassigned orders. Support users only see orders whose business_id is one of the businesses assigned to them (many-to-many in SQLite). Support users with no assignment see an empty order list.
List endpoints accept optional ?business_id=<id> to filter. Use business_id=unassigned (admin) to list orders with no business. Omit or use all for no filter.
All routes below require a valid session except where noted. Admin-only actions need role: "admin" on the account.
| Method | Path | Auth | Notes |
|---|---|---|---|
| GET | /api/me |
Session | { userId, username, role, businesses[], unassignedCount } — businesses is scoped to the user (all for admin, assigned for support). |
| GET | /api/orders |
Session | Scoped by role; optional ?business_id=. Active orders only by default. Admin: ?include_deleted=1, ?deleted_only=1 (trash). Support never receives deleted rows. |
| PATCH | /api/orders/:id |
Session | Body: customer fields, agent_notes. Updates order in place. |
| POST | /api/orders/:id/status |
Session | Body: { "status": "pending"|"confirmed"|"shipped"|"delivered"|"cancelled" } |
| DELETE | /api/orders/:id |
Admin | Soft-delete: sets deleted_at (moves to trash). |
| POST | /api/orders/:id/restore |
Admin | Clears deleted_at for that order. |
| GET | /api/stats |
Session | Aggregates for active orders in scope; same ?business_id= filter as /api/orders. |
| GET | /api/businesses |
Admin | List businesses (id, slug, name, hosts). |
| POST | /api/businesses |
Admin | Body: { slug, name, hosts: ["domain.com", …] }. |
| PATCH | /api/businesses/:id |
Admin | Partial update: slug, name, hosts. |
| DELETE | /api/businesses/:id |
Admin | Deletes business row (assignments cleared via FK). |
| GET | /api/users |
Admin | Users with businessIds for support accounts. |
| POST | /api/users |
Admin | Body: { username, password } — creates a support user. |
| PATCH | /api/users/:id/businesses |
Admin | Body: { businessIds: number[] } — replaces support user’s business assignments (ignored for admin users). |
| GET | /api/olivraison/config |
Session | Public-safe Olivraison flags (no raw secrets). |
| POST | /api/orders/:id/olivraison/push |
Session | Creates package when Olivraison keys are configured. |
| POST | /api/orders/:id/olivraison/sync |
Session | Pulls remote package status. |
| POST | /api/olivraison/sync-all |
Session | Body or query limit (max 100). Bulk sync tracked orders. |
GET /health — JSON with status, uptime, version, DB check, Olivraison configured flag. Intended for monitors; do not expose sensitive data publicly without network controls.
Login, webhook, and /api/* routes use express-rate-limit (see server configuration). Expect 429 when exceeded.
Run npm run test:oliv from the project root (loads .env). It calls POST /auth/login and GET /status_list. If login returns code: "002" / “Client not found”, the API key or secret is wrong or the partner account is inactive — copy keys from the Olivraison partner dashboard.
Package creation returns a tracking id under trackingID or alternate field names; the server normalizes these. If push still fails, check the JSON error body from Olivraison (destination blacklisted, missing city, invalid pickup JSON, etc.).