Skip to content

Admin Users API ​

Provisioning and management of administration users — the staff and platform-admin accounts that operate the admin portal.

Every /admin/users endpoint requires an authenticated super_admin (the owner) or admin session; the router is mounted behind a requireRole('super_admin', 'admin') guard, and any other role receives 403 AUTH_FORBIDDEN. An admin then needs the staff-users area: View for the list, Manage to provision. PATCH, DELETE and resend-invite are decided per target, see Access Rules. The acting identity and role are taken from the verified session, never from the request body.

Roles and Areas ​

The admin portal is partitioned into manageable areas. A user's access is derived from its appRole plus, for the admin role, an explicit set of assigned areas.

Roles ​

RoleAreasAssignable here
super_adminAll areas (owner)No — owners are not managed here
adminThe subset assigned via managedAreas (platform-admin)Yes
activation_officerofficer, customers (fixed)Yes
supportsupport, customers (fixed)Yes
b2b_adminb2b (fixed)No — company people are invited through POST /admin/companies/invites
end_userNoneNo

Roles assignable through this API are exactly the portal staff roles admin, activation_officer, and support. super_admin and end_user can never be assigned here, so an owner cannot mint another owner through this endpoint. b2b_admin cannot be assigned here either: a company person is provisioned only through the company invite, which also links them to their company.

Company people are still managed through PATCH, DELETE and resend-invite on /admin/users/:id, under two extra rules — see Managing company people. They are listed per company by GET /admin/companies/:id/users, and before registration by GET /admin/companies/pending-admins, not by GET /admin/users.

Areas ​

AreaPortal section
activation-recordsActivation records dashboard
packagesPackage catalog
officerActivation officer tools. Resending a delivery email needs Manage
supportCustomer support
b2bB2B company portal
notificationsNotification administration, gating the admin notification routes. No portal screen yet
customersCustomer search and record, gating the /admin/customers routes. Fixed for activation_officer and support, assignable to admin
storeStore order oversight, gating the admin store order routes. Assignable to admin only — no staff role holds it
identity-detailCustomer identity check detail, gating GET /admin/customers/:id/identity on top of the customers area. Assignable to admin only — no staff role holds it
auditAudit history viewer, gating GET /admin/audit-logs. Assignable to admin only — no staff role holds it. Values stay owner only

Three areas gate people management:

AreaPortal section
staff-usersStaff users screen and /admin/users. View lists staff, Manage provisions and edits them. Manage on it is owner-only to grant, and a user holding it can be edited or removed only by the owner
company-peopleCompany people: the per-company list and managing their accounts. Needs b2b
company-onboardingInvited company admins who have not registered a company yet. Needs b2b

activation-records, customers, identity-detail and audit are View-only: they are granted at View or not at all, and managedAreasWrite must not include them. A dependent area needs its parent in managedAreas: identity-detail needs customers, and company-people and company-onboarding need b2b.

Support and officer routes are not part of /admin/users. Support ticket triage and refund decisions live under /admin/support and use the support area; the completion tool, fallout queue, officer-scoped tickets and officer refunds live under /admin/officer and use the officer area. Area grants are resolved at request time; the role is carried in the session token, so a role change, deactivation or delete ends the user's live sessions and they sign in again.

Access Rules ​

The owner is not bound by these checks. For an admin, the service checks every change against the caller's own stored access, and a change outside it returns 403 ADMIN_USER_SCOPE_EXCEEDED.

  • Grants stay within your own levels. An admin may hand out only a role and areas it holds itself, at the same level or lower. Only the owner grants staff-users Manage.
  • Fixed roles need their module. Creating or moving someone into activation_officer needs officer Manage and customers View; support needs support Manage and customers View.
  • Targets of PATCH, DELETE and resend-invite. Never yourself. An owner is not a manageable target at all (404 ADMIN_USER_NOT_FOUND). A staff target needs staff-users Manage, and the caller's levels must cover the target's effective levels. A company person who belongs to a company needs company-people Manage; one with no company yet needs company-onboarding Manage. A target outside every area the caller can view answers 404 ADMIN_USER_NOT_FOUND, as if it did not exist.

View vs Manage ​

For the admin role, areas are split into two levels:

  • managedAreas — the View baseline: areas the admin may open (read).
  • managedAreasWrite — the Manage subset: areas the admin may write to. Must be a subset of managedAreas.

Areas in managedAreas but not in managedAreasWrite are View-only. Fixed staff roles derive their fixed areas from the role and omit both fields. Areas that are View-only always read as View, even if a stored Manage exists. The server resolves these at request time (never frozen in the JWT). A user's effective areas are returned in the user object of GET /auth/session as managedAreas and managedAreasWrite.

Viewer Shaping ​

Staff-facing reads that render customer data shape it by viewer, independently of which area admitted the caller. The admin user, pending company admin and company people lists follow the same rule for phone and email. The owner (super_admin) sees the stored values. Every other viewer — staff roles and platform admins alike — sees the masked forms, with the same field names and types either way:

  • Phone: the stored E.164 value for the owner, a prefix plus the last two digits behind fixed bullets for everyone else (+9665•••••67).
  • Email: the stored address for the owner, the first character plus the full domain behind fixed bullets for everyone else (a•••@example.com).
  • ICCID: the whole serial for the owner, its last 4 characters for everyone else.
  • Operator reference: the stored transaction reference for the owner, its last 4 characters behind fixed bullets for everyone else (•••c123).

This applies to the customer record, the admin activation records list, and the admin store order views. On a store order the split is sharper: the owner additionally receives the issued profile fields with a masked activation code, while every other viewer receives only the ICCID suffix.


Administration User Object ​

The shape returned by the list, create, and update endpoints. The list and update responses mask phone and email for every caller but the owner, and return the areas as null for a caller without staff-users Manage; the create response returns the contact as it was submitted.

FieldTypeDescription
idstring (uuid)User ID
phonestring | nullPhone in E.164 format; null for email-only users
emailstring | nullEmail address; null for phone-only users
namestring | nullDisplay name
rolestringAssigned UserRole
managedAreasstring[] | nullView areas (admin only; null for staff roles, and in the list and update responses for a caller without staff-users Manage)
managedAreasWritestring[] | nullManage subset (admin only; null for staff roles, and in the list and update responses for a caller without staff-users Manage)
isActivebooleanWhether the account is active
createdAtstringISO 8601 creation timestamp

Success responses are data envelopes (no localized message). Error messages are localized via Accept-Language, and the sign-in invitation sent to a new user is bilingual (English + Arabic).


GET /admin/users ​

List the portal staff users, newest first. Excludes end_user accounts, super_admin owners, and b2b_admin company people, who are listed per company under B2B Administration.

Requires super_admin, or an admin with staff-users View. Phone and email are masked unless the caller is the owner, and managedAreas and managedAreasWrite are null for a caller without staff-users Manage.

Response ​

200 OK

json
{
  "data": [
    {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "phone": "+966501234567",
      "email": null,
      "name": "Sara Officer",
      "role": "activation_officer",
      "managedAreas": null,
      "managedAreasWrite": null,
      "isActive": true,
      "createdAt": "2026-06-29T00:00:00.000Z"
    },
    {
      "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
      "phone": null,
      "email": "product.admin@skyte.sa",
      "name": "Platform Admin",
      "role": "admin",
      "managedAreas": ["packages", "activation-records"],
      "managedAreasWrite": ["packages"],
      "isActive": true,
      "createdAt": "2026-06-28T00:00:00.000Z"
    }
  ]
}

Error Responses ​

CodeStatusCondition
AUTH_REQUIRED401Missing token (an invalid or expired token returns AUTH_SESSION_EXPIRED)
AUTH_FORBIDDEN403Caller is not super_admin or admin, or lacks the required staff-users level

POST /admin/users ​

Provision a new administration user. Requires at least one of phone or email, a name, and a role. For the admin role, managedAreas is required and non-empty; fixed staff roles must omit the area fields. After creation, a bilingual invitation is sent over whichever channel(s) were provided — best-effort, so a delivery failure does not fail creation.

Requires super_admin, or an admin with staff-users Manage and a grant within its own levels (see Access Rules).

Request Body ​

FieldTypeRequiredDescription
phonestringConditionalSaudi E.164 (+966XXXXXXXXX). At least one of phone or email.
emailstringConditionalEmail address. At least one of phone or email.
namestringYesFull name (1–255 characters)
rolestringYesOne of admin, activation_officer, support
managedAreasstring[]ConditionalRequired and non-empty when role is admin; must be omitted for other roles. Values from the area list. A dependent area needs its parent.
managedAreasWritestring[]NoManage subset of managedAreas (admin only). Must be a subset of managedAreas and must not include a View-only area.

Examples ​

Platform-admin with assigned areas:

json
{
  "email": "product.admin@skyte.sa",
  "name": "Platform Admin",
  "role": "admin",
  "managedAreas": ["packages", "activation-records"],
  "managedAreasWrite": ["packages"]
}

Fixed staff role (phone-only):

json
{
  "phone": "+966501234567",
  "name": "Sara Officer",
  "role": "activation_officer"
}

Response ​

201 Created — the created Administration User Object wrapped in data.

json
{
  "data": {
    "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "phone": null,
    "email": "product.admin@skyte.sa",
    "name": "Platform Admin",
    "role": "admin",
    "managedAreas": ["packages", "activation-records"],
    "managedAreasWrite": ["packages"],
    "isActive": true,
    "createdAt": "2026-06-30T12:00:00.000Z"
  }
}

Error Responses ​

CodeStatusCondition
AUTH_REQUIRED401Missing token (an invalid or expired token returns AUTH_SESSION_EXPIRED)
AUTH_FORBIDDEN403Caller is not super_admin or admin, or lacks the required staff-users level
VALIDATION_ERROR400Schema validation failed (no credential, area rules, managedAreasWrite not a subset of managedAreas or including a View-only area, a dependent area missing its parent, etc.)
ADMIN_USER_SCOPE_EXCEEDED403The grant exceeds the caller's own levels, or the role needs areas the caller lacks
ADMIN_USER_PHONE_EXISTS409Phone number already registered
ADMIN_USER_EMAIL_EXISTS409Email address already registered
ADMIN_USER_CREATE_FAILED502Auth account creation failed upstream
ADMIN_USER_PROFILE_NOT_CREATED502Profile row was not seeded; the orphaned auth identity is rolled back

PATCH /admin/users/:id ​

Update an administration user's access (role + areas) and/or active state. At least one field must be provided. Changing to a fixed staff role clears any admin scope; changing to admin sets the scope from the request. Identity (name/phone/email) is immutable here. Creation and update are audited with managedAreasWrite alongside the role and areas; an update also records the old values, and an edit that changes nothing writes no entry.

Changing role or setting isActive: false signs the user out of every session; an admin area edit that keeps the same role does not.

Requires super_admin, or an admin allowed to act on the target (see Access Rules).

Path Parameters ​

ParameterTypeRequiredDescription
idstring (uuid)YesTarget user ID

Request Body ​

All fields optional; at least one is required.

FieldTypeRequiredDescription
rolestringNoNew role (admin, activation_officer, support). Required in order to change areas. Refused for a b2b_admin target.
managedAreasstring[]ConditionalRequired and non-empty when role is admin
managedAreasWritestring[]NoManage subset of managedAreas
isActivebooleanNoActivate or deactivate the account

Examples ​

Grant admin scope:

json
{
  "role": "admin",
  "managedAreas": ["packages"],
  "managedAreasWrite": ["packages"]
}

Deactivate:

json
{
  "isActive": false
}

Response ​

200 OK — the updated Administration User Object wrapped in data.

Error Responses ​

CodeStatusCondition
AUTH_REQUIRED401Missing token (an invalid or expired token returns AUTH_SESSION_EXPIRED)
AUTH_FORBIDDEN403Caller is not super_admin or admin (an admin's reach is decided per target, see the two rows below)
VALIDATION_ERROR400Schema validation failed (invalid id, no fields, area rules)
ADMIN_USER_SCOPE_EXCEEDED403The target is the caller, or is outside the caller's access, or the new grant exceeds it
ADMIN_USER_NOT_FOUND404No manageable user with this id, or the target is outside every area the caller can view
COMPANY_ADMIN_ROLE_LOCKED409role sent for a b2b_admin target — a company person's role cannot be changed; remove them instead
COMPANY_LAST_ADMIN409isActive: false for the last active admin of a company

DELETE /admin/users/:id ​

Permanently remove an administration user. Deletes the profile row, then the auth identity, freeing the phone/email for re-registration. The user's live sessions end with the removal. A removal that is refused or rolls back leaves them signed in.

The profile row is deleted in a transaction that also runs the last-admin guard for a company person, and the removal is audited before the auth identity is deleted. A failed auth delete therefore leaves an orphaned auth identity that cannot sign in, since sign-in looks up the profile row, rather than an active account with no identity.

Ticket comments the user wrote are kept, with their author cleared.

Requires super_admin, or an admin allowed to act on the target (see Access Rules).

Path Parameters ​

ParameterTypeRequiredDescription
idstring (uuid)YesTarget user ID

Response ​

204 No Content — empty body.

Error Responses ​

CodeStatusCondition
AUTH_REQUIRED401Missing token (an invalid or expired token returns AUTH_SESSION_EXPIRED)
AUTH_FORBIDDEN403Caller is not super_admin or admin (an admin's reach is decided per target, see the two rows below)
VALIDATION_ERROR400id is not a valid UUID
ADMIN_USER_SCOPE_EXCEEDED403The target is the caller, or is outside the caller's access
ADMIN_USER_NOT_FOUND404No manageable user with this id, or the target is outside every area the caller can view
COMPANY_LAST_ADMIN409The target is the last active admin of a company
ADMIN_USER_DELETE_FAILED502Auth identity removal failed upstream, after the profile row was already deleted

POST /admin/users/:id/resend-invite ​

Re-send the bilingual sign-in invitation to an existing administration user over its stored channel(s). Best-effort delivery.

Requires super_admin, or an admin allowed to act on the target (see Access Rules).

Path Parameters ​

ParameterTypeRequiredDescription
idstring (uuid)YesTarget user ID

Response ​

200 OK

json
{
  "message": "Invitation re-sent"
}

Error Responses ​

CodeStatusCondition
AUTH_REQUIRED401Missing token (an invalid or expired token returns AUTH_SESSION_EXPIRED)
AUTH_FORBIDDEN403Caller is not super_admin or admin (an admin's reach is decided per target, see the two rows below)
VALIDATION_ERROR400id is not a valid UUID
ADMIN_USER_SCOPE_EXCEEDED403The target is the caller, or is outside the caller's access
ADMIN_USER_NOT_FOUND404No manageable user with this id, or the target is outside every area the caller can view

Internal documentation - Activation System