Public kiosk API
The public API is the set of endpoints used by kiosk tablets themselves. These are unauthenticated in the traditional sense — there's no user login — but they're identified by a device_id burned into the tablet's localStorage at pair time and passed as a header on every request. The base URL is https://api.yumkiosk.com, which rewrites internally to https://agent.yumkiosk.com/api/public.
Authentication
Every request must include:
X-Device-Id: <uuid-v4>
X-Device-Token: <long-lived-token>
The token is issued during the pairing handshake and is valid until the kiosk is unpaired. If the token is missing or invalid, the server returns 401 Unauthorized.
POST /sessions/start
Start a new session from the kiosk. Called the moment the customer taps Tap to order.
POST /sessions/start
Content-Type: application/json
{
"device_id": "b7e2f1a3-...",
"customer_locale": "en-US"
}
Response:
{
"session_id": "ses_8f2c1a",
"status": "pending",
"created_at": "2026-04-10T14:22:00Z",
"poll_url": "/sessions/ses_8f2c1a/status"
}
The session is immediately broadcast to all eligible agents over WebSocket. The kiosk should begin polling poll_url every 2 seconds (or listen on WebSocket if supported) until the session transitions from pending to active.
GET /sessions/{id}/status
Polls the current state of a session. Used as a WebSocket fallback.
GET /sessions/ses_8f2c1a/status
Response:
{
"session_id": "ses_8f2c1a",
"status": "active",
"agent": {
"id": "agt_42",
"name": "Maria R.",
"avatar_url": "https://.../avatars/42.jpg"
},
"agora_channel": "ses_8f2c1a",
"created_at": "2026-04-10T14:22:00Z",
"accepted_at": "2026-04-10T14:22:04Z"
}
Possible status values: pending, active, payment, completed, abandoned, failed.
GET /sessions/{id}/order
Fetches the current cart for an active session. The kiosk polls this (or subscribes via WebSocket) to render the cart UI as the agent builds it.
GET /sessions/ses_8f2c1a/order
Response:
{
"session_id": "ses_8f2c1a",
"currency": "USD",
"subtotal_cents": 1450,
"tax_cents": 116,
"total_cents": 1566,
"lines": [
{
"id": "line_1",
"menu_item_id": "mi_burger",
"name": "Cheeseburger",
"quantity": 1,
"unit_price_cents": 899,
"modifiers": [
{ "group": "Doneness", "value": "Medium" }
],
"notes": "no onions"
},
{
"id": "line_2",
"menu_item_id": "mi_fries",
"name": "Large Fries",
"quantity": 1,
"unit_price_cents": 551,
"modifiers": []
}
],
"updated_at": "2026-04-10T14:24:10Z"
}
GET /agora/token
Fetches a signed Agora token for joining the video channel. Called by the kiosk right after a session transitions to active.
GET /agora/token?channel=ses_8f2c1a&role=publisher
Response:
{
"app_id": "abc123...",
"token": "007eJxTYD...",
"channel": "ses_8f2c1a",
"uid": 2147483647,
"expires_at": "2026-04-10T15:22:00Z"
}
The token is valid for the duration of the session plus a small grace period. If the session runs long, the kiosk should re-fetch the token before expiry.
POST /kiosks/pair
Trade a 6-digit pairing code for a long-lived device token. Called exactly once per kiosk lifecycle.
POST /kiosks/pair
Content-Type: application/json
{
"code": "482913",
"device_id": "b7e2f1a3-...",
"device_name": "Samsung Tab A9+ (Front counter)"
}
Response:
{
"kiosk_id": "kio_12",
"device_token": "ey...",
"business": {
"name": "Joe's Tacos",
"logo_url": "https://.../logos/joes.png"
},
"settings": {
"primary_color": "#E23636",
"attract_message": "Welcome to Joe's Tacos"
}
}
Store device_token securely — it's the credential for all future API calls from this tablet.
Error responses
All errors follow the standard shape:
{
"error": {
"code": "PAIRING_CODE_EXPIRED",
"message": "The pairing code has expired. Generate a new one.",
"request_id": "req_abc123"
}
}
Common codes: DEVICE_NOT_FOUND, DEVICE_TOKEN_INVALID, SESSION_NOT_FOUND, SESSION_ALREADY_ENDED, PAIRING_CODE_EXPIRED, PAIRING_CODE_ALREADY_USED.