Admin Users API
Super-admin 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 session; the router is mounted behind a requireRole('super_admin') guard. Any other role receives 403 AUTH_FORBIDDEN. 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
| Role | Areas | Assignable here |
|---|---|---|
super_admin | All areas (owner) | No — owners are not managed here |
admin | The subset assigned via managedAreas (platform-admin) | Yes |
activation_officer | officer (fixed) | Yes |
support | support (fixed) | Yes |
b2b_admin | b2b (fixed) | Yes |
end_user | None | No |
Roles assignable through this API are exactly admin, b2b_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.
Areas
| Area | Portal section |
|---|---|
activation-records | Activation records dashboard |
packages | Package catalog |
officer | Activation officer tools |
support | Customer support |
b2b | B2B company portal |
user-management is intentionally not an assignable area — provisioning admins is an owner-only power.
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 ofmanagedAreas.
Areas in managedAreas but not in managedAreasWrite are View-only. Fixed staff roles derive their single area from the role and omit both fields. 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.
Administration User Object
The shape returned by the list, create, and update endpoints:
| Field | Type | Description |
|---|---|---|
id | string (uuid) | User ID |
phone | string | null | Phone in E.164 format; null for email-only users |
email | string | null | Email address; null for phone-only users |
name | string | null | Display name |
role | string | Assigned UserRole |
managedAreas | string[] | null | View areas (admin only; null for staff roles) |
managedAreasWrite | string[] | null | Manage subset (admin only; null for staff roles) |
isActive | boolean | Whether the account is active |
createdAt | string | ISO 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 manageable administration users, newest first. Excludes end_user accounts and super_admin owners.
Requires super_admin.
Response
200 OK
{
"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
| Code | Status | Condition |
|---|---|---|
AUTH_REQUIRED | 401 | Missing token (an invalid or expired token returns AUTH_SESSION_EXPIRED) |
AUTH_FORBIDDEN | 403 | Caller is not super_admin |
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.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
phone | string | Conditional | Saudi E.164 (+966XXXXXXXXX). At least one of phone or email. |
email | string | Conditional | Email address. At least one of phone or email. |
name | string | Yes | Full name (1–255 characters) |
role | string | Yes | One of admin, b2b_admin, activation_officer, support |
managedAreas | string[] | Conditional | Required and non-empty when role is admin; must be omitted for other roles. Values from the area list. |
managedAreasWrite | string[] | No | Manage subset of managedAreas (admin only). Must be a subset of managedAreas. |
Examples
Platform-admin with assigned areas:
{
"email": "product.admin@skyte.sa",
"name": "Platform Admin",
"role": "admin",
"managedAreas": ["packages", "activation-records"],
"managedAreasWrite": ["packages"]
}Fixed staff role (phone-only):
{
"phone": "+966501234567",
"name": "Sara Officer",
"role": "activation_officer"
}Response
201 Created — the created Administration User Object wrapped in data.
{
"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
| Code | Status | Condition |
|---|---|---|
AUTH_REQUIRED | 401 | Missing token (an invalid or expired token returns AUTH_SESSION_EXPIRED) |
AUTH_FORBIDDEN | 403 | Caller is not super_admin |
VALIDATION_ERROR | 400 | Schema validation failed (no credential, area rules, managedAreasWrite not a subset of managedAreas, etc.) |
ADMIN_USER_PHONE_EXISTS | 409 | Phone number already registered |
ADMIN_USER_EMAIL_EXISTS | 409 | Email address already registered |
ADMIN_USER_CREATE_FAILED | 502 | Auth account creation failed upstream |
ADMIN_USER_PROFILE_NOT_CREATED | 502 | Profile 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.
Requires super_admin.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes | Target user ID |
Request Body
All fields optional; at least one is required.
| Field | Type | Required | Description |
|---|---|---|---|
role | string | No | New role (admin, b2b_admin, activation_officer, support). Required in order to change areas. |
managedAreas | string[] | Conditional | Required and non-empty when role is admin |
managedAreasWrite | string[] | No | Manage subset of managedAreas |
isActive | boolean | No | Activate or deactivate the account |
Examples
Grant admin scope:
{
"role": "admin",
"managedAreas": ["packages"],
"managedAreasWrite": ["packages"]
}Deactivate:
{
"isActive": false
}Response
200 OK — the updated Administration User Object wrapped in data.
Error Responses
| Code | Status | Condition |
|---|---|---|
AUTH_REQUIRED | 401 | Missing token (an invalid or expired token returns AUTH_SESSION_EXPIRED) |
AUTH_FORBIDDEN | 403 | Caller is not super_admin |
VALIDATION_ERROR | 400 | Schema validation failed (invalid id, no fields, area rules) |
ADMIN_USER_NOT_FOUND | 404 | No manageable user with this id |
DELETE /admin/users/:id
Permanently remove an administration user. Deletes the auth identity and the profile row, freeing the phone/email for re-registration.
Requires super_admin.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes | Target user ID |
Response
204 No Content — empty body.
Error Responses
| Code | Status | Condition |
|---|---|---|
AUTH_REQUIRED | 401 | Missing token (an invalid or expired token returns AUTH_SESSION_EXPIRED) |
AUTH_FORBIDDEN | 403 | Caller is not super_admin |
VALIDATION_ERROR | 400 | id is not a valid UUID |
ADMIN_USER_NOT_FOUND | 404 | No manageable user with this id |
ADMIN_USER_DELETE_FAILED | 502 | Auth identity removal failed upstream |
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.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes | Target user ID |
Response
200 OK
{
"message": "Invitation re-sent"
}Error Responses
| Code | Status | Condition |
|---|---|---|
AUTH_REQUIRED | 401 | Missing token (an invalid or expired token returns AUTH_SESSION_EXPIRED) |
AUTH_FORBIDDEN | 403 | Caller is not super_admin |
VALIDATION_ERROR | 400 | id is not a valid UUID |
ADMIN_USER_NOT_FOUND | 404 | No manageable user with this id |