Mobile App Login (verify_user)

verify_user

Mobile-app user login/verification endpoint (v3.108; reworked v3.110 to the two-layer model). An ACCOUNT can have many USERS — so the call verifies the account first and the user on top:

  1. Account layer: the standard API authentication every endpoint uses — either the Inventory Manager token login (username + token + account, no header) or the legacy Authorization1 API key + username + password + account. See API Credentials for both modes.
  2. User layer: the user_token parameter must be an ACTIVE mobile-app token belonging to a user of the SAME verified account.

The CMS renders each user's QR code (Users → Mobile app tab, Employees editor → CMS user account panel, or the user's own Profile page) as one token string and nothing else — no domain, no account id, no JSON wrapper. The app already knows the server and the account from its configured API credentials; scanning the QR only identifies WHICH user is using it.

Request

GET or POST {base_url}/public_api/verify_user/

Parameter Type Required Description
account int yes API account ID (main admin id) — resolves the tenant
username + token string one auth mode Inventory Manager token login: a token row's username + token value (no header)
username + password + Authorization1 header string other auth mode Legacy login: API key in the Authorization1 header + API username + API password
user_token string yes The 40-character user token encoded in the user's QR code

Response

{
  "OUTPUT": {
    "response_type": "success",
    "user": {
      "admin_id": 123,
      "account_id": 45,
      "username": "kai",
      "email": "kai@jiahe.fi",
      "name": "Kai Wu",
      "user_type": "employee",
      "language_id": 1,
      "domain_name": "jiahe.fi",
      "permits": ["dashboard", "profile", "currency", "password", "ecommerce", "ordering", "orders_view_only", "order_picking"],
      "permits_map": { "dashboard": 1, "profile": 1, "currency": 1, "password": 1, "ecommerce": 1, "ordering": 1, "orders_view_only": 1, "order_picking": 1, "purchasing": 0 },
      "order_picking": 1,
      "token_created_at": "2026-09-25 12:00:00",
      "token_last_used_at": "2026-09-25 14:02:11"
    }
  },
  "INFO": { "start": 0, "limit": 1, "total_count": 1, "count": 1, "tip": "" }
}

Errors:

  • response_type: "error" with message: user_token_missing, invalid_token (unknown/revoked token, deleted or disabled user) or user_not_in_account (the token belongs to a user of a different account than the one the call authenticated as).
  • UN-AUTHORIZED … — the ACCOUNT layer failed (missing/incorrect API credentials), exactly like every other endpoint.

Permits

The user object carries the user's permissions twice:

  • permits — flat array of the granted permit names (stored value 1). Gate app features on membership in this list.
  • permits_map — the complete stored permit map: every permit key with its value (0 denied, 1 granted, 2 shown-but-locked in the CMS). Use it when you need to distinguish "locked" from plain "not permitted", or want the whole picture in one object.

order_picking is a convenience boolean derived from the same data.

Security notes

  • The account is verified before the user — a user token alone can never call this endpoint; the app must hold valid account-level API credentials.
  • Passwords are never selected or returned — the t_admin read behind this endpoint is a strict column whitelist (admin_pass is not part of it).
  • User tokens are 40 random characters from a 31-character alphabet (≈198 bits) — practical brute force is not feasible.
  • Regenerating the QR in the CMS revokes the previous token immediately (lost-phone kill switch). Disabling the CMS user (active_state=0) or deleting the user also invalidates the token.
  • The endpoint stamps last_used_at / last_ip on every successful call so admins can audit device usage from the QR tab.