How Kiosk-X works — the whole picture
This is the "explain it like I'm five" map of the whole system, with every real URL, endpoint, and message kept in so engineers can use it too.
The simple story. A vending machine is like a little shop. The cloud
(api.kiosk-x.ai) is the shop's brain — it remembers every machine, every
snack, every sale. The operator app is the owner's magic remote: they can
see money coming in and refill machines from their phone. And MQTT
(mq.kiosk-x.ai) is a walkie-talkie so the brain and the machines can whisper to
each other instantly. Everything is ours — no other company's servers are in the
loop anymore.
The big map
com.ruiye.jd"] VMC["Motor board (VMC)
via serial cable"] Kiosk <-->|dispense| VMC end subgraph Owner["Operator (owner)"] OpApk["Operator APK
(native Android)"] OpWeb["operator.kiosk-x.ai
(same thing, in a browser)"] end subgraph Cloud["Our cloud — api.kiosk-x.ai"] Partner["Partner API
/api/v1/*
(for people)"] Device["Device API
/apk/*
(for machines)"] Docs["Docs
/guides /docs /redoc"] end MQ(["MQTT broker
mq.kiosk-x.ai:1883"]) Nayax(["Nayax
card reader + settlement"]) S3(["S3 downloads / OTA
APK files"]) Kiosk -->|"HTTP: getEquipment, getGoods, createOrder"| Device Kiosk <-->|"presence + remote dispense + OTA"| MQ Kiosk -->|checks for new version| S3 VMC -. card tap .-> Nayax Nayax -->|webhook| Partner OpApk -->|"HTTPS + token"| Partner OpWeb -->|"HTTPS + token"| Partner Partner -->|remote dispense / push OTA| MQ MQ -->|command| Kiosk
Every address (URLs)
| What it is | URL | Who talks to it |
|---|---|---|
| Cloud brain (API) | https://api.kiosk-x.ai |
everyone |
| Partner API (for people/apps) | https://api.kiosk-x.ai/api/v1/* |
operator app, web console, integrations |
| Device API (for machines) | https://api.kiosk-x.ai/apk/* |
the Kiosk X vending APK |
| Operator web console | https://operator.kiosk-x.ai |
operators in a browser |
| MQTT walkie-talkie | tcp://mq.kiosk-x.ai:1883 |
machines ⇄ cloud (instant push) |
| Live docs | https://api.kiosk-x.ai/guides · /docs (Swagger) · /redoc · /openapi.json |
anyone |
| Docs mirror (static) | http://kiosk-x-docs.s3-website-us-east-1.amazonaws.com |
anyone |
| App downloads (APKs) | /downloads — ZHZN 1.0.26, Reyeah 1.2.7, content-addressed on media.intelli-verse-x.ai |
installers / OTA |
| Readiness self-test | https://api.kiosk-x.ai/sandbox/readiness |
ops / monitoring |
| Legacy aliases (still work) | kiosk-x.intelli-verse-x.ai · hotbox-api.intelli-verse-x.ai |
old links |
One host, two "doors":
api.kiosk-x.ai/api/v1/*is the people door andapi.kiosk-x.ai/apk/*is the machine door. They share the same brain and data but speak slightly different languages (see below).
What's inside the cloud
The cloud has two API "doors" plus the docs.
Door 1 — Partner API (/api/v1/*) — for people and their apps
Uses normal REST: HTTP status codes (200 OK, 401 unauthorized, 404 not found).
| Endpoint | What it does |
|---|---|
POST /api/v1/auth/login |
Sign in with email + password (checked against Intelliverse identity), get a fleet-scoped bearer token |
GET /api/v1/machines |
List the operator's machines (status, location, reported APK version) |
POST /api/v1/machines/register |
Add a new machine — or claim one that auto-registered itself |
GET /api/v1/machines/{no} |
One machine's detail |
GET /api/v1/inventory?machineNos=… |
Aisle-level stock across machines |
GET /api/v1/inventory/machines/{no} |
Aisle-level stock for one machine |
POST /api/v1/inventory/machines/{no}/restock |
Refill aisles after a route stop |
GET /api/v1/orders · /orders/{id} |
Sales history and detail |
GET /api/v1/orders/summary |
Revenue + order totals for the dashboard |
GET /api/v1/pnl |
Operator P&L (gross − tax − FIFO − host − …) |
POST /api/v1/crm/venues |
Host venue + commission deal — name machineNos at create if the cabinet has already sold |
POST /api/v1/purchases |
Receive stock; this is what creates the FIFO lots the P&L consumes |
GET /api/v1/tax/schedules |
Sales-tax rate schedule |
POST /api/v1/machines/{no}/commands/dispense |
Remote "drop this aisle" (cloud → machine) |
GET /api/v1/accounts (admin) |
Operator accounts |
POST /api/v1/rollouts |
Start a staged OTA rollout (see OTA section) |
POST /api/v1/payments/nayax/webhook |
Nayax posts card transactions here (HMAC-signed) |
Door 2 — Device API (/apk/*) — for the vending machines
This mirrors the original kiosk firmware's protocol. It uses a small envelope, not HTTP status codes:
{ "code": 0, "message": "ok", "data": { } }
code: 0 = success, code: 1 = failure. The machine's software checks code.
| Endpoint | When the machine calls it |
|---|---|
GET /apk/getEquipment |
On boot — "who am I, what's my grid?" (auto-provisions unknown boards) |
GET /apk/getStyle |
On boot — screen theme/layout |
GET /apk/getGoods |
On boot — the product list with prices + stock |
GET /apk/getAd |
Idle screen ads |
POST /apk/createOrder · createOrderCart |
A sale just happened — record it |
POST /apk/offlineCreateOrder* |
Flush sales made while the internet was down |
GET /apk/getUpgradeVersion |
"Is there a newer APK for me?" (OTA) |
POST /apk/apkUp |
"I'm now running version X" (adoption tracking) |
POST /apk/ordersUpdate, submitFault, uploadLog, … |
Status + telemetry (accepted and acknowledged) |
Every other
/apk/*call the firmware might make is answered with a friendly success so the machine never freezes on an unknown request.
Signing in (auth)
- Operators →
POST /api/v1/auth/login(email + password) → bearer token, or a long-livedX-API-Key. Login is wired to Intelliverse identity (intelli-verse-x.ai). - Machines → identify themselves with an
equipmentNoheader; the cloud pins a session cookie atgetEquipmenttime. - Integrations → OAuth 2.0 client credentials at
POST /oauth/token.
The vending app (kiosk) — end to end
The current Reyeah build (reyeah-vending-kioskx-1.2.7-aaa3d485cbaa.apk on /downloads) runs on the machine's Android tablet
(Rockchip RK3288, Android 7.1.2). Here's one full customer purchase:
Offline is fine. If the internet drops, the machine still sells (payment +
motor are local) and queues the sales, then sends them later via
/apk/offlineCreateOrder.
The operator app — end to end
The kiosk-x-operator.apk is a native Android app (same thing lives at
operator.kiosk-x.ai in a browser). It only ever talks to the Partner API.
MQTT — the instant walkie-talkie
HTTP polling is the reliable backbone; MQTT is the fast extra channel for
things that shouldn't wait for the next poll. It runs entirely on our broker,
mq.kiosk-x.ai:1883. If the broker is unreachable, nothing breaks — the
machine keeps vending over HTTP.
{equipmentNo, state}"| B1 B2 -->|"cloud → device"| S1
| Direction | Topic | Message | Meaning |
|---|---|---|---|
| device → cloud | presence topic | {equipmentNo, state} |
machine online; a Last-Will marks it offline if it drops |
| cloud → device | <equipmentNo> |
{type:"1", orderNumber, aisleNo} |
remote dispense |
| cloud → device | <equipmentNo> |
{type:"2", url, version} |
push an APK install (OTA) |
| cloud → device | <equipmentNo> |
{type:"3", …} |
install/update the VMC firmware |
Payments & Nayax — where the money goes
method, amount, terminal, txn id"] Order --> OpApp["Operator app shows the sale"]
Each operator has their own Nayax merchant account, and each machine is bound to a terminal, so money always lands in the right place and the matching sale shows up in that operator's app.
OTA rollouts — updating machines safely
New APKs are built + signed in CI, stored on S3, and rolled out gradually so a bad build can't brick the whole fleet at once.
One-time migration note: a machine already running the old vendor build must
be uninstalled first (adb uninstall com.ruiye.jd) because our signing key is
different. After that, all future updates apply in place automatically.
The full lifecycle, in one line each
- Install — flash the current Reyeah or ZHZN build from
/downloads(today Reyeah 1.2.7 / ZHZN 1.0.26, content-addressed; never the mutablereyeah-vending-kioskx.apkkey) on the tablet (armeabi-v7a, API 25+). - First boot —
getEquipmentauto-provisions the board into an "unclaimed" pool, fully stocked. - Claim — operator runs
POST /api/v1/machines/registerto take ownership (orders + inventory move to them). - Sell — customer taps + pays (Nayax), motor drops via serial,
createOrderrecords it. - Refill — operator sees low aisles in the app and calls
restockafter a route stop. - Update — CI ships a new signed APK; staged OTA rolls it out and tracks adoption.
- Watch — MQTT presence +
/sandbox/readinessshow what's online and healthy.
Everything above is our own stack end to end — one cloud (api.kiosk-x.ai), one
broker (mq.kiosk-x.ai), our own signed apps.