Skip to content
NexG ConciergeDevelopers

Requests and responses

The JSON envelope every response shares, pagination, money, times, caching and versions.

On this page

Requests

  • Every call is HTTPS. Reads are GET with their options in the query string.
  • List endpoints refuse a query parameter they do not know with 400 invalid_request, so a typo such as town=nairobi fails loudly instead of being ignored.
  • If a parameter is repeated (?city=nairobi&city=mombasa), the first one is used.
  • Writes, when they ship, take a JSON body with Content-Type: application/json and an Idempotency-Key header.

The response envelope

Every response, success or failure, is a JSON object with ok first. Check it before anything else.

A list

200 OK
{
  "ok": true,
  "data": {
    "items": [ { "id": "9b2f6c1e-…", "trading_name": "Sokoni Grill House", "…": "…" } ],
    "next_cursor": "MjAyNi0xMC0yMVQwODoxNTowMCswMDowMHw5YjJmNmMxZS00YTdk…"
  },
  "meta": { "api_version": "v1", "limit": 25 }
}

One object

200 OK
{
  "ok": true,
  "data": {
    "merchant": { "id": "9b2f6c1e-…", "trading_name": "Sokoni Grill House", "…": "…" },
    "catalogue": [ { "name": "Nyama choma platter", "price_kes": 1450, "…": "…" } ]
  },
  "meta": { "api_version": "v1" }
}

An error

400 Bad Request
{
  "ok": false,
  "error": {
    "code": "invalid_request",
    "message": "Unrecognized key(s) in object: 'town'",
    "fields": { "_": "Unrecognized key(s) in object: 'town'" }
  },
  "meta": { "api_version": "v1" }
}

meta.api_version is always v1 on these endpoints. On a list, meta.limit is the page size the server applied. See Errors for every error code.

Pagination

Lists are paged with a cursor, never an offset. Each list has a fixed order (merchants by when they went live, newest first; orders by when they were placed, newest first; experiences by start time, soonest first), and next_cursor marks where the page ended. Pass it back as cursor to get the next page, unchanged; it is opaque and its contents may change. When next_cursor is null, there are no more pages.

Pages hold 25 by default; ask for 1 to 100 with limit. Each page starts strictly after the last row of the one before, so a row is never returned twice or skipped, even when new rows arrive while you page. A cursor the endpoint did not issue is refused with 400 invalid_request.

Reading every page
// Follow next_cursor until it is null.
let cursor = null;

do {
  const url = new URL('https://api.nexgapp.com/v1/orders');
  url.searchParams.set('limit', '100');
  if (cursor) url.searchParams.set('cursor', cursor);

  const res = await fetch(url, { headers: { 'X-Api-Key': process.env.NEXG_API_KEY } });
  const body = await res.json();
  if (!body.ok) throw new Error(`${res.status} ${body.error.code}`);

  await saveOrders(body.data.items); // upsert by order_id
  cursor = body.data.next_cursor;
} while (cursor);

Money

Amounts are integers, never decimals, so they add up exactly. Two units are in use, and the field name always says which.

FieldUnitExample
price_kesWhole Kenyan shillings1450 is KES 1,450
total_centsCents161625 is KES 1,616.25
pay_on_delivery_cap_kesWhole Kenyan shillings5000 is KES 5,000

The currency is always KES. A price on an order is fixed when the order is placed, and NexG works it out itself: no request ever states a price.

Times and ids

  • Times are ISO 8601 with an offset, and NexG sends them in UTC, for example 2026-11-10T09:42:17.204+00:00. Show them to people in Nairobi time (UTC+3).
  • When you send a time, include its offset: 2026-11-14T06:30:00+03:00.
  • Ids are UUIDs. Orders also have a reference such as NX-261110-0003 for people to read. Store both.

Caching

Public data may come from a shared cache, so a change at NexG, such as a merchant closing early, can take a minute or more to reach you. Your own orders and every error are never cached.

EndpointsCache-ControlFresh for
Merchants, a merchant, experiencespublic, s-maxage=60, stale-while-revalidate=30060 seconds
Zones, servicespublic, s-maxage=300, stale-while-revalidate=36005 minutes
Your orders, every errorno-storeNever cached

Request ids

Every JSON error carries an X-Request-Id header, unique to that response. Log it with the failure. NexG logs the id of every server error (5xx) with what went wrong, so quoting it is the fastest way to have a failure looked into.