Clients API
Base URL: /api/clients
This section documents the endpoints for managing clients/leads and their assignments. All routes require authentication. Role-based access is enforced per endpoint.
Auth model: Cookie-based session (JWT under the hood). Send your authenticated cookie with each request.
Authorization matrix
| Endpoint | Method | Roles |
|---|---|---|
/api/clients | GET | admin, sales, finance |
/api/clients/:id | GET | admin, sales, finance, agent |
/api/clients/userClients/:id | GET | admin, sales, finance |
/api/clients | POST | admin, sales, finance |
/api/clients/:id | PUT | admin, sales, finance |
/api/clients/:id | DELETE | admin, sales, finance |
/api/clients/:id/change-password | POST | admin, sales, finance |
/api/clients/passport/:id | POST (multipart) | admin, sales, finance |
Data models
Client
{
"id": 123,
"name": "Jane Doe",
"email": "jane@example.com",
"phone": "+971500000000",
"address": "Dubai, UAE",
"isActive": true,
"createdBy": 12,
"createdAt": "2025-01-02T10:15:36.000Z",
"updatedAt": "2025-01-15T08:01:10.000Z",
"company": "Acme Properties", // company name
"hasJoined": true, // whether the client has set a password
"agents": [
{ "agentId": 42, "agentName": "Ahmed Ali" }
]
}
Pagination envelope
- Default page size: 10
- Query param:
page(1-based index)
Common list response shape:
{
"leads": [ /* Client[] */ ],
"totalPages": 5
}
Some endpoints return
clientsinstead ofleadsfor the array key (see each endpoint).
GET /api/clients
List clients with optional filters and pagination.
Query parameters
query(string, optional) — search by name, email, or phone (contains).category(string, optional) — one of:active,not_active.page(number, optional) — 1-based page index; when omitted, returns the full list.company(number, optional) — filter by company id. If omitted and the caller is notsuper-admin, results are limited to the caller’s allowed companies.
Response
200 OK
{
"leads": [Client, ...],
"totalPages": 3
}
cURL
curl -X GET "https://your.api/api/clients?query=jane&category=active&page=1" \
-H "Cookie: token=..."
GET /api/clients/:id
Get a single client by id.
Path parameters
id(number, required) — client id.
Response
200 OK
{
"leads": Client
}
Note: the top-level key is
leadsby implementation.
404 Not Found — when the client does not exist.
cURL
curl -X GET "https://your.api/api/clients/123" -H "Cookie: token=..."
GET /api/clients/userClients/:id
List clients assigned to a specific agent.
Path parameters
id(number, required) — agent (user) id.
Query parameters
page(number, optional) — 1-based page index.
Response
200 OK
{
"clients": [Client, ...],
"totalPages": 2
}
cURL
curl -X GET "https://your.api/api/clients/userClients/42?page=1" \
-H "Cookie: token=..."
POST /api/clients
Create a new client/lead and (optionally) assign one or more agents.
Request body
{
"name": "Jane Doe",
"email": "jane@example.com",
"phone": "+971500000000",
"address": "Dubai, UAE",
"source": "referral", // optional
"agents": [ // optional; one or many
{ "agentId": 42, "agentName": "Ahmed Ali" }
],
"isActive": true,
"company": 7 // numeric company id (required)
}
Responses
201 Created—{ "message": "LeadClient created successfully" }400 Bad Request— e.g., duplicate email{ "message": "Lead with this email already exists" }
cURL
curl -X POST "https://your.api/api/clients" \
-H "Content-Type: application/json" \
-H "Cookie: token=..." \
-d '{
"name": "Jane Doe",
"email": "jane@example.com",
"phone": "+971500000000",
"address": "Dubai, UAE",
"company": 7,
"agents": [{"agentId":42,"agentName":"Ahmed Ali"}],
"isActive": true
}'
PUT /api/clients/:id
Update client fields and/or reassign agents.
Path parameters
id(number, required) — client id.
Request body
Any updatable subset of:
{
"name": "Jane Doe",
"email": "jane@example.com",
"phone": "+971500000000",
"address": "Dubai, UAE",
"source": "referral",
"agents": [ { "agentId": 42, "agentName": "Ahmed Ali" } ],
"isActive": true
}
Notes:
- When
agentsis provided, existing assignments are replaced with the provided list.
Responses
200 OK—{ "message": "LeadClient updated successfully" }404 Not Found— if the client id does not exist.
cURL
curl -X PUT "https://your.api/api/clients/123" \
-H "Content-Type: application/json" -H "Cookie: token=..." \
-d '{"phone":"+971511111111","agents":[{"agentId":42,"agentName":"Ahmed Ali"}]}'
DELETE /api/clients/:id
Soft-disable a client (sets isActive to false).
Path parameters
id(number, required) — client id.
Response
200 OK
{ "message": "Lead disabled successfully" }
cURL
curl -X DELETE "https://your.api/api/clients/123" -H "Cookie: token=..."
POST /api/clients/:id/change-password
Set or change a client’s password (admin-side action).
Path parameters
id(number, required) — client id.
Request body
{ "password": "NewStrongPassword!" }
Response
200 OK
{ "message": "Password changed successfully" }
cURL
curl -X POST "https://your.api/api/clients/123/change-password" \
-H "Content-Type: application/json" -H "Cookie: token=..." \
-d '{"password":"NewStrongPassword!"}'
POST /api/clients/passport/:id
Upload or replace a client’s passport document (admin-side upload).
Path parameters
id(number, required) — client id.
Request
Content-Type: multipart/form-data
- Field name:
passport - File: image/PDF/etc.
Response
200 OK
{
"message": "Passport uploaded successfully",
"path": "https://.../path/to/passport.pdf"
}
cURL
curl -X POST "https://your.api/api/clients/passport/123" \
-H "Cookie: token=..." \
-F "passport=@/path/to/passport.pdf"
Errors
The API uses standard HTTP status codes. Common error shapes:
{ "message": "Invalid company ID" }
{ "message": "Error updating leadClient" }
400 Bad Request— malformed or invalid parameters.401 Unauthorized— missing/invalid session.403 Forbidden— authenticated but lacks the required role.404 Not Found— resource doesn’t exist.500 Internal Server Error— unexpected server error.
Notes & constraints
- Page size is fixed at 10 items when
pageis used. - When
companyis not specified and the caller is notsuper-admin, results are limited to the caller’s allowed companies. - The
hasJoinedflag indicates whether a client has created a password (joined the portal). - File uploads overwrite the previously stored passport; the previous file is removed server-side.