Skip to main content

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., allowedUsers access 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 for client role (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 in allowedCompanies and, if a group maintains an allowlist, to groups where the user is on igAllowedUsers.

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: true and 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 token cookie (httpOnly, secure in production, sameSite set per environment).
  • Non-super-admin users are constrained by allowedCompanies and group-level allow lists.