21 KiB
Work Management Service API Documentation
Base URL: /api/work (via gateway) or http://localhost:8083 (direct)
Service Port: 8083
Overview
The Work Management Service handles all project, task, and work order management for the Coppertone.tech platform. It provides:
- Project management with approval workflow
- Task tracking with assignments and time tracking
- Work orders with IPFS document integration
- Role-based access control (RBAC)
Authentication
All endpoints require a valid JWT token in the Authorization header:
Authorization: Bearer <jwt_token>
The token is validated against the auth-service and must contain:
user_id/userId: User identifierroles: Array of user roles
Role-Based Access
| Role | Projects | Tasks | Work Orders |
|---|---|---|---|
SUPERUSER |
Full access | Full access | Full access |
ADMIN |
Full access | Full access | Full access |
STAFF |
Full access | Full access | Full access |
CLIENT |
Own projects only | Tasks on own projects | No access |
Note: SUPERUSER role automatically grants full access to all endpoints.
Health Check
GET /healthz
Response:
200 OK
ok
Projects
List Projects
Retrieve a list of approved projects. CLIENTs see only their own projects.
GET /projects
Headers:
Authorization: Bearer <token>
Required Role: Any authenticated user
Response (200 OK):
[
{
"id": 1,
"name": "Website Redesign",
"description": "Complete redesign of corporate website",
"status": "IN_PROGRESS",
"clientId": 5,
"ipfsMetadataCid": "QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco",
"startDate": "2024-01-15",
"endDate": "2024-03-15",
"requestedBy": 5,
"approvedBy": 2,
"approvalStatus": "APPROVED",
"approvalDate": "2024-01-10T14:30:00Z",
"rejectionReason": null,
"createdAt": "2024-01-05T10:00:00Z",
"updatedAt": "2024-01-20T09:15:00Z"
}
]
Authorization Logic:
STAFF/ADMIN/SUPERUSER: See all approved projectsCLIENT: See only projects whereclient_idmatches their user ID
Get Project by ID
Retrieve a single project by ID.
GET /projects/{id}
Headers:
Authorization: Bearer <token>
Required Role: Any authenticated user (with access)
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id |
integer | Project ID |
Response (200 OK):
{
"id": 1,
"name": "Website Redesign",
"description": "Complete redesign of corporate website",
"status": "IN_PROGRESS",
"clientId": 5,
"ipfsMetadataCid": "QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco",
"startDate": "2024-01-15",
"endDate": "2024-03-15",
"requestedBy": 5,
"approvedBy": 2,
"approvalStatus": "APPROVED",
"approvalDate": "2024-01-10T14:30:00Z",
"rejectionReason": null,
"createdAt": "2024-01-05T10:00:00Z",
"updatedAt": "2024-01-20T09:15:00Z"
}
Error Responses:
| Status | Condition | Response |
|---|---|---|
| 400 | Invalid ID | Invalid project ID |
| 403 | No access | Forbidden: you do not have access to this project |
| 404 | Not found | Project not found |
Authorization Logic:
- User must be: project owner (
client_id), requester (requested_by), orSTAFF/ADMIN/SUPERUSER
Create Project
Create a new project. Projects created by STAFF/ADMIN are auto-approved.
POST /projects
Headers:
Authorization: Bearer <token>
Required Role: STAFF, ADMIN, or SUPERUSER
Request Body:
{
"name": "New Mobile App",
"description": "iOS and Android app development",
"status": "PLANNING",
"clientId": 5,
"ipfsMetadataCid": "QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco",
"startDate": "2024-02-01",
"endDate": "2024-06-30"
}
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string | Yes | - | Project name |
description |
string | No | - | Project description |
status |
string | No | PLANNING |
Project status |
clientId |
integer | No | - | Assigned client user ID |
ipfsMetadataCid |
string | No | - | IPFS CID for metadata |
startDate |
string | No | - | Planned start date (YYYY-MM-DD) |
endDate |
string | No | - | Planned end date (YYYY-MM-DD) |
Valid Status Values:
PLANNINGIN_PROGRESSON_HOLDCOMPLETEDCANCELLED
Response (201 Created):
{
"id": 2,
"name": "New Mobile App",
"description": "iOS and Android app development",
"status": "PLANNING",
"clientId": 5,
"ipfsMetadataCid": "QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco",
"startDate": "2024-02-01",
"endDate": "2024-06-30",
"requestedBy": 2,
"approvedBy": 2,
"approvalStatus": "APPROVED",
"approvalDate": "2024-01-25T12:00:00Z",
"rejectionReason": null,
"createdAt": "2024-01-25T12:00:00Z",
"updatedAt": "2024-01-25T12:00:00Z"
}
Error Responses:
| Status | Condition | Response |
|---|---|---|
| 400 | Missing name | Project name is required |
| 403 | Insufficient role | Insufficient permissions |
Update Project
Update an existing project.
PUT /projects/{id}
Headers:
Authorization: Bearer <token>
Required Role: STAFF, ADMIN, or SUPERUSER (or project owner)
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id |
integer | Project ID |
Request Body:
{
"name": "Updated Project Name",
"description": "Updated description",
"status": "IN_PROGRESS",
"clientId": 5,
"ipfsMetadataCid": "QmNewCid...",
"startDate": "2024-02-01",
"endDate": "2024-07-31"
}
Response (200 OK):
{
"id": 1,
"name": "Updated Project Name",
...
}
Error Responses:
| Status | Condition | Response |
|---|---|---|
| 403 | No permission | Forbidden: you do not have permission to update this project |
| 404 | Not found | Project not found |
Delete Project
Delete a project. Only STAFF/ADMIN can delete projects.
DELETE /projects/{id}
Headers:
Authorization: Bearer <token>
Required Role: STAFF, ADMIN, or SUPERUSER
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id |
integer | Project ID |
Response (204 No Content)
Error Responses:
| Status | Condition | Response |
|---|---|---|
| 403 | No permission | Forbidden: only STAFF or ADMIN can delete projects |
| 404 | Not found | Project not found |
Project Requests (Client Workflow)
CLIENTs can request new projects, which require STAFF/ADMIN approval.
List My Project Requests
Retrieve all project requests made by the current user.
GET /project-requests
Headers:
Authorization: Bearer <token>
Required Role: Any authenticated user
Response (200 OK):
[
{
"id": 3,
"name": "New Feature Request",
"description": "We need a new dashboard feature",
"status": "PENDING_APPROVAL",
"clientId": 5,
"requestedBy": 5,
"approvedBy": null,
"approvalStatus": "PENDING",
"approvalDate": null,
"rejectionReason": null,
"createdAt": "2024-01-25T10:00:00Z",
"updatedAt": "2024-01-25T10:00:00Z"
}
]
Create Project Request
Submit a new project request for approval.
POST /project-requests
Headers:
Authorization: Bearer <token>
Required Role: Any authenticated user
Request Body:
{
"name": "Custom Integration Project",
"description": "We need to integrate our CRM with the platform. This should include bidirectional sync and real-time updates."
}
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Project name |
description |
string | Yes | Detailed description of the project request |
Response (201 Created):
{
"id": 4,
"name": "Custom Integration Project",
"description": "We need to integrate our CRM with the platform...",
"status": "PENDING_APPROVAL",
"clientId": 5,
"requestedBy": 5,
"approvalStatus": "PENDING",
"createdAt": "2024-01-25T14:30:00Z",
"updatedAt": "2024-01-25T14:30:00Z"
}
Error Responses:
| Status | Condition | Response |
|---|---|---|
| 400 | Missing name | Project name is required |
| 400 | Missing description | Project description is required to help us understand your needs |
Get Project Request
Retrieve a specific project request.
GET /project-requests/{id}
Headers:
Authorization: Bearer <token>
Required Role: Request owner, or STAFF/ADMIN/SUPERUSER
Response (200 OK):
{
"id": 4,
"name": "Custom Integration Project",
"description": "...",
"status": "PENDING_APPROVAL",
"approvalStatus": "PENDING",
...
}
Cancel Project Request
Cancel a pending project request.
DELETE /project-requests/{id}
Headers:
Authorization: Bearer <token>
Required Role: Request owner only (cannot cancel after approval)
Response (204 No Content)
Error Responses:
| Status | Condition | Response |
|---|---|---|
| 403 | Not owner | Forbidden: you can only cancel your own requests |
| 403 | Already processed | Cannot cancel: request has already been processed |
| 404 | Not found | Project request not found |
Project Approval (Admin Workflow)
List Pending Projects
Retrieve all projects pending approval.
GET /projects/pending
Headers:
Authorization: Bearer <token>
Required Role: STAFF, ADMIN, or SUPERUSER
Response (200 OK):
[
{
"id": 4,
"name": "Custom Integration Project",
"description": "...",
"status": "PENDING_APPROVAL",
"clientId": 5,
"requestedBy": 5,
"approvalStatus": "PENDING",
"createdAt": "2024-01-25T14:30:00Z",
"updatedAt": "2024-01-25T14:30:00Z"
}
]
Approve or Reject Project
Approve or reject a pending project request.
POST /projects/approve/{id}
Headers:
Authorization: Bearer <token>
Required Role: STAFF, ADMIN, or SUPERUSER
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id |
integer | Project ID |
Request Body (Approve):
{
"action": "approve"
}
Request Body (Reject):
{
"action": "reject",
"reason": "The requested scope is too broad. Please break this into smaller projects."
}
| Field | Type | Required | Description |
|---|---|---|---|
action |
string | Yes | approve or reject |
reason |
string | If rejecting | Reason for rejection |
Response (200 OK - Approved):
{
"message": "Project approved successfully",
"project": {
"id": 4,
"name": "Custom Integration Project",
"status": "PLANNING",
"approvalStatus": "APPROVED",
"approvedBy": 2,
"approvalDate": "2024-01-26T09:00:00Z",
...
}
}
Response (200 OK - Rejected):
{
"message": "Project rejected",
"project": {
"id": 4,
"name": "Custom Integration Project",
"status": "CANCELLED",
"approvalStatus": "REJECTED",
"rejectionReason": "The requested scope is too broad...",
...
}
}
Error Responses:
| Status | Condition | Response |
|---|---|---|
| 400 | Invalid action | Invalid action. Must be 'approve' or 'reject' |
| 400 | Missing reason | Rejection reason is required |
| 400 | Not pending | Project is not pending approval |
| 404 | Not found | Project not found |
Tasks
List Tasks
Retrieve tasks, optionally filtered by project.
GET /tasks
GET /tasks?project_id=1
Headers:
Authorization: Bearer <token>
Required Role: Any authenticated user
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
project_id |
integer | Optional. Filter tasks by project |
Response (200 OK):
[
{
"id": 1,
"projectId": 1,
"title": "Design Homepage Mockup",
"description": "Create wireframes and visual design for homepage",
"status": "IN_PROGRESS",
"assigneeId": 3,
"dueDate": "2024-02-01",
"completedAt": null,
"priority": 2,
"estimatedHours": 8.0,
"actualHours": 3.5,
"createdAt": "2024-01-20T10:00:00Z",
"updatedAt": "2024-01-25T14:00:00Z"
}
]
Authorization Logic:
STAFF/ADMIN/SUPERUSER: See all tasks (optionally filtered)CLIENT: See only tasks for projects they own
Get Task by ID
Retrieve a single task.
GET /tasks/{id}
Headers:
Authorization: Bearer <token>
Required Role: Any authenticated user (with access to parent project)
Response (200 OK):
{
"id": 1,
"projectId": 1,
"title": "Design Homepage Mockup",
"description": "Create wireframes and visual design for homepage",
"status": "IN_PROGRESS",
"assigneeId": 3,
"dueDate": "2024-02-01",
"completedAt": null,
"priority": 2,
"estimatedHours": 8.0,
"actualHours": 3.5,
"createdAt": "2024-01-20T10:00:00Z",
"updatedAt": "2024-01-25T14:00:00Z"
}
Error Responses:
| Status | Condition | Response |
|---|---|---|
| 403 | No access | Forbidden: you do not have access to this task |
| 404 | Not found | Task not found |
Create Task
Create a new task for a project.
POST /tasks
Headers:
Authorization: Bearer <token>
Required Role: STAFF, ADMIN, or SUPERUSER
Request Body:
{
"projectId": 1,
"title": "Implement API Endpoints",
"description": "Create REST endpoints for user management",
"status": "TODO",
"assigneeId": 3,
"dueDate": "2024-02-15",
"priority": 1,
"estimatedHours": 16.0
}
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
projectId |
integer | Yes | - | Parent project ID |
title |
string | Yes | - | Task title |
description |
string | No | - | Task description |
status |
string | No | TODO |
Task status |
assigneeId |
integer | No | - | Assigned user ID |
dueDate |
string | No | - | Due date (YYYY-MM-DD) |
priority |
integer | No | 0 | Priority (higher = more important) |
estimatedHours |
float | No | - | Estimated hours to complete |
Valid Status Values:
TODOIN_PROGRESSREVIEWCOMPLETEDBLOCKED
Response (201 Created):
{
"id": 5,
"projectId": 1,
"title": "Implement API Endpoints",
"status": "TODO",
...
}
Error Responses:
| Status | Condition | Response |
|---|---|---|
| 400 | Missing fields | Task title and project_id are required |
| 403 | No permission | Insufficient permissions |
Update Task
Update an existing task.
PUT /tasks/{id}
Headers:
Authorization: Bearer <token>
Required Role: STAFF, ADMIN, or SUPERUSER
Request Body:
{
"title": "Implement API Endpoints",
"description": "Updated description",
"status": "COMPLETED",
"assigneeId": 3,
"dueDate": "2024-02-15",
"priority": 1,
"estimatedHours": 16.0,
"actualHours": 14.5
}
Special Behavior:
- Setting
statustoCOMPLETEDautomatically setscompletedAttimestamp - Setting
statusto anything else clearscompletedAt
Response (200 OK):
{
"id": 5,
"projectId": 1,
"title": "Implement API Endpoints",
"status": "COMPLETED",
"completedAt": "2024-02-14T16:30:00Z",
...
}
Delete Task
Delete a task.
DELETE /tasks/{id}
Headers:
Authorization: Bearer <token>
Required Role: STAFF, ADMIN, or SUPERUSER
Response (204 No Content)
Work Orders
Work orders are internal documents for tracking billable work. STAFF/ADMIN only.
List Work Orders
Retrieve work orders, optionally filtered by project.
GET /workorders
GET /workorders?project_id=1
Headers:
Authorization: Bearer <token>
Required Role: STAFF, ADMIN, or SUPERUSER
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
project_id |
integer | Optional. Filter by project |
Response (200 OK):
[
{
"id": 1,
"projectId": 1,
"title": "Initial Setup and Configuration",
"description": "Server setup, environment configuration, CI/CD pipeline",
"orderNumber": "WO-2024-001",
"ipfsDocumentCid": "QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco",
"createdBy": 2,
"createdAt": "2024-01-20T10:00:00Z",
"updatedAt": "2024-01-20T10:00:00Z"
}
]
Get Work Order by ID
GET /workorders/{id}
Headers:
Authorization: Bearer <token>
Required Role: STAFF, ADMIN, or SUPERUSER
Response (200 OK):
{
"id": 1,
"projectId": 1,
"title": "Initial Setup and Configuration",
"description": "Server setup, environment configuration, CI/CD pipeline",
"orderNumber": "WO-2024-001",
"ipfsDocumentCid": "QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco",
"createdBy": 2,
"createdAt": "2024-01-20T10:00:00Z",
"updatedAt": "2024-01-20T10:00:00Z"
}
Create Work Order
POST /workorders
Headers:
Authorization: Bearer <token>
Required Role: STAFF, ADMIN, or SUPERUSER
Request Body:
{
"projectId": 1,
"title": "Database Migration",
"description": "Migrate from PostgreSQL 14 to 16",
"orderNumber": "WO-2024-002",
"ipfsDocumentCid": "QmNewCid...",
"createdBy": 2
}
| Field | Type | Required | Description |
|---|---|---|---|
projectId |
integer | Yes | Parent project ID |
title |
string | Yes | Work order title |
orderNumber |
string | Yes | Unique order number |
description |
string | No | Work order description |
ipfsDocumentCid |
string | No | IPFS CID for attached document |
createdBy |
integer | No | User ID who created the work order |
Response (201 Created):
{
"id": 2,
"projectId": 1,
"title": "Database Migration",
"orderNumber": "WO-2024-002",
...
}
Error Responses:
| Status | Condition | Response |
|---|---|---|
| 400 | Missing fields | Work order title, project_id, and order_number are required |
| 409 | Duplicate order number | Work order number already exists |
Update Work Order
PUT /workorders/{id}
Headers:
Authorization: Bearer <token>
Required Role: STAFF, ADMIN, or SUPERUSER
Request Body:
{
"title": "Updated Title",
"description": "Updated description",
"orderNumber": "WO-2024-002",
"ipfsDocumentCid": "QmUpdatedCid..."
}
Response (200 OK)
Delete Work Order
DELETE /workorders/{id}
Headers:
Authorization: Bearer <token>
Required Role: STAFF, ADMIN, or SUPERUSER
Response (204 No Content)
CORS Configuration
Same as auth-service:
| Header | Value |
|---|---|
Access-Control-Allow-Origin |
Configured via CORS_ALLOW_ORIGIN env var |
Access-Control-Allow-Methods |
GET, POST, PUT, DELETE, OPTIONS |
Access-Control-Allow-Headers |
Content-Type, Authorization |
Access-Control-Allow-Credentials |
true |
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
JWT_SECRET |
Yes | - | Secret key for JWT validation (must match auth-service) |
DB_HOST |
Yes | - | PostgreSQL host |
DB_USER |
Yes | - | PostgreSQL user |
DB_PASSWORD |
Yes | - | PostgreSQL password |
DB_NAME |
Yes | - | PostgreSQL database name |
DB_SSL_MODE |
No | require |
SSL mode |
DB_SCHEMA |
No | public |
Schema: dev, testing, prod |
CORS_ALLOW_ORIGIN |
No | http://localhost:8090 |
Allowed CORS origin |
Database Tables
projects
CREATE TABLE projects (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
status VARCHAR(50) DEFAULT 'PLANNING',
client_id INTEGER REFERENCES users(id),
ipfs_metadata_cid VARCHAR(100),
start_date DATE,
end_date DATE,
requested_by INTEGER REFERENCES users(id),
approved_by INTEGER REFERENCES users(id),
approval_status VARCHAR(20) DEFAULT 'APPROVED',
approval_date TIMESTAMP,
rejection_reason TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
tasks
CREATE TABLE tasks (
id SERIAL PRIMARY KEY,
project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
title VARCHAR(255) NOT NULL,
description TEXT,
status VARCHAR(50) DEFAULT 'TODO',
assignee_id INTEGER REFERENCES users(id),
due_date DATE,
completed_at TIMESTAMP,
priority INTEGER DEFAULT 0,
estimated_hours DECIMAL(5,2),
actual_hours DECIMAL(5,2),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
work_orders
CREATE TABLE work_orders (
id SERIAL PRIMARY KEY,
project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
title VARCHAR(255) NOT NULL,
description TEXT,
order_number VARCHAR(50) NOT NULL UNIQUE,
ipfs_document_cid VARCHAR(100),
created_by INTEGER REFERENCES users(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);