Skip to main content

Issue Resolution Flow

Once an issue is filed it follows a structured lifecycle designed to create accountability and prevent premature closure.

Status lifecycle​

open ──► in_progress ──► resolved ──► closed  (terminal)
β”‚
└─── reopen request approved ──► open

All status transitions are guarded on the server. Attempting an invalid transition (e.g. resolving an already-resolved issue) returns 409 Conflict.


Roles and permissions​

ActionWho can do it
Mark resolvedReporter of the issue or admin/superadmin
Rate a resolutionAny authenticated user except the reporter
Request reopenAny non-viewer authenticated user
Review reopen requestReporter or admin/superadmin
Close issueReporter or admin/superadmin

POST /issues/:id/resolve​

Mark an open or in-progress issue as resolved. Requires a description of the fix and at least one proof media item already uploaded to the issue.

Auth required: Yes β€” reporter or admin

Request body​

{
"description": "Pothole filled with bitumen by BBMP Roads crew on 28 Feb. Area cordoned off for 24 hrs to cure.",
"mediaIds": ["media-abc123", "media-def456"]
}
FieldTypeRules
descriptionstring10–5000 characters
mediaIdsstring[]At least 1 item; each ID must be a media item already attached to this issue

Response 201 Created​

{
"success": true,
"data": {
"issueId": "ISS-001",
"status": "resolved",
"resolution": {
"description": "Pothole filled with bitumen…",
"mediaIds": ["media-abc123", "media-def456"],
"resolvedBy": { "id": "u1", "name": "Rahul Sharma", "role": "admin" },
"resolvedAt": "2026-03-01T09:45:00Z",
"ratings": [],
"reopenRequests": []
}
}
}

Errors​

StatusCause
403Caller is not the reporter or an admin
409Issue is already resolved or closed
422mediaIds is empty or description too short

POST /issues/:id/rate-resolution​

Rate the quality of a resolution thumbs-up or thumbs-down. A user can change their vote by calling this endpoint again with a different value.

Auth required: Yes β€” any user except the original reporter

Request body​

{ "vote": "up" }
FieldTypeValues
votestringup | down

Response 200 OK​

{
"success": true,
"data": {
"issueId": "ISS-001",
"resolution": {
"ratings": [
{ "userId": "u2", "vote": "up", "at": "2026-03-02T11:00:00Z" }
]
}
}
}

Errors​

StatusCause
403Reporter trying to rate their own issue
409Issue is not in resolved status

POST /issues/:id/reopen-request​

Submit a request to reopen a resolved issue (e.g. the fix didn't hold, or new evidence has emerged). Only admins can approve/deny reopen requests.

Auth required: Yes β€” any non-viewer user

Request body​

{
"reason": "The pothole has reappeared after the first rain. The fix was superficial and did not address the root cause."
}
FieldTypeRules
reasonstring5–1000 characters

Response 201 Created​

{
"success": true,
"data": {
"requestId": "req-7f3a",
"issue": {
"issueId": "ISS-001",
"resolution": {
"reopenRequests": [
{
"id": "req-7f3a",
"userId": "u2",
"userName": "Priya Nair",
"reason": "The pothole has reappeared…",
"at": "2026-03-02T12:00:00Z",
"status": "pending"
}
]
}
}
}
}

Errors​

StatusCause
403User has viewer role
409Issue is closed (terminal β€” cannot be reopened)

PATCH /issues/:id/reopen-request/:reqId​

Approve or deny a pending reopen request.

  • Approve β€” reverts the issue to open, removes the resolution object entirely, and marks the request as approved.
  • Deny β€” sets the request's status to denied; issue remains resolved.

Auth required: Yes β€” reporter or admin

Request body​

{ "action": "approve" }
FieldTypeValues
actionstringapprove | deny

Response 200 OK (approve)​

{
"success": true,
"data": {
"issueId": "ISS-001",
"status": "open",
"resolution": null
}
}

Errors​

StatusCause
403Not the reporter or an admin
404Request ID not found on this issue
409Request has already been reviewed (not pending)

PATCH /issues/:id/close​

Permanently close a resolved issue. This is a terminal state β€” closed issues cannot be reopened or modified.

Typically triggered by the reporter once they are satisfied, or by an admin after the 7-day auto-close window.

Auth required: Yes β€” reporter or admin

Request body​

{ "source": "owner" }
FieldTypeValuesMeaning
sourcestringownerReporter manually closed it
adminAdmin closed it
autoSystem closed it after 7-day window

Response 200 OK​

{
"success": true,
"data": {
"issueId": "ISS-001",
"status": "closed",
"closedAt": "2026-03-08T09:45:00Z",
"closedBy": "owner"
}
}

Errors​

StatusCause
403Not the reporter or an admin
409Issue is not in resolved status (must be resolved before closing)

Auto-close​

The frontend fires a close request automatically if a resolved issue's resolution.resolvedAt is more than 7 days old. The source for these requests is "auto".

In production, this would be a scheduled Lambda/cron function that scans for resolved issues older than 7 days and calls PATCH /issues/:id/close with { "source": "auto" }.