Installment Groups API
Base URL: /api/installment-groups
This API manages installment groups (properties/contracts), their installments, attachments, client/user access, logs, and reminders.
Authentication & Roles
All endpoints require an authenticated user (JWT set as an HTTP-only cookie named token). Role-based behavior:
- super-admin: Full access across companies.
- admin / finance / sales: Access restricted to their
allowedCompanies. Some filters also apply (see notes below). - agent: Only groups where the agent is assigned to at least one client in the group.
- client: Only groups that include the client (via
igClients).
Some endpoints further restrict/shape results (e.g.,
allowedUsersaccess list on groups). See each route for details.
Group Routes
GET /api/installment-groups
List installment groups with pagination and rich stats.
Query Params
query(string, optional) – searches by group name, unit number, client name, project name, or developer/company name (case-insensitive).client(number, optional) – filter groups containing this client. Ignored forclientrole (they’re auto-filtered to self).company(number, optional) – filter by company id.project(number, optional) – filter by project id.developer(number, optional) – filter by developer id (via project).page(number, default: 1) – page number.limit(number, default: 10) – page size.
Role Notes
client: forced to only their groups.agent: forced to only groups with at least one of their assigned clients.- non-
super-admin/non-client: forced to companies inallowedCompaniesand, if a group maintains an allowlist, to groups where the user is onigAllowedUsers.
Response 200 OK
{
"data": [
{
"id": 123,
"name": "",
"project": 9,
"projectName": "Palm Residences",
"description": "Penthouse",
"company": 4,
"unitNumber": "A-1203",
"city": "Dubai",
"createdBy": 7,
"createdAt": "2024-08-14T10:12:40.000Z",
"updatedAt": "2024-08-14T10:12:40.000Z",
"companyName": "MyHomePlan",
"createdByName": "Admin User",
"clients": [
{ "clientId": 33, "clientName": "John Doe", "phone": "+971...", "agents": [{"agentId": 18, "agentName": "Agent A"}] }
],
"totalAmount": 1000000,
"paidAmount": 250000,
"remainingAmount": 750000,
"dueAmount": 200000,
"stats": {
"totalInstallments": 12,
"totalAmount": 1000000,
"paidAmount": 250000,
"pendingAmount": 750000,
"dueAmount": 200000,
"attachmentsCount": 3
}
}
],
"pagination": { "total": 42, "page": 1, "limit": 10, "pages": 5 }
}
Errors: 500 on server error.
Example
curl -b "token=<JWT>" \
"/api/installment-groups?query=palm&company=4&page=1&limit=10"
GET /api/installment-groups/:id
Get a single group with clients, allowed users, and aggregate stats.
Path Params
id(number, required)
Response 200 OK
{
"id": 123,
"name": "",
"projectName": "Palm Residences",
"project": 9,
"developer": 2,
"developerName": "Emaar",
"description": "Penthouse",
"company": 4,
"unitNumber": "A-1203",
"city": "Dubai",
"createdBy": 7,
"createdAt": "2024-08-14T10:12:40.000Z",
"updatedAt": "2024-08-14T10:12:40.000Z",
"companyName": "MyHomePlan",
"createdByName": "Admin User",
"clients": [ { "clientId": 33, "clientName": "John Doe", "phone": "+971...", "agents": [{"agentId": 18, "agentName": "Agent A"}] } ],
"allowedUsers": [ { "userId": 7, "userName": "Admin User" } ],
"stats": { "totalInstallments": 12, "totalAmount": 1000000, "attachmentsCount": 3 }
}
Errors: 404 if not found, 500 on server error.
POST /api/installment-groups
Create a new installment group and attach one or more clients.
Body
{
"project": 9,
"clientIds": [33, 34],
"description": "Penthouse",
"company": 4,
"unitNumber": "A-1203",
"city": "Dubai"
}
Response 201 Created
{ "message": "Installment group created successfully", "id": 123 }
Errors: 400 missing fields, 500 server error.
PUT /api/installment-groups/:id
Update base fields and fully replace the client list and/or the allowed user list.
Body (any subset)
{
"project": 10,
"description": "Updated description",
"unitNumber": "B-804",
"city": "Abu Dhabi",
"clientIds": [40, 41],
"allowedUsers": [7, 18]
}
Response 200 OK
{ "message": "Installment group updated successfully" }
Errors: 500 server error.
Audit logs are written automatically for changed fields.
DELETE /api/installment-groups/:id
Delete an installment group.
Response 200 OK
{ "message": "Installment group deleted successfully" }
Errors: 500 server error.
Installments Routes
GET /api/installment-groups/:groupId/installments
Get all installments for a group, including computed summary.
Path Params
groupId(number, required)
Response 200 OK
{
"results": [
{
"id": 456,
"groupId": 123,
"amount": 100000,
"balance": 50000,
"dueDate": "2024-09-01T00:00:00.000Z",
"description": "10% on contract",
"status": "unpaid",
"isFee": false,
"percentage": 10,
"postHandover": false,
"payments": [
{ "id": 790, "amount": "50000", "clientId": 33, "paymentDate": "2024-09-10" }
]
}
],
"summary": {
"totalAmount": 1000000,
"availableBalance": 250000,
"remainingBalance": 750000,
"totalAmountWithoutFees": 950000,
"upcomingPayment": 100000
}
}
Errors: 500 server error.
GET /api/installment-groups/installments/unpaid
Return all overdue installments across groups plus the next upcoming unpaid installment per group.
Response 200 OK
{
"data": [ { "id": 456, "groupId": 123, "amount": 100000, "dueDate": "2024-08-01T00:00:00.000Z", "status": "unpaid", "isFee": false } ],
"total": 8
}
Errors: 500 server error.
GET /api/installment-groups/installments/:id
Get a single installment by id.
Path Params
id(number, required)
Response 200 OK
{
"id": 456,
"groupId": 123,
"amount": 100000,
"dueDate": "2024-09-01T00:00:00.000Z",
"description": "10% on contract",
"paymentId": null,
"createdBy": 7,
"createdByName": "Admin User",
"createdAt": "2024-08-14T10:12:40.000Z",
"updatedAt": "2024-08-14T10:12:40.000Z",
"percentage": 10,
"postHandover": false
}
Errors: 404 if not found, 500 server error.
POST /api/installment-groups/installments
Create one or more installments for a group.
Body
{
"groupId": 123,
"installments": [
{
"amount": 100000,
"dueDate": "2024-09-01",
"description": "10% on contract",
"isFee": false,
"status": "pending",
"postHandover": false,
"percentage": 10
}
]
}
Response 201 Created
{ "message": "1 installment(s) created successfully", "count": 1 }
Errors: 400 missing fields, 404 group not found, 500 server error.
PUT /api/installment-groups/installments/:id
Update a single installment. Any subset of fields may be provided.
Body (any subset)
{ "amount": 120000, "dueDate": "2024-09-15", "description": "updated", "paymentId": 999, "postHandover": true, "percentage": 12 }
Response 200 OK
{ "message": "Installment updated successfully" }
Errors: 404 if not found, 500 server error.
Field-level changes are logged automatically.
PUT /api/installment-groups/installments/bulk
Bulk update multiple installments from a group. Only changed fields are persisted & logged.
Body
{
"groupId": 123,
"installments": [
{ "id": 456, "amount": 120000, "dueDate": "2024-09-15" },
{ "id": 457, "percentage": 15, "description": "Milestone updated" }
]
}
Response 200 OK
{ "message": "Updated 2 installments successfully" }
Errors: 400 missing fields, 500 server error.
DELETE /api/installment-groups/installments/:id
Delete a single installment.
Response 200 OK
{ "message": "Installment deleted successfully" }
Errors: 404 if not found, 500 server error.
Attachments Routes
GET /api/installment-groups/:groupId/attachments
List all attachments for the group (and payment-scoped ones if present).
Path Params
groupId(number, required)
Response 200 OK
[
{
"id": 901,
"title": "Contract.pdf",
"groupId": 123,
"attachment": "https://s3/.../Contract.pdf",
"createdBy": 7,
"createdByName": "Admin User",
"createdAt": "2024-08-14T10:12:40.000Z",
"paymentId": null
}
]
Errors: 500 server error.
POST /api/installment-groups/attachments
Upload one or more attachments to a group (optionally linked to a payment).
Upload Limits: individual file size ≤ 10 MB.
Content-Type: multipart/form-data
Form Fields
groupId(number, required)files(file[], required) – one or more files.titles(string[], optional) – custom titles for each file. If omitted, original filenames are used.paymentId(number, optional) – link attachment to a payment record.
Response 201 Created
{ "message": "2 attachment(s) added successfully", "count": 2 }
Errors: 400 missing fields, 404 group not found, 500 server error.
Example
curl -b "token=<JWT>" -F groupId=123 \
-F "files=@/path/contract.pdf" \
-F "files=@/path/receipt.jpg" \
-F "titles=Signed Contract" \
-F "titles=Payment Receipt" \
-X POST \
/api/installment-groups/attachments
DELETE /api/installment-groups/attachments/:id
Delete a single attachment (S3/file + DB record).
Response 200 OK
{ "message": "Attachment deleted successfully" }
Errors: 404 if not found, 500 server error.
Logs
GET /api/installment-groups/logs/:groupId
Return detailed logs for a group (sorted newest first).
Response 200 OK
[
{ "id": 1, "description": "Installment created...", "eventType": "create", "createdAt": "2024-08-14T10:12:40.000Z", "createdBy": "Admin User" }
]
Errors: 500 server error.
Reminders
POST /api/installment-groups/sendReminder
Send a WhatsApp template reminder using the company’s configured template settings.
Body
{ "contact": "+9715xxxxxxx", "groupId": 123 }
Response 200 OK
{ "status": "success", ... }
Errors: 200 with explanatory message when settings/group are missing; 500 when the remote API returns an error.
Common Notes & Conventions
- Dates in bodies may be
YYYY-MM-DD; dates in responses are ISO 8601. - Monetary values are numeric; formatting (e.g., AED) is handled by the client.
- Fee installments are flagged with
isFee: trueand are excluded from some aggregate calculations. - Field changes are audited to
detailedInstallmentLogs. - File storage is handled via an object store (e.g., S3) – deletions remove both the object and the DB record.
Error Shape
{ "message": "<human readable error>" }
Security
- Auth via signed JWT stored in the
tokencookie (httpOnly,securein production,sameSiteset per environment). - Non-
super-adminusers are constrained byallowedCompaniesand group-level allow lists.