Users API
Base path: /api/users
This section documents all endpoints for managing staff users (admins, sales, finance, agents). Authentication is cookie-based (JWT set as an httpOnly cookie at login). Access to each route is role-gated as noted below.
Roles & access
super-admincan view all users and create admins.admincan manage non-admin staff. Creating or updating admin users is restricted tosuper-admin.sales&financehave limited access (e.g., request password for clients via dedicated route below).Company scoping: For non–super-admins, list & update operations are limited to users that share at least one company with the requester.
GET /api/users
List users. Results are scoped by the caller's allowed companies unless the caller is super-admin.
Query parameters
query(string, optional) — Filter by partial name match.accessLevel("admin" | "finance" | "sales" | "none", optional) — Exact role filter.includeAdmins(boolean, optional) — When false (default for non–super-admins), admin & super-admin users are excluded.
Responses
200 OK— Array of user objects.500 Internal Server Error
Example
curl -X GET 'https://api.example.com/api/users?query=ali&accessLevel=sales' \
--cookie "token=<JWT>"
GET /api/users/:id
Fetch a single user by id. Roles: admin (and super-admin).
Path params
id(number, required) — User ID.
Responses
200 OK— User object with anallowedCompaniesarray of{ companyId, userId }mappings.404 Not Found— User not found.500 Internal Server Error
Example
curl -X GET 'https://api.example.com/api/users/42' \
--cookie "token=<JWT>"
POST /api/users
Create a new staff user. Roles: admin (with restrictions below).
- Only
super-adminmay create users withaccessLevel = "admin". - On success, a password reset email is sent to the user's email with a one-time link.
Request body (JSON)
{
"name": "Jane Doe",
"email": "jane@example.com",
"phone": "+971555555555",
"accessLevel": "sales", // one of: admin | sales | finance | agent
"password": "optional-initial-password", // optional; defaults internally
"dateOfBirth": "1990-05-12", // optional, ISO date
"startDate": "2024-01-01", // optional, ISO date
"endDate": null, // optional, ISO date or null
"governmentIds": [ // optional; will be stored as JSON
{ "type": "EmiratesID", "number": "784-XXXX-XXXXXXX-X" }
],
"allowedCompanies": [1, 3, 9] // optional; company ids the user can access
}
Responses
201 Created— Returns the created user identifier (e.g.,{ id: 123 }).400 Bad Request— Incorrect access level, or non–super-admin attempting to create an admin.500 Internal Server Error
Example
curl -X POST 'https://api.example.com/api/users' \
-H 'Content-Type: application/json' \
--cookie "token=<JWT>" \
-d '{
"name": "Jane Doe",
"email": "jane@example.com",
"accessLevel": "sales",
"allowedCompanies": [1,3]
}'
PUT /api/users/:id
Update a user. Roles: admin (and super-admin).
- Any provided fields will be updated; omitted fields remain unchanged.
- Providing
passwordwill reset the password (server will hash it). - Passing
allowedCompaniesreplaces all existing company relations for the user. - If the requester is company-scoped, they can only assign companies they themselves are allowed to access.
Path params
id(number, required) — User ID.
Request body (partial JSON)
{
"name": "Updated Name",
"email": "updated@example.com",
"phone": "+971...",
"accessLevel": "finance", // must be one of: admin | sales | finance | agent
"password": "newPassword123", // optional
"dateOfBirth": "1990-05-12",
"startDate": "2024-01-01",
"endDate": null,
"governmentIds": [{ "type": "EID", "number": "..." }],
"allowedCompanies": [2,4]
}
Responses
200 OK—{ message: "user updated successfully" }400 Bad Request— No data to update, or invalid accessLevel.403 Forbidden— Attempt to assign companies outside caller's allowed companies.500 Internal Server Error
Example
curl -X PUT 'https://api.example.com/api/users/42' \
-H 'Content-Type: application/json' \
--cookie "token=<JWT>" \
-d '{ "accessLevel": "finance", "allowedCompanies": [2,4] }'
DELETE /api/users/:id
Delete a user. Auth required.
Path params
id(number, required) — User ID.
Responses
200 OK—{ message: "user deleted successfully" }500 Internal Server Error
Example
curl -X DELETE 'https://api.example.com/api/users/42' \
--cookie "token=<JWT>"
POST /api/users/requestPassword/:id
Send a password reset email for a client or staff user.
Roles: admin, super-admin, sales, finance.
Behavior
- If
:idbelongs to a client and the client is active: a client-specific reset email is sent. - Otherwise, the endpoint targets a staff user by id and sends a staff reset email.
- When called for a staff user by id, attempting to reset for disallowed roles is rejected.
Path params
id(number, required) — Target user (client or staff) id.
Alternate targeting by email
- The service also accepts
{ "email": "..." }in the body for lookups when an id is not used in internal flows.
Request body (optional for email targeting)
{ "email": "person@example.com" }
Responses
201 Created—{ message: "Email has been successfully" }401 Unauthorized— Attempt to reset for a restricted staff role.500 Internal Server Error
Example (reset by id)
curl -X POST 'https://api.example.com/api/users/requestPassword/77' \
--cookie "token=<JWT>"
Example (reset by email)
curl -X POST 'https://api.example.com/api/users/requestPassword/0' \
-H 'Content-Type: application/json' \
--cookie "token=<JWT>" \
-d '{ "email": "client@example.com" }'
Data model (response shape)
User (abbreviated)
{
id: number;
name: string;
email: string;
phone?: string;
accessLevel: 'super-admin' | 'admin' | 'sales' | 'finance' | 'agent' | 'none';
// ...additional profile fields
}
AllowedCompany
{
userId: number;
companyId: number;
}
Errors
All endpoints return a JSON body with a message when an error occurs. Common statuses:
400— Validation or bad request.401— Unauthorized (missing/invalid session) or disallowed operation for role.403— Forbidden (company scoping violation).404— Resource not found.500— Server error.
Security
- Authentication via secure,
httpOnlycookie. Requires prior login. - Non–super-admin operations are scoped to the caller's allowed companies.
- Admin-creation restricted to
super-admin. - Passwords are never returned by the API.