Orders API

Create orders with atomic stock deduction, view history, and track delivery status.

Base: /api/v1/orders  |  X-Tenant-ID required  |  All endpoints require auth 🔒
Order creation uses PostgreSQL row-level locks (SELECT FOR UPDATE) to prevent overselling during concurrent checkout.
POST /api/v1/orders 🔒 Auth Required

Create an order. Server calculates subtotal, tax, discount, and delivery fee. Initiates payment automatically.

Request Body

{
  "items": [
    { "variant_id": "uuid", "quantity": 1 } // required
  ],
  "shipping_address_id": "uuid",   // use saved address (OR provide shipping_address inline)
  "shipping_address": {            // OR inline (for guests)
    "recipient_name": "Jane",
    "phone": "0712345678",
    "street": "Kimathi St",
    "county": "nairobi",
    "town": "CBD"
  },
  "county": "nairobi",             // required — used for delivery zone lookup
  "payment_method": "MPESA",       // required — "MPESA" | "CARD"
  "mpesa_phone": "0712345678",     // required if MPESA
  "coupon_code": "WELCOME10",      // optional
  "customer_note": "Leave at gate" // optional
}

Response

{
  "success": true,
  "data": {
    "order": {
      "id": "uuid",
      "order_number": "ALO-2024-00001",
      "status": "pending",
      "payment_status": "pending",
      "subtotal": 1500,
      "discount_amount": 150,
      "tax_amount": 216,
      "delivery_fee": 200,
      "total": 1766,
      "tracking_status": [],
      "estimated_delivery_at": null
    },
    "payment": {
      "method": "MPESA",
      "checkout_request_id": "ws_CO_...",
      "message": "STK push sent to 0712345678"
    }
  }
}

Possible Errors

CodeHTTPWhen
STOCK_INSUFFICIENT422Variant out of stock
COUPON_INVALID422Invalid coupon
VALIDATION_ERROR422Missing required fields
GET /api/v1/orders/my 🔒 Auth Required

Paginated list of the current user's orders, newest first.

Query Parameters

ParamTypeRequiredDescription
pagenumberNoPage
limitnumberNoPer page

Response

{ "success": true, "data": { "orders": [ { "id","order_number","status","payment_status","total","created_at" } ] }, "meta": { ... } }
GET /api/v1/orders/my/:id 🔒 Auth Required

Full order detail including line items, tracking timeline, and invoice link.

Response

{
  "success": true,
  "data": {
    "order": {
      "id": "uuid",
      "order_number": "ALO-2024-00001",
      "status": "shipped",
      "tracking_status": [
        { "state": "Order Placed", "note": "Received", "timestamp": "2024-01-10T09:00:00Z" },
        { "state": "Shipped", "note": "Left warehouse", "timestamp": "2024-01-11T08:00:00Z" }
      ],
      "order_items": [ { "product_snapshot": { "name","price" }, "quantity", "unit_price", "total_price" } ],
      "estimated_delivery_at": "2024-01-15",
      "delivery_fee_adjusted": false,
      "invoice": { "id", "invoice_number", "pdf_url" }
    }
  }
}
POST /api/v1/orders/:id/cancel 🔒 Auth Required

Cancel an order. Only possible when status = "pending".

Response

{ "success": true, "message": "Order cancelled successfully." }

Possible Errors

CodeHTTPWhen
NOT_FOUND404Order not found or not yours
VALIDATION_ERROR400Order is not cancellable
POST /api/v1/orders/:id/reorder 🔒 Auth Required

Fetch items from an old order for easy reordering.

Response

{
  "success": true,
  "message": "Reorder items prepared.",
  "data": {
    "items": [
      { "product_id": "...", "variant_id": "...", "name": "Classic Tee", "image": "url", "quantity": 2, "unit_price": "1500" }
    ],
    "omitted_count": 0
  }
}

Public Order Tracker

POST /api/v1/orders/track/request-otp Public

Request a 6-digit OTP for secure order tracking.

Request Body

{ "order_number": "ORD-123", "email": "john@example.com" }

Response

{ "success": true, "data": { "otp_session_token": "eyJ...", "message": "OTP sent to email." } }
POST /api/v1/orders/track/verify-otp Public

Verify the OTP to get a long-lived tracking token.

Request Body

{ "otp_code": "123456", "otp_session_token": "eyJ..." }

Response

{ "success": true, "data": { "trusted_link_token": "eyJ..." } }
GET /api/v1/orders/track Public

Fetch public tracking timeline using a trusted link token.

Query Parameters

ParamTypeRequiredDescription
tokenstringYestrusted_link_token from verify step

Response

{
  "success": true,
  "data": {
    "order_number": "ORD-123",
    "status": "shipped",
    "tracking_number": "TRK-999",
    "carrier": "G4S",
    "estimated_delivery": "2026-06-25T00:00:00Z",
    "items": [ { "product_name": "...", "quantity": 1, "product_image": "..." } ],
    "timeline": [ { "status": "shipped", "note": "On the way" } ]
  }
}

Admin Orders /api/v1/admin/orders

GET /api/v1/admin/orders 🔒 Auth Requiredorders:read

List all orders with filters.

Query Parameters

ParamTypeRequiredDescription
statusstringNopending | confirmed | shipped | delivered | cancelled
payment_statusstringNopaid | pending | failed
fromdateNoISO date start
todateNoISO date end
countystringNoFilter by county
searchstringNoOrder number search
assigned_touuidNoFilter by assigned staff
pagenumberNoPage
limitnumberNoPer page

Response

{ "success": true, "data": { "orders": [ ... ] }, "meta": { ... } }
PATCH /api/v1/admin/orders/:id/status 🔒 Auth Requiredorders:write

Update order status. Optionally sends customer notification.

Request Body

{ "status": "shipped", "note": "Dispatched via road courier", "notify_customer": true }

Response

{ "success": true, "data": { "order": { "id","status","tracking_status" } } }
PATCH /api/v1/admin/orders/:id/tracking 🔒 Auth Requiredorders:write

Append a new entry to the tracking timeline.

Request Body

{ "state": "Out for Delivery", "note": "Driver in Mombasa CBD" }

Response

{ "success": true, "data": { "order": { "tracking_status": [ ... ] } } }
PATCH /api/v1/admin/orders/:id/delivery-fee 🔒 Auth Requiredorders:write

Adjust delivery fee. Sets delivery_fee_adjusted = true and notifies customer.

Request Body

{ "new_fee": 800, "note": "Remote area surcharge", "notify_customer": true }

Response

{ "success": true, "data": { "order": { "delivery_fee": 800, "delivery_fee_adjusted": true } } }
PATCH /api/v1/admin/orders/:id/assign 🔒 Auth Requiredorders:assign

Assign an order to a fulfillment staff member.

Request Body

{ "assigned_to": "uuid" }

Response

{ "success": true, "data": { "order": { "assigned_to": "uuid" } } }
POST /api/v1/admin/orders/:id/internal-note 🔒 Auth Requiredorders:write

Add an internal note to an order (not visible to customer).

Request Body

{ "note": "Customer called to confirm address" }

Response

{ "success": true, "data": { "order": { "internal_note": "..." } } }