README docs frontend-handout/01-AUTH-AND-CONVENTIONS.md

01 — Auth, conventions, errors, health

For Android / iOS clients integrating invoice-services.


Base URL

Mount point for all versioned APIs:

{INVOICE_SERVICE_BASE_URL}/v1/...

Examples (environment-specific — use your deployment config):

https://<invoice-host>/v1/invoices/sync
https://<invoice-host>/v1/kds-orders

Gateway may already rewrite legacy /api/v2/... paths to these; prefer calling the invoice service paths listed in this pack.

Body size limit: JSON body up to 25 MB (express.json limit).


Authentication (all business endpoints)

Authorization: Bearer <jwt>
Content-Type: application/json

Required JWT claims

Claim Also accepted Required Notes
customer_id customerId yes Tenant customer
location_id locationId yes Tenant location

If either is missing → HTTP 400 with message:

Authentication must include customer_id and location_id

Missing / invalid / expired JWT → HTTP 401.

All list/get/cancel queries are scoped to the token’s customer_id + location_id. Sending another tenant id in the body for sync must align with your business rules; history/KDS/dine/cancel ignore query-string tenant overrides and use the token only.

Useful optional claims (cancel audit)

Claim Use
username or user Appended to cancel note (**canceled by:{username})
id or user_id Default close_by_user_id when cancelling an open invoice

Common conventions

Enums (exact strings)

Field Values
Invoice status "open", "cancelled", "completed"
Line-item discount_type "percentage", "amount"

Boolean / flag fields

Send and expect numbers:

Value Meaning
0 false / off
1 true / on
omitted usually defaults to 0 (server Joi)

Exceptions:

Field Notes
is_syspos default 1
is_combo, is_internal may be 0 / 1 / null (is_combo also allows 2 on write via level2BooleanFlag)
is_accepted (reads / KDS) 0 pending, 1 accepted, 2 rejected

Timestamps

Context Format
Sync request / history / KDS / dine invoice fields Unix seconds (number)
Ably status event updated_at, done_at ISO-8601 strings

Notes (iOS / legacy TRACK)

Client key DB column Read key
notes (array of strings) preferred order_notes notes
order_notes (alias) same

If both notes and order_notes are sent on sync, notes wins.

Payments (preferred shape)

[
  {
    "payment_method": "cash",
    "amount": 105,
    "tip_amount": 0
  }
]

Avoid legacy payment_method object maps for new code. History/KDS/dine always return payments as above (or null).

Line items: addons & modifiers

Concept How to send / receive
Addons Nested under parent items[].addons[]; server flattens to DB rows with is_addon = 1
Modifiers Flat array on the line: items[].modifiers[] (not nested items groups)
Parent linked_uuid Required for correct addon re-nesting on history/KDS/dine

Extra JSON keys

Invoice and line-item objects allow unknown keys (.unknown(true)). Prefer documented fields; unknown keys may be ignored or persisted depending on refactor/DB mapping.

App metadata (recommended on sync)

Field Example
os_type "android" / "ios"
app_version "1.6.0"
build_number number
app_os_version OS version string

KDS / dine-orders also accept app_version + build_number as query params to control whether items[].qty is returned as int vs float (legacy iOS compatibility around app 1.5.5).


Response envelopes (know the differences)

Endpoint Success shape
POST /sync { status: true, code: 200, message, data: [...], trace_id }
GET /history { status: true, message, data: { current_page, records_per_page, total_records, orders } }
PUT /cancel { status: true, message, data: { uuid, status, … } }
GET /kds-orders raw array of invoices
PUT /kds-orders/:uuid { status: true, message }
GET /dine-orders raw array of table objects
GET /invoices/:uuid raw invoice row (no sync mapper — see 03)

Do not assume every endpoint uses the same wrapper.


Error envelope

Global handler returns roughly:

{
  "success": false,
  "code": 400,
  "error_code": "DUPLICATE_REQUEST",
  "service_name": "",
  "message": "…"
}
Field Notes
success Always false on errors
code HTTP status
error_code Optional; sync duplicates may use DUPLICATE_REQUEST on the invoice object in data, not always in this envelope
message Human-readable; validation joins Joi messages with ", "
stack Only in development

Common HTTP codes

Code When
400 Validation, missing JWT claims, missing uuid / request_id on sync
401 Auth failure
404 Invoice not found for tenant (cancel / get-by-uuid)
500 Unexpected server error

Idempotency headers (optional logging)

On sync, the service may log:

  • Header x-request-id (normalized lowercase) — correlation, separate from body request_id
  • Response trace_id on sync success

Body field request_id (per invoice) is the business idempotency key — required. See 02-INVOICE-SYNC.md.


Realtime (KDS)

Subscribe with Ably:

Channel order:{invoiceUuid}
Event status

Payload:

{
  "status": "pending" | "preparing" | "ready" | "cancelled",
  "is_accepted": 0 | 1 | 2,
  "done_at": null | "2026-06-16T12:34:56.789Z",
  "updated_at": "2026-06-16T12:34:56.789Z"
}

Derived status:

Condition status
is_accepted === 2 cancelled
done_at set ready
is_accepted === 1 preparing
else pending

Published after successful KDS PUT when ABLY_PUBLISH_KEY is configured. Details: 04-KDS.md.


Health checks (no auth)

GET /health
→ { "message": "Invoice service is running" }

GET /v1/health
GET /v1/health/detailed

Use for connectivity / load-balancer probes only.


Platform checklist

Android

  • OkHttp/Retrofit base URL ends before /v1 or includes it consistently
  • Gson/Moshi: map flags as Int/Integer (0/1), not Boolean
  • Timestamps as Long seconds
  • Sync body = List<Invoice> (JSON array root, not { "invoices": [...] })
  • Keep local UUID + request_id stable across retries

iOS

  • Decode flags as Int (0/1)
  • Prefer notes: [String] on sync (maps to DB order_notes, returns as notes)
  • Sync body must be a top-level JSON array
  • Do not treat sync 200 as “row fully committed”; refresh from history/KDS if needed
  • Pass app_version / build_number on KDS/dine if qty typing matters for your build

Scope reminder

This service targets master catalogues: modifiers + addons. Non-master extras/materials flows are out of scope.