by Intelliverse X

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"

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).

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.