GET/v1/kitchen-tickets
List kitchen tickets
Returns a page of the work the kitchen has been told to make at one location, oldest sent first, optionally of one order.
Read-only and derived from the ledger: there is no write endpoint, and no channel ever creates a ticket. A ticket is one round of an order — the lines it sent together — so a counter sale is one ticket and each round a tab holds is its own ticket. Lines an order has not yet sent are not here.
Parameters
-
location_idquery · string · uuidRequired
Only tickets at this location; a ticket is for one room.
-
order_idquery · string · uuidOnly tickets for this order.
-
station_idquery · string · uuidOnly tickets routed to this station, for a station's board.
-
cursorquery · stringOpaque cursor from a previous page's
next_cursor. Omit to start from the beginning.Pass it back exactly as received. It is base64 and its structure is not part of this contract — decoding it, or constructing one by hand, will break without notice.
-
limitquery · integer · int32Maximum items to return. The server may return fewer, so always drive the loop from
has_morerather than from the size of the page you got back.
Responses
-
200
A page of kitchen tickets.
KitchenTicketPage · application/json
has_morebooleanRequired
Whether another page follows.
-
itemsarray of KitchenTicketRequired
kindstring · enumWhat this ticket is.
roundis the ordinary ticket — make these lines.cancelis a cancellation: a line that was sent to the kitchen was taken back off the tab, so the kitchen is told to stop making it. The board draws a cancellation plainly so it is not read as a fresh order.roundcanceldefault round
bumped_atstring · date-timeNullable
When this ticket was bumped — the kitchen said it is done. Bump state is kitchen data, not order data, and lives here rather than on the order. Null until bumped.
channelstring · enumRequired
Where the order came from, set by the server from the caller and never taken from a client:
posis a till,tablea table-side device,onlinea venue's ordering page,integrationa third-party integration. The kitchen shows an online or integration order so a pass can tell an outside guest from the room — "two identical coffees" is not enough when one is for a delivery the shop has already been paid for.postableonlineintegrationcustomer_namestringNullable
Whose sale it is — the name a tab was held under, or the guest on the order. Null for a walk-in.
≤ 255 chars
line_idstring · uuidNullable
The removed line a cancellation is for — the key it is acknowledged under, so acknowledging it never touches the round it cancels. Null for a round ticket.
-
linesarray of KitchenTicketLineRequired
What to make, in the order it was rung.
namestringRequired
Product name as sold. Snapshot; never re-read from a product.
1–255 chars
allergen_clashesarray of stringThe allergens this line's product carries that clash with what the linked guest declared on their profile. The clash, not the guest's health record, which a kitchen device is not allowed to read. Empty for a walk-in, a product with no allergens, or a guest with none declared.
allergens_uncheckedbooleanTrue when this line could not be checked against the guest's declared allergies at all — an open item, which carries no product, or a product the catalogue no longer has. The guest declared something; this line was not looked at.
It exists because an empty
allergen_clashesotherwise means two different things, and one of them is dangerous: a cook reading no flag assumes the line was checked and is clean. The till draws the same distinction for the cashier (_allergyWarnings,ui/till_screen.dart); this carries it to the pass.courseinteger · int32Nullable
The course this item belongs to — starters, mains, desserts — so the kitchen times it and knows what is still held. Null for a single-course sale or a course-less line.
≥ 1
-
modifiersarray of objectThe options chosen on this line, as names only — the kitchen reads "Oat milk" and "Decaf", not a price delta. In the order the till sent them in.
namestringRequired
Option name as sold. Snapshot.
1–255 chars
notestringNullable
Free text from the till — "no nuts", "well done".
≤ 512 chars
quantityQuantityRequired
How much of something, as a decimal string in the
unit_of_measurebeside it (eachwhere there is none), as Square'squantityis: three croissants are"3", 1.4 kg of beans is"1.4". A string because a JSON number is a binary float to most parsers.It has at most as many decimal places as its unit's base unit holds — three in
each,gandml, six inkgandl— and a finer one is refused, never rounded. Tenvelo holds every quantity as an integer of that base unit (thousandths of an each, milligrams, microlitres;docs/decisions/0063); a client that computes with one should read the digits the same way, never through a float.≤ 26 chars · pattern ^-?(0|[1-9][0-9]*)(\.[0-9]{1,6})?$
seatinteger · int32Nullable
The seat this item is for, carried from the order so the kitchen plates to the right place. Null for a counter sale or an unnumbered seat.
≥ 1
location_idstring · uuidRequired
The location the order is in; a ticket is for one room.
order_idstring · uuidRequired
The order this ticket belongs to.
pickup_atstring · date-timeNullable
When a collection order is due, carried from the order's promise so the kitchen can pace the work and tell a waiting guest the truth. Null for an in-house sale or one nobody promised a time for. When the guest was told they could collect, set by the channel that took the order (online-ordering item 13). Null for a dine-in order and for a counter sale — the kitchen knows a requested time only when there is one. This ticket is for collection when it is set.
roundinteger · int32Required
Which round of the order this is, one-based in the order they were sent. A tab held twice has two tickets,
round1 and 2; a completed counter sale is a single round.≥ 1
sent_atTimestampRequiredRead only
Server-assigned RFC 3339 timestamp, always UTC.
Server-assigned because device clocks drift and cannot be trusted for ordering — a till that has been powered off for a week may come back with a clock hours out. Where the device's own view of time matters it is carried separately in
client_created_at.station_idstring · uuidNullable
The station this ticket was routed to, when the location has stations configured — a round spanning stations becomes one ticket per station. Null for a kitchen with no stations, or for an un-routed line.
station_namestringNullable
The station's name, as the kitchen knows it.
≤ 255 chars
table_namestringNullable
The table the order is on, resolved from the table plan. Null for a counter sale.
≤ 255 chars
undoable_untilstring · date-timeNullableRead only
Until when the bump may still be taken back. Null for a ticket that is not bumped. A board offers the undo until then, and the server refuses it after.
undone_atstring · date-timeNullable
When the bump was undone, within the window a bump may be un-bumped (a bumped ticket that was not finished is a lost meal). Presence means the ticket is no longer bumped. Null while bumped.
next_cursorstringNullable
Opaque cursor for the next page, or null on the last page. Pass it back verbatim as the
cursorquery parameter; its contents are not part of this contract.
-
400
The request could not be parsed. Malformed JSON, a bad query parameter, or a header that failed to decode, among them an
API-Versionlater than the server speaks or not a date (api-version-unknown, with the server's own inapi_version).Distinct from
422: a400never reached validation, soinvalid_paramsis absent. Retrying without changing the request will fail again, except that a request refused for itsAPI-Versiongoes once the server is updated. -
401
No credentials, or credentials that could not be verified — missing bearer token, malformed JWT, expired token, or a signature that failed against the issuer's JWKS.
Refresh the token and retry once. Repeated
401after a refresh means the device needs to re-enrol. -
403
Authenticated, but not permitted. The token is valid and its merchant was resolved, but the caller's role does not allow this operation, or the target belongs to a different merchant.
Cross-tenant access is reported as
403on a resource the caller may not see, and as404where even confirming existence would leak information. Retrying will not help. -
410
The request names an
API-Versionthis server no longer serves (docs/decisions/0011). Nothing in it was read.api_versionnames the version the server speaks: the client is behind, and sends the request again once it is updated. -
422
The request parsed but failed validation.
invalid_paramslists every failing field with a JSON Pointer, not just the first, so a client can mark up a whole form in one pass rather than making the user fix errors one round trip at a time.Also returned when an
Idempotency-Keyis replayed with a different body, and when device-computed order totals disagree with the server's recomputation. -
429
Rate limit exceeded. Honour
Retry-Afterrather than applying your own backoff.A fleet of tills coming back online together after an outage is the realistic cause, which is exactly when uncoordinated client-side retry turns a brief outage into a sustained one. The limit is per process, and per site for what records trade there — a request spends its site's budget, and a till's own smaller one inside it — and per
(client, merchant)for everything else; noRateLimitfield is sent, so this response and itsRetry-Afterare the whole of the contract. -
500
Something failed on our side.
trace_ididentifies the request in our tracing — quote it in a support ticket.The body never carries internal detail: no SQL, no stack traces, no upstream error strings. Safe to retry with the same
Idempotency-Key. -
503
Temporarily unable to serve the request — a dependency is down or the service is shedding load.
Retry with the same
Idempotency-KeyafterRetry-After. This is always safe: a request that did take effect before the failure will be recognised as a replay.