OneTablet API Documentation

fetchOrders

POST/management/orders/fetchOrders

The order feed and single-order read in one operation: filter by time window, statuses, kitchen or storefront — or pass orderIds to fetch specific orders.

createdAtStartDate / createdAtEndDate bound the order's CREATION time — the moment OneTablet recorded the order, which is the createdAt every order carries. createdAtStartDate is INCLUSIVE and createdAtEndDate is EXCLUSIVE. There is no maximum window width (ONE-2750 removed the 31-day ceiling) and no rolling retention ceiling either, so a full warehouse backfill is a loop over contiguous [createdAtStartDate, createdAtEndDate) windows sized to whatever your own loader prefers. Both bounds are optional; omit both for all-time.

⚠️ THE PARAMETER NAME IS THE CONTRACT: these filter on createdAt and on nothing else (ONE-2748, made literally true by ONE-2750, which moved this feed onto a query that reads that column). An order's PICKUP time is a different timestamp, minutes to days away from its creation, and there is no pickup-time window on this operation — a pickup filter would arrive as a separately named parameter. Size your windows against creation time accordingly: an order created just before createdAtStartDate belongs to the PREVIOUS window even when it is picked up inside this one, and a backfill assembled from contiguous creation-time windows is complete precisely because every order is created exactly once.

The page is SORTED (ONE-2750): by createdAt, newest first unless sort says otherwise, with ties broken on the order's id so the ordering is total. sort: "createdAtAsc" with no cursor is "my very first order". Ordering on this operation used to be unspecified — it is not any more, and the cursor is a partition of the result rather than a best effort.

locationCode and storeId narrow to one kitchen or one storefront respectively and are MUTUALLY EXCLUSIVE; sending both is a 400. locationCode replaced locationId in ONE-2750.

Every filter here is served; nothing on fetchOrders answers 400 for being unimplemented.

Cursor-paginated, to a depth of 10,000 orders — see page. Deeper is a 400 pointing you at a narrower window, which is the cheaper way to ask the same question. The response is an object, so new fields can arrive additively.

⚠️ ITERATION IS STILL NOT SNAPSHOT-STABLE. The sort is total, so pages no longer overlap or skip under concurrent writes — but an order CREATED mid-sweep still lands wherever its createdAt puts it, which on the default newest-first sweep is a page you have already read. Close the window (an upper bound in the past cannot gain rows) and reconcile on order id for anything that must be exact.

Request

application/json
required
  • orderIdsstring[]

    Fetch specific orders. Every other filter is ignored when this is present — the window, the statuses, the kitchen and the storefront. sort and page still apply: they shape the answer rather than narrowing it.

    That has always been what this field says, and as of ONE-2750 it is also what happens. Before it, the filters were combined, so an orderIds call that also carried a stale time window came back missing the ids that fell outside it — a subset indistinguishable from "those orders do not exist". If you were sending filters alongside orderIds and relying on them to narrow, send them without orderIds instead.

  • locationCodestring

    One kitchen per call, addressed by its short site code — the code on the Location that fetchStores and fetchStore already report. Omit for every kitchen. An array form may be added additively later.

    ⚠️ REPLACES locationId (ONE-2750). The feed behind this operation filters on the location CODE, and the previous parameter took an id that this service then resolved to a code by making a second call to the store directory — which meant a real kitchen with no storefronts yet answered 404 for a filter that should simply have matched nothing. Taking the code directly removes that call, that failure mode, and one hop from every request. The code is published on every store row, so it is a value you already hold.

    Mutually exclusive with storeId — see that field.

  • storeIdstring

    One storefront per call, by Store.id as fetchStores reports it (ONE-2750). Omit for every storefront.

    MUTUALLY EXCLUSIVE WITH locationCode: sending both is a 400. A store is already inside exactly one kitchen, so the pair is either redundant or contradictory, and answering either way would hand you a page whose meaning depends on which filter we decided to honour. Send whichever one you actually mean.

  • statusesstring[]

    Filter by stored status. recentlyCancelled is a stored-status filter value that renders as cancelled in responses — pass both to retrieve all cancelled orders.

    Possible values: [new, future, ready, finished, rejected, cancelled, recentlyCancelled]
  • createdAtStartDatestring<date-time>

    Inclusive lower bound on the order's CREATION time (Order.createdAt), ISO-8601 UTC.

  • createdAtEndDatestring<date-time>

    Exclusive upper bound on the order's CREATION time (Order.createdAt), ISO-8601 UTC.

    NO MAXIMUM WINDOW WIDTH (ONE-2750 removed the 31-day ceiling), and omitting both bounds is legal and means all-time.

  • sortstring

    How to order the page, on the order's creation time (ONE-2750). Defaults to createdAtDesc.

    createdAtDesc — newest first. This is what you want for "what has arrived since I last polled", and it is what every OneTablet surface shows a kitchen.

    createdAtAsc — oldest first, which with no cursor is exactly "my very first order".

    The order is TOTAL — ties on createdAt break on the order's id — so a given page of a sweep names the same rows every time you ask for it.

    Possible values: [createdAtAsc, createdAtDesc]
  • pagePageRequest

    Cursor pagination.

    ⚠️ THE CURSOR HAS A DEPTH LIMIT ON THIS FEED: 10,000 orders. Past that you get a 400 naming the number. Paging deeper is a backfill, and the answer for a backfill is a narrower [createdAtStartDate, createdAtEndDate) window — which costs you nothing, since there is no limit on how WIDE a window may be. The two together give you every order we hold; what they refuse is the one access pattern that is expensive for both of us.

Responses

200 — OK
  • ordersOrder[]

    The page of orders, ordered by creation time in the direction sort asked for — newest first unless you said otherwise (ONE-2750; ordering used to be unspecified here).

    Iteration is still NOT snapshot-stable: an order created while you are paging lands wherever its createdAt puts it, which for the default newest-first sweep is a page you have already read. For an exact backfill, close the window — contiguous [createdAtStartDate, createdAtEndDate) ranges whose upper bound is in the past cannot gain rows while you page them — and reconcile on order id.

  • nextCursorstring

    Opaque cursor for the next page. Absent means the last page.

About Orders

Orders placed with your restaurants — read them, and act on them.

This is where an integration starts: poll fetchOrders for the orders OneTablet has accepted on your behalf across every delivery platform you are connected to, normalised into ONE shape regardless of whether they arrived from DoorDash, Uber Eats, Grubhub or a direct channel.

Two conventions hold everywhere on this API and are worth learning once. Money is always an integer in the currency's minor unit — 1250 is $12.50 — because a decimal total cannot survive a round trip through every language's default number type. Times are always ISO-8601 in UTC; a store's local time is a rendering decision, and the API does not make it for you.

All endpoints are POST with a single JSON body, and the path carries no version segment: evolution here is additive — new fields and new endpoints, never removals or narrowing (ONE-2596) — so there is no cutover a /v1 would ever mark.