README docs frontend-handout/04-KDS.md

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_at in current business day (opening hours via main app APP_URL)
  • status not cancelled variants
  • done_at IS NULL unless is_order_rebump=1
  • Line items loaded with is_deleted = 0 only
  • Sorted by order_no ascending

Success response

HTTP 200plain 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

  1. Update done_at / is_accepted and/or line is_prepared
  2. Ably publish on channel order:{uuid}, event status (if configured)
  3. RabbitMQ grubtech.status (best-effort): accept / reject when is_accepted is 1/2; prepared when done_at is 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

  1. Poll or open screen → GET /v1/kds-orders?is_order_rebump=0
  2. Subscribe Ably order:{uuid} for tickets you care about (or refresh list on timer)
  3. Mark item prepared → PUT with identifiers
  4. Mark ticket ready → PUT with done_at
  5. Rebump → PUT with is_rebump: 1; list with is_order_rebump=1 to 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 }