04 — KDS (Kitchen Display System)
List kitchen orders for the current business day and update prep / done / rebump status.
Invoice objects use the shared sync payload shape (06-PAYLOAD-REFERENCE.md) plus order_type_icon.
Routes
| Method | Path | Purpose |
|---|---|---|
GET |
/v1/kds-orders |
Active (or rebumped) kitchen orders — JSON array |
PUT |
/v1/kds-orders/:uuid |
Done / rebump / line is_prepared |
Auth: Authorization: Bearer <jwt> (tenant from token).
Legacy:
| Legacy | New |
|---|---|
GET /kds-orders |
GET /v1/kds-orders |
PUT /kds-order-status / PUT /api/v2/kds-orders/:uuid |
PUT /v1/kds-orders/:uuid |
GET /v1/kds-orders
GET /v1/kds-orders?brand_id=1&is_order_rebump=0&icon_pixel_size=32&icon_format=png
Authorization: Bearer <jwt>
Query parameters
| Param | Type | Default | Description |
|---|---|---|---|
brand_id |
number | — | Filter invoices.brand_id |
c |
string | — | Product category (products.type). Omit or "All" = no filter |
is_order_rebump |
0 | 1 |
0 |
0 = active queue (done_at IS NULL); 1 = done orders (done_at set) |
app_version |
string | — | Affects items[].qty int vs float (legacy) |
build_number |
number | string | — | Used with app_version for qty typing |
icon_pixel_size |
number | 24 |
1–512; icon width/height |
icon_format |
string | png |
png | jpg | jpeg | webp | gif |
icon_url_format |
string | — | Alias of icon_format |
limit |
number | 10000 |
Max orders (cap 10000) |
Invalid query → 400.
Selection rules (server)
Included when all true:
- JWT
customer_id+location_id created_atin current business day (opening hours via main appAPP_URL)statusnot cancelled variantsdone_at IS NULLunlessis_order_rebump=1- Line items loaded with
is_deleted = 0only - Sorted by
order_noascending
Success response
HTTP 200 — plain array (no pagination wrapper):
[
{
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"status": "open",
"customer_id": 1,
"location_id": 2,
"order_no": 42,
"customer_invoice_id": 1001,
"created_at": 1718534400,
"updated_at": 1718534500,
"paid_at": null,
"done_at": null,
"is_accepted": 0,
"order_type": "Dine In",
"order_type_icon_key": "mdi:silverware-fork-knife",
"order_type_icon": "https://api.iconify.design/mdi/silverware-fork-knife.png?width=32&height=32",
"payments": [
{ "payment_method": "cash", "amount": 50, "tip_amount": 0 }
],
"items": [
{
"name": "Burger",
"product_id": 10,
"qty": 2,
"actual_price": 25,
"vat_pct": 5,
"is_prepared": 0,
"modifiers": [],
"addons": null
}
]
}
]
Empty queue → [].
Datatype rules (same as history + extras)
| Concern | Rule |
|---|---|
| Timestamps | Unix seconds |
| Flags | 0 / 1 |
is_accepted |
0 pending, 1 accepted, 2 rejected |
payments |
array or null |
items |
parents + nested addons; modifiers flat |
notes |
string array |
order_type_icon |
Raster URL or null (from order_type_icon_key + icon query params) |
PUT /v1/kds-orders/:uuid
PUT /v1/kds-orders/13A2C02A-C989-4C1B-B9E0-E14A6EB36867
Authorization: Bearer <jwt>
Content-Type: application/json
{
"is_rebump": 0,
"done_at": 1728391030,
"identifiers": [
{ "identifier": "1234567890", "is_prepared": 1 }
]
}
Path
| Param | Required | Description |
|---|---|---|
uuid |
yes | Invoice UUID |
Body (all optional — send only what changes)
| Field | Type | Description |
|---|---|---|
is_rebump |
0 | 1 |
1 clears done_at (back to active queue); ignores body done_at |
done_at |
number | null | Unix seconds; set invoice done_at when not rebumping |
is_accepted |
0 | 1 | 2 |
Optional; 1 = accept, 2 = reject (also publishes Grubtech status via RabbitMQ) |
identifiers |
array | { identifier: string, is_prepared: 0|1 }[] — update matching line items |
Duplicate identifier entries: last wins. Empty identifier strings ignored.
Behaviour matrix
| Input | Effect |
|---|---|
is_rebump: 1 |
invoices.done_at = null |
done_at (no rebump) |
Sets done_at |
identifiers[] |
Sets each matched row’s is_prepared |
Also bumps invoices.updated_at when the invoice row changes, and may publish Ably.
Success response
{
"status": true,
"message": "Kds Order 1001 rebumped"
}
| Condition | Typical message |
|---|---|
| Rebump | Kds Order {order_no} rebumped |
| Identifiers only | Kds Order {order_no} items updated |
| Other | Kds Order {order_no} updated |
| UUID not found | No invoice found against this uuid (still status: true) |
| No order number yet | Order rebumped. / Order updated. |
Side effects
- Update
done_at/is_acceptedand/or lineis_prepared - Ably publish on channel
order:{uuid}, eventstatus(if configured) - RabbitMQ
grubtech.status(best-effort):accept/rejectwhenis_acceptedis 1/2;preparedwhendone_atis set
Ably realtime
| Channel | order:{invoiceUuid} |
| Event name | status |
{
"status": "preparing",
"is_accepted": 1,
"done_at": null,
"updated_at": "2026-06-16T12:34:56.789Z"
}
Derived status |
When |
|---|---|
cancelled |
is_accepted === 2 |
ready |
done_at set |
preparing |
is_accepted === 1 |
pending |
otherwise |
Use Ably subscribe SDK on the device (publish key is server-side only). Obtain the correct Ably subscribe credentials from your environment / backend ops — not from this service’s publish key.
If Ably is unset server-side, KDS PUT still updates DB; clients should poll GET /kds-orders as fallback.
Acceptance field
is_accepted on invoice:
| Value | Meaning |
|---|---|
0 |
Pending |
1 |
Accepted |
2 |
Rejected |
KDS list returns this as 0|1|2. Realtime derives string status as above.
Mobile integration tips
Kitchen display flow
- Poll or open screen →
GET /v1/kds-orders?is_order_rebump=0 - Subscribe Ably
order:{uuid}for tickets you care about (or refresh list on timer) - Mark item prepared →
PUTwithidentifiers - Mark ticket ready →
PUTwithdone_at - Rebump →
PUTwithis_rebump: 1; list withis_order_rebump=1to see done tickets
Qty typing (legacy iOS)
Pass app_version / build_number if you depend on integer qty for old builds (1.5.5 + specific build threshold). Newer apps should get floats.
Icons
Send order_type_icon_key on sync as Iconify-style "prefix:name" (e.g. mdi:silverware-fork-knife). KDS resolves order_type_icon URL via Iconify CDN when listing.
Errors
| Case | HTTP |
|---|---|
| Bad JWT | 401 |
| Missing tenant claims | 400 |
| Invalid query/body (when validation wired) | 400 |
| Success list | 200 + [] or array |
| Success update | 200 + { status, message } |