Skip to content
Sales Docs

Help · For developers

Endpoints

All paths are relative to https://sales.api.kushi.ng. API-key endpoints need authentication; storefront endpoints use your store slug.


Integrator API — Orders board (API key)

Create an order

POST /public/v1/orders

Creates an order on your Orders board. Each item either links a catalog product by productId (price and name come from the catalog) or is a free-text line with your own name and unitPrice. A line without productId is still linked if its sku, or its name (ignoring case), matches a product, or if you've linked that name under Order links in Stock. An unknown productId returns 400.

Linking by productId is what lets the order affect inventory — but stock moves when the order reaches Delivered, not when it's created, because the goods are still yours until then. Set the status with PATCH /public/v1/orders/{id} and the deduction happens then, taking from the assigned agent if there is one, otherwise the office. Combos deduct their constituents; a quantity bundle deducts its free gifts.

Request

{
  "customerName": "Ada Obi",
  "phone": "08012345678",
  "address": "12 Marina, Lagos",
  "codAmount": 27500,
  "deliveryFee": 1500,
  "courier": "GIG",
  "source": "shopify",
  "items": [
    { "productId": "3f9c…", "qty": 2 },
    { "name": "Gift wrap", "qty": 1, "unitPrice": 500 }
  ]
}

Only customerName and a non-empty items array are required. New orders start in Pending. codAmount defaults to the order total.

Retries — send your own order reference as externalId. A second request with the same externalId returns the existing order (200, with "duplicate": true) instead of creating another.

Response — 201 Created

{ "id": "a1b2…", "reference": "A1B2C3D4", "status": "Pending", "total": 55500 }

Duplicate guard — the same phone + total within 6 hours returns the existing order instead of creating another:

{ "id": "a1b2…", "duplicate": true }

List orders

GET /public/v1/orders?status=Pending&limit=50

Recent orders, newest first. status (optional) filters to one status; limit defaults to 50 (max 200).

Response — 200 OK

{ "orders": [
  { "id": "a1b2…", "reference": "A1B2C3D4", "customerName": "Ada Obi", "phone": "08012345678",
    "status": "Pending", "paymentStatus": "unpaid", "total": 55500, "source": "shopify",
    "createdAt": "2026-07-21T10:00:00Z", "items": [ { "name": "…", "qty": 2, "unitPrice": 27500 } ] }
] }

Update an order

PATCH /public/v1/orders/{id}

Send status and/or paymentStatus. Statuses: Pending, In Transit, Delivered, Rescheduled, Call Back, Rejected, Follow Up.

This does what the status change in the app does: reaching Delivered moves stock and adds the cash to the agent's balance, moving out of Delivered puts them back, and an order.status_changed webhook is sent.

{ "status": "In Transit", "paymentStatus": "paid" }

Response — 200 OK → { "ok": true, "status": "In Transit" }

List products

GET /public/v1/products
{ "products": [
  { "id": "3f9c…", "name": "Magic Brush", "sku": null, "unit": "pack", "type": "simple",
    "price": 27500, "cost": 4700, "stock": 98, "isActive": true }
] }

Live stock

GET /public/v1/stock
{ "stock": [
  { "productId": "3f9c…", "name": "Magic Brush", "office": 98, "withAgents": 31, "total": 129 }
] }

Storefront — customer checkout (no key)

Store details

GET /public/v1/store/{slug}

Returns the store name/branding and its active products (with offer tiers) — enough to render a store page.

Place an order

POST /public/v1/store/{slug}/orders

A pay-on-delivery order from a customer. Prices are computed server-side from the catalog. Body: customerName, phone, address, optional email/note, and items: [{ productId, qty }]. Returns 201 with { "orderId": "…", "ref": "A1B2C3D4", "total": 27500 }. The same phone and total within 6 hours returns 200 with { "id": "…", "duplicate": true } instead.

Track an order

GET /public/v1/orders/{id}

Public status timeline for one order — the full order id acts as the token. No key required.