Authentication
Every request (except /health, /sandbox/readiness, and the sign-in
endpoint itself) must be authenticated. Four interchangeable methods resolve
to the same principal with scopes and a rate limit.
Scopes
| Scope | Access |
|---|---|
machines:read |
List and inspect machines |
machines:write |
Update machine name, notes, location |
inventory:read |
Read aisle stock levels |
inventory:write |
Update aisles, trigger restocks |
orders:read |
Query sales orders |
orders:write |
Refund / fulfil / otherwise mutate orders |
commands:read |
Poll / inspect remote dispense commands |
commands:write |
Enqueue and ack remote dispense commands |
ads:read |
Read ad campaigns and proof-of-play |
ads:write |
Create and update ad campaigns |
machines:build |
Factory only: write build/QA/shipping and take the factory sign-off. Not a flavour of machines:write. |
accounts:admin |
Create operator accounts, audit log, sandbox reset, cross-tenant money |
A credential lacking the required scope receives 403 Insufficient scope.
Who holds which scopes, how the operator picker works, and who can see manufacturer shipping is in rbac.md.
Method 0 — Intelliverse sign-in (what the operator app uses)
POST /api/v1/auth/login takes an email + password. A correct password does
not return a fleet Bearer token. It returns mfaRequired (enroll or
totp) and a short-lived mfaTicket. Start first-time TOTP
enrollment by calling POST /api/v1/auth/mfa/enroll/start with mfaTicket,
adding the returned TOTP secret to an authenticator, then calling
POST /api/v1/auth/mfa/enroll/confirm with mfaTicket and code. For an
already enrolled account, use POST /api/v1/auth/mfa/verify; a recovery code
uses POST /api/v1/auth/mfa/recovery. Successful confirmation or verification
returns the fleet token. Credentials are verified against the real Intelliverse identity
service (user.intelli-verse-x.ai); the seeded sandbox operators also
accept the demo password (KioskX-Demo-2026 by default, override with
KIOSKX_DEMO_PASSWORD). The demo password is not an MFA bypass.
# Password success returns mfaRequired + mfaTicket — never a fleet token.
# POST /api/v1/auth/mfa/verify (or enroll/confirm, or recovery) mints TOKEN.
curl -s -X POST https://api.kiosk-x.ai/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"admin@superiorvapevending.com","password":"KioskX-Demo-2026"}'
curl https://api.kiosk-x.ai/api/v1/machines \
-H "Authorization: Bearer $TOKEN"
- A first-time sign-in from a real intelli-verse-x.ai account creates the operator with an empty fleet, ready to register machines.
admin@intelli-verse-x.ai(demo password) signs in as fleet-wide admin.- Sessions last 30 days (
KIOSKX_LOGIN_TTL); check yours withGET /api/v1/auth/me.
Method 1 — API key (partner keys)
curl https://api.kiosk-x.ai/api/v1/orders \
-H "X-API-Key: vtm_e08725ea09876af65f431c6a3742d7c0"
| Key | Operator | Scopes | Rate limit |
|---|---|---|---|
vtm_e08725ea09876af65f431c6a3742d7c0 |
admin@superiorvapevending.com | machines+inventory+commands read/write, orders:read | 1,000/hr |
vtm_66eef98855b77f6d9a8f4fb5aaa70da5 |
akashsinghania2022@gmail.com | machines+inventory+commands read/write, orders:read | 1,000/hr |
vtm_kioskx_admin_5f2a9c1d8e7b4a3f |
(admin — all operators) | all scopes | 5,000/hr |
Partner keys are long-lived machine credentials for server-to-server integrations; interactive clients (the operator APK and console) use the Intelliverse sign-in above instead.
Method 2 — OAuth 2.0 client credentials (sandbox)
Exchange a client id/secret for a bearer token, exactly like Cognito's
/oauth2/token:
curl -X POST https://api.kiosk-x.ai/oauth/token \
-d grant_type=client_credentials \
-d client_id=kiosk-x-sandbox-superior \
-d client_secret=kx_secret_superior_0a1b2c3d4e5f6a7b
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "machines:read machines:write inventory:read inventory:write orders:read"
}
Then:
curl https://api.kiosk-x.ai/api/v1/machines \
-H "Authorization: Bearer $ACCESS_TOKEN"
| Client ID | Secret | Operator |
|---|---|---|
kiosk-x-sandbox-superior |
kx_secret_superior_0a1b2c3d4e5f6a7b |
admin@superiorvapevending.com |
kiosk-x-sandbox-akash |
kx_secret_akash_8c9d0e1f2a3b4c5d |
akashsinghania2022@gmail.com |
kiosk-x-sandbox-admin |
kx_secret_admin_6e7f8a9b0c1d2e3f |
admin (all scopes) |
HTTP Basic is also accepted: -u client_id:client_secret.
Optional scope form field narrows the granted scopes (subset only, else 403 invalid_scope).
Method 3 — Intelliverse Cognito JWT
Bearer tokens issued by the Intelliverse Cognito user pool
(us-east-1_M5qxN8b74, issuer https://cognito-idp.us-east-1.amazonaws.com/us-east-1_M5qxN8b74)
are validated against the pool's JWKS (RS256, issuer + expiry checked).
- Regular users get read access (
machines:read inventory:read orders:read) across the sandbox. - Users in the
admin/Admins/super_adminCognito group get all scopes. - Machine-to-machine (client-credentials) tokens whose Cognito resource-server
scope ends in
/all(e.g.yourapi/all) get all scopes.
TOKEN=$(curl -s -X POST https://aicartx.auth.us-east-1.amazoncognito.com/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-u "$COGNITO_CLIENT_ID:$COGNITO_CLIENT_SECRET" \
-d "grant_type=client_credentials" | jq -r .access_token)
curl https://api.kiosk-x.ai/api/v1/machines \
-H "Authorization: Bearer $TOKEN"
Rate limiting
Each credential has an hourly request budget (fixed window). Exceeding it returns:
{"code": 429, "message": "Rate limit exceeded", "data": null}
with a Retry-After: 3600 header.