Browse docs
API · /auth

Authentication endpoints

Sign up, log in, refresh tokens, and redeem invite codes. The four endpoints every Frontelio client needs to know.

All authentication endpoints live under /auth. They are public by design — they don't require an existing token, because they're how clients get one in the first place. Each endpoint has a per-IP rate limit to deter brute-force and abuse.

MethodPathAuthDescription
POST/auth/loginPublic · 10/min/IPExchange email + password for a JWT pair.
POST/auth/refreshPublic · 30/min/IPTrade an expiring refresh token for a fresh JWT pair.
POST/auth/signupPublic · 5/min/IPCreate a new tenant + owner. Optional invite code for sibling-tenant signups.
POST/auth/invite/:code/redeemPublic · 5/5min/IPRedeem a magic-link invite to join an existing tenant.
GET/auth/meBearer JWTReturns the authenticated user's profile + permissions.
POST/auth/switch-tenantBearer JWTIssue a fresh JWT for a different tenant the user belongs to.
GET/auth/membershipsBearer JWTList every tenant the caller has access to.
PATCH/auth/me/passwordBearer JWT · 10/min/IPSelf-service password change. Requires the current password.

POST /auth/login

Exchange a user identifier and password for a JWT pair. The identifier can be the user's email or their linkedIdentityKey for cross-tenant accounts. Throttled at 10/min/IP for brute-force defence.

Request

POST /auth/login
{
  "identifier": "owner@cafejoud.ae",
  "password": "••••••••"
}

Response · 200 OK

200 OK
{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
  "user": {
    "id": "user_abc123",
    "email": "owner@cafejoud.ae",
    "fullName": "Mariam Al-Hashimi",
    "roles": ["TENANT_OWNER"],
    "tenantId": "tenant_xyz"
  }
}

Token lifetimes: accessToken is valid for 15 minutes; refreshToken is valid for 30 days. Persist both client-side — the access token in memory or a short-TTL store, the refresh token in secure storage (Keychain on iOS, Keystore on Android, an HttpOnly cookie or IndexedDB on web).

POST /auth/refresh

When the 15-minute access token expires (401 from any authenticated endpoint), exchange the refresh token for a fresh pair. Refresh tokens are single-use — each refresh issues a new refresh token alongside the new access token, and the old refresh token is revoked.

Request

POST /auth/refresh
{
  "refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}

Response · 200 OK

200 OK
{
  "accessToken": "eyJhbGciOiJIUzI1NiIs... (new)",
  "refreshToken": "eyJhbGciOiJIUzI1NiIs... (new)"
}

POST /auth/signup

Create a new tenant. There are two paths through the same endpoint, distinguished by whether the request includes a code field:

  • Self-service signup (no code) — anyone can start a brand-new company from zero. Provisions a tenant + owner + full role/permission matrix + a default outlet, then logs them in.
  • Invite signup (code present) — consume an FCO-INV code, which can pre-parent the new tenant under an existing TenantGroup. Used to add a sibling company into a group you already own.

Either way the response logs the new owner in immediately. Throttled 5/min/IP to deter spray-signups and DB-pressure abuse.

Request

POST /auth/signup
{
  "tenantName": "Café Joud",
  "ownerEmail": "owner@cafejoud.ae",
  "ownerFullName": "Mariam Al-Hashimi",
  "password": "••••••••",
  "seedDemoData": true,
  "code": "FCO-INV-XYZ789"          // optional — sibling-tenant join
}

Response · 200 OK

200 OK
{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
  "user": { ... TENANT_OWNER user ... },
  "tenant": {
    "id": "tenant_abc",
    "name": "Café Joud",
    "createdAt": "2026-05-28T08:32:10.123Z"
  },
  "owner": { ... same as user, for convenience ... },
  "demoCredentials": [             // only present when seedDemoData=true
    { "email": "aisha.khan@demo.local", "role": "STAFF",   "password": "Demo2026!" },
    { "email": "omar.farouk@demo.local", "role": "OUTLET_MANAGER", "password": "Demo2026!" },
    ...
  ]
}

POST /auth/invite/:code/redeem

Magic-link onboarding for users joining an existing tenant. The 6-character invite code is generated either in the bulk-invite flow at /users → Bulk invite or as one of the printable QR codes in the onboarding wizard.

Request

POST /auth/invite/INV-A3F2X1/redeem
{
  "fullName": "Layla Hassan",
  "password": "••••••••",
  "phone": "+971501234567"      // optional
}

Response · 200 OK

Same shape as /auth/login — the new user is immediately authenticated and can call the rest of the API.

GET /auth/me

Returns the current user's profile, roles, and resolved permission set. Useful as a session-validity probe and to render role-aware UI.

GET /auth/me
Authorization: Bearer <accessToken>

200 OK
{
  "id": "user_abc",
  "email": "owner@cafejoud.ae",
  "fullName": "Mariam Al-Hashimi",
  "roles": ["TENANT_OWNER"],
  "permissions": ["tenant.manage", "user.invite", ... ],
  "tenant": {
    "id": "tenant_xyz",
    "name": "Café Joud",
    "planCode": "GROWTH"
  },
  "primaryOutletId": "outlet_main"
}

Multi-tenant tokens

A single user identity can belong to multiple tenants (managed by an owner with several companies, or a contractor working for more than one Frontelio customer). Each tenant gets its own access token, scoped to that tenant only.

  • GET /auth/memberships — list every tenant the caller has access to.
  • POST /auth/switch-tenant — issue a new JWT scoped to a different tenant. Body: { "tenantId": "tenant_other" }. Returns the same shape as /auth/login plus a switchedTo confirmation block.

Quick curl example

bash
# 1. Log in
ACCESS=$(curl -sX POST https://api.frontelio.com/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"identifier":"owner@cafejoud.ae","password":"YourPass1!"}' \
  | jq -r .accessToken)

# 2. Verify the token works
curl -sX GET https://api.frontelio.com/api/v1/auth/me \
  -H "Authorization: Bearer $ACCESS"

# 3. Refresh when the access token expires
curl -sX POST https://api.frontelio.com/api/v1/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{"refreshToken":"...your refresh token..."}'

Continue to the /access API reference for the Frontelio Access endpoints.