Skip to content

Mobile App — Authentication

Authentication endpoints for the in-store staff/POS mobile app. These are not Directus user-token flows. Instead they issue a per-device, per-workspace mobile session consisting of a short-lived access token and a long-lived refresh token (JWTs minted by MobileAppAuthService).

There are two ways to obtain a mobile session:

  • Pairing code — a 6-digit code is generated by a workspace admin (see Users) and delivered to the device's operator, who exchanges it at POST /mobile-app/auth/pair.
  • Directus credentials — an existing workspace member logs in at POST /mobile-app/auth/login and receives one session profile for every workspace they belong to.

Token model (from MobileAppAuthService):

  • Access token — JWT, type: "access", expires in 15 minutes (expires_in: 900). Sent on every mobile-app runtime request in the X-Mobile-Token header.
  • Refresh token — JWT, type: "refresh", expires in 30 days. Exchanged at POST /mobile-app/auth/refresh for a new access/refresh pair.

The access token JWT encodes the mobile_app_user_id (sub), workspace_id, and session_id, so all downstream runtime calls (see Config & Runtime) are workspace-scoped without extra headers.

Auth summary per endpoint:

EndpointAuth
POST /mobile-app/auth/loginPublic (email + password in body)
POST /mobile-app/auth/pairPublic (pairing code in body; IP rate-limited)
POST /mobile-app/auth/refreshPublic (valid refresh token in body)
GET /mobile-app/meMobile session (X-Mobile-Token)
POST /mobile-app/auth/logoutMobile session (X-Mobile-Token)

No SDK methods. The @wallethero/sdk does not wrap the mobile-auth endpoints. Call these REST endpoints directly from the mobile client.


Login with Directus credentials

POST /wallethero-api/mobile-app/auth/login

Verifies a Directus user's email/password and provisions a mobile session for every workspace the user belongs to. Returns an array of profiles — one per workspace — each carrying its own access/refresh token pair. Suspended workspace memberships are skipped.

Auth: Public — credentials in the request body.

Request Body

FieldTypeRequiredDescription
emailstring (email)YesDirectus user email
passwordstringYesDirectus user password
device_idstringNoStable device identifier
device_namestringNoHuman-readable device name
device_osstringNoDevice OS string
push_tokenstringNoPush-notification token for the device

Example Request

bash
curl -X POST "https://api.wallethero.app/wallethero-api/mobile-app/auth/login" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "password": "hunter2",
    "device_id": "ipad-front-desk-01",
    "device_name": "Front Desk iPad",
    "device_os": "iPadOS 17"
  }'

Response (200)

json
{
  "data": [
    {
      "mobile_app_user_id": "9d1b...",
      "workspace_id": "11111111-1111-1111-1111-111111111111",
      "workspace_name": "Acme Loyalty",
      "access_token": "eyJhbGciOi...",
      "refresh_token": "eyJhbGciOi...",
      "expires_in": 900,
      "user": {
        "id": "9d1b...",
        "email": "[email protected]",
        "phone": null,
        "first_name": "Sam",
        "last_name": "Taylor",
        "full_name": "Sam Taylor"
      }
    }
  ]
}

Pair a device with a pairing code

POST /wallethero-api/mobile-app/auth/pair

Exchanges a 6-digit pairing code (generated by a workspace admin) for a mobile session. On success it marks the code used, flips the mobile app user to paired, opens a session row, and returns the access/refresh tokens plus the user record.

The endpoint is IP rate-limited: repeated failed attempts (invalid/expired code, too many tries) temporarily block the client IP. Codes also expire and have a per-code attempt cap.

Auth: Public — pairing code in the request body.

Request Body

FieldTypeRequiredDescription
codestringYes6-digit numeric pairing code (^\d{6}$)
device_idstringNoStable device identifier
device_namestringNoHuman-readable device name
device_osstringNoDevice OS string
push_tokenstringNoPush-notification token for the device

Example Request

bash
curl -X POST "https://api.wallethero.app/wallethero-api/mobile-app/auth/pair" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "482915",
    "device_id": "ipad-front-desk-01",
    "device_name": "Front Desk iPad",
    "device_os": "iPadOS 17"
  }'

Response (200)

json
{
  "access_token": "eyJhbGciOi...",
  "refresh_token": "eyJhbGciOi...",
  "expires_in": 900,
  "user": {
    "id": "9d1b...",
    "email": "[email protected]",
    "phone": null,
    "first_name": "Sam",
    "last_name": "Taylor",
    "workspace_id": "11111111-1111-1111-1111-111111111111"
  }
}

Errors

  • 400 — Invalid or expired pairing code, expired code, max attempts exceeded, or too many failed pairing attempts from this IP.

Refresh the access token

POST /wallethero-api/mobile-app/auth/refresh

Verifies a refresh-token JWT, validates the backing session (must exist, not be revoked, not expired) and that the user is still active, then issues a new access/refresh token pair and bumps the session's last_active_at.

Auth: Public — valid refresh token in the request body.

Request Body

FieldTypeRequiredDescription
refresh_tokenstringYesThe refresh-token JWT returned by login/pair/refresh

Example Request

bash
curl -X POST "https://api.wallethero.app/wallethero-api/mobile-app/auth/refresh" \
  -H "Content-Type: application/json" \
  -d '{ "refresh_token": "eyJhbGciOi..." }'

Response (200)

json
{
  "access_token": "eyJhbGciOi...",
  "refresh_token": "eyJhbGciOi...",
  "expires_in": 900
}

Errors

  • 400 — Invalid refresh token, invalid token type, session not found or revoked, session expired, or user not found/inactive.

Get current mobile user profile

GET /wallethero-api/mobile-app/me

Returns the profile of the mobile app user backing the current session, including their workspace summary.

Auth: Mobile session — X-Mobile-Token: <access_token>.

Example Request

bash
curl "https://api.wallethero.app/wallethero-api/mobile-app/me" \
  -H "X-Mobile-Token: ACCESS_TOKEN"

Response (200)

json
{
  "data": {
    "id": "9d1b...",
    "email": "[email protected]",
    "phone": null,
    "first_name": "Sam",
    "last_name": "Taylor",
    "status": "active",
    "paired_at": "2026-06-10T08:30:00.000Z",
    "last_login": "2026-06-16T09:00:00.000Z",
    "metadata": {},
    "workspace": {
      "id": "11111111-1111-1111-1111-111111111111",
      "name": "Acme Loyalty",
      "slug": "acme"
    }
  }
}

Logout

POST /wallethero-api/mobile-app/auth/logout

Revokes the current session (sets revoked_at). The access token's session_id is used, so only the calling device's session is invalidated.

Auth: Mobile session — X-Mobile-Token: <access_token>.

Example Request

bash
curl -X POST "https://api.wallethero.app/wallethero-api/mobile-app/auth/logout" \
  -H "X-Mobile-Token: ACCESS_TOKEN"

Response (200)

json
{ "message": "Logged out successfully" }

WalletHero Documentation