Dine Orders API — floor-plan tables with active dine-in orders
Endpoints for listing floor-plan tables and their open / done dine-in invoices. Each invoice inside dinerOrderDetail uses the same object shape and datatypes as POST /v1/invoices/sync, GET /v1/invoices/history, and GET /v1/kds-orders, defined in src/validations/invoice.validation.js (syncInvoicePayloadSchema), plus one optional UI field: order_type_icon.
Authentication: jwtAuth — valid JWT in Authorization: Bearer <token>. Tenant scope (customer_id, location_id) is taken from the token, not from query parameters.
Validation: src/validations/dineOrders.validation.js (request + response contract schemas for frontend alignment).
See also:
- INVOICE_REQUEST.md — full invoice / line-item field reference
- INVOICE_HISTORY.md — same read shape, paginated history endpoint
- KDS_API.md — kitchen display orders (sync-shaped invoices, no table wrapper)
Routes
| Method | Path | Handler |
|---|---|---|
GET |
/v1/dine-orders |
List floor-plan tables with open/done invoices per table |
Legacy / gateway paths (for migration reference):
| Legacy / external | Invoice service |
|---|---|
GET /dine-orders |
GET /v1/dine-orders |
Authentication
Authorization: Bearer <jwt>
| Claim | Aliases | Required |
|---|---|---|
customer_id |
customerId |
yes |
location_id |
locationId |
yes |
Missing claims → 400 Authentication must include customer_id and location_id.
All data is scoped to the token’s customer_id + location_id.
GET /v1/dine-orders
Returns a plain JSON array of table objects (no pagination wrapper). Empty array when no tables exist for the tenant location.
Implementation: dineOrdersController.getDineOrders → dineOrdersService.getDineOrders → TableRepository.getDineTables + InvoiceRepository.getDineInvoicesByTableIds → mapInvoiceToSyncPayloadShape.
Query parameters
| Param | Type | Default | Description |
|---|---|---|---|
table_id |
number | — | Filter to a single floor-plan table (tables.id) |
brand_id |
number | — | Filter invoices by invoices.brand_id |
app_version |
string | — | Client version; affects items[].qty numeric type in response |
build_number |
number | string | — | Used with app_version for qty typing |
icon_pixel_size |
number | 24 |
1–512; width/height for order_type_icon URL |
icon_format |
string | png |
png, jpg, jpeg, webp, gif |
icon_url_format |
string | — | Alias of icon_format |
Invalid query values → 400 with Joi validation message.
Selection rules
Tables included when:
tables.customer_id+tables.location_idmatch JWT- Optional
table_idquery filter - Sorted by
tables.idascending
Invoices loaded per table when:
invoices.customer_id+invoices.location_idmatch JWTinvoices.table_idmatches a returned tableinvoices.statusinopen,done- Optional
brand_idquery filter - Sorted by
created_atdescending (newest first per table) - Line items: only
is_deleted = 0rows are loaded
Inclusion in dinerOrderDetail:
- Only invoices with at least one non-deleted line item are included
- Invoices with no items are skipped (not added to
dinerOrderDetailoruuid)
dinerOrderDetail values:
| Condition | dinerOrderDetail |
|---|---|
| No open/done invoices for table | null |
| Invoices exist but none have items | [] (empty array) |
| One or more invoices with items | Array of sync-shaped invoice objects |
plate_number on table row: taken from the latest open/done invoice for that table (created_at DESC), not from tables.plate_number.
Example request
GET /v1/dine-orders?table_id=5935&brand_id=412&icon_pixel_size=32&icon_format=png
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Response
HTTP status: 200 OK
Body: dineOrdersListResponseSchema — array of dineTableResponseSchema.
[
{
"floor_id": 606,
"id": 5935,
"name": "Table A5",
"customer_id": 437,
"location_id": 907,
"x": 72,
"y": 49,
"is_rounded": 0,
"rotation": 0,
"height": 30,
"width": 30,
"people_size": 0,
"attended_by": null,
"unavailable": 1,
"is_plate": 1,
"plate_number": null,
"uuid": [
"AA792674-95CA-4C2E-AED4-BDB68314CA83"
],
"dinerOrderDetail": [
{
"uuid": "AA792674-95CA-4C2E-AED4-BDB68314CA83",
"status": "open",
"customer_id": 437,
"location_id": 907,
"order_no": 2,
"customer_invoice_id": 234972,
"table_id": 5935,
"order_type": "dine-in",
"order_type_icon_key": "",
"order_type_icon": null,
"payments": null,
"items": [
{
"name": "Mocha",
"product_id": 257508,
"qty": 1,
"price": 10,
"subtotal": 10,
"modifiers": [],
"addons": null
}
]
// … remaining sync payload fields — see INVOICE_REQUEST.md
}
]
}
]
Table-level response fields
| Field | Type | Description |
|---|---|---|
floor_id |
number | null | Floor plan reference (tables.floor_id) |
id |
number | Table primary key |
name |
string | null | Table display name |
customer_id |
number | Tenant customer |
location_id |
number | Tenant location |
x, y |
number | null | Floor-plan coordinates |
is_rounded |
0 | 1 | null |
Rounded table shape flag |
rotation |
number | null | Rotation in degrees |
height, width |
number | null | Table dimensions on floor plan |
people_size |
number | null | Configured people size |
attended_by |
string | null | Attendant identifier / name |
unavailable |
0 | 1 | null |
Table unavailable flag |
is_plate |
0 | 1 | null |
Plate-order table flag |
plate_number |
string | null | From latest open/done invoice on this table |
uuid |
string[] | Invoice UUIDs included in dinerOrderDetail |
dinerOrderDetail |
array | null | Sync-shaped invoices (see below) |
Prod tables columns such as x_pos, y_pos, guest_name, current_status, capacity, and qr_code_link are not returned by this endpoint (legacy-compatible subset only).
Invoice objects in dinerOrderDetail
Each element is syncInvoicePayloadSchema plus optional order_type_icon — identical rules to KDS_API.md:
| Concern | Rule |
|---|---|
Timestamps (created_at, updated_at, paid_at, done_at, item timestamps) |
Unix seconds (number) |
| Boolean flags on invoice/items | 0 / 1 numbers |
payments |
Array of { payment_method, amount, tip_amount } or null |
items |
Parent lines only; addons nested; modifiers flat array |
notes |
String array parsed from DB order_notes (legacy iOS / invoiceSyncV3 key) |
order_type_icon |
Raster URL string or null (from order_type_icon_key + icon query params) |
Full field list: INVOICE_REQUEST.md.
Database tables used
| Table | Role |
|---|---|
tables |
Floor-plan table layout and metadata |
invoices |
Open/done orders linked via table_id |
invoice_items |
Line items (non-deleted only) |
payments |
Payment rows joined by invoice uuid |
floors is not joined; only tables.floor_id is returned on each table object.
Validation schemas (frontend contract)
Exported from src/validations/dineOrders.validation.js:
| Export | Purpose |
|---|---|
getDineOrders |
Request validator for GET /v1/dine-orders query |
dineOrdersListResponseSchema |
Full list response body |
dineTableResponseSchema |
Single table wrapper object |
dineOrderInvoiceSchema |
Single invoice inside dinerOrderDetail (syncInvoicePayloadSchema + order_type_icon) |
Base invoice read shape: syncInvoicePayloadSchema in src/validations/invoice.validation.js.
Related code
| Piece | Path |
|---|---|
| Routes | src/routes/v1/dineOrders.route.js |
| Controller | src/controllers/dineOrders.controller.js |
| Service | src/services/dineOrders.service.js |
| Table repository | src/data/repositories/TableRepository.js |
| Invoice repository | src/data/repositories/InvoiceRepository.js → getDineInvoicesByTableIds |
| Payment repository | src/data/repositories/PaymentRepository.js → getPaymentsByUuids |
| Models | src/models/sequelize/Table.js, src/models/sequelize/Floor.js |
| Response mapper | src/utils/invoice-sync-response-mapper.js → mapInvoiceToSyncPayloadShape |
| Icon / qty helpers | src/utils/kdsResponseHelpers.js |
| Validation | src/validations/dineOrders.validation.js |
Errors
| Case | HTTP | Body |
|---|---|---|
| Invalid / missing JWT | 401 |
Standard auth error |
Missing customer_id / location_id in token |
400 |
Authentication must include customer_id and location_id |
| Invalid query (Joi) | 400 |
Validation message from error handler |
| Success — no tables | 200 |
[] |
| Success — tables found | 200 |
Array of table objects |
Differences from legacy GET /dine-orders
The table wrapper shape matches the legacy POS endpoint. Invoice objects inside dinerOrderDetail intentionally use the invoiceSync read shape (same as KDS/history), not the legacy v3 format:
| Legacy v3 | This service |
|---|---|
order_items |
items |
payment_method: [{ "cash": 50 }] |
payments: [{ payment_method, amount, tip_amount }] |
modifier (string/JSON) |
modifiers (flat array) |
| Flat addons / combo JSON subquery | Nested addons; combo via is_combo / combo_item_id |
notes (parsed from order_notes) |
notes (string array) |
Product image, type on each item |
Not included (sync shape) |