This document is a developer-facing guide for using the HappyRobot Task Management API locally.
Default local base URLs:
HTTP API: http://localhost:8080
WebSocket: ws://localhost:8080
All JSON requests should send:
Content-Type: application/json
curl http://localhost:8080/healthzcurl 'http://localhost:8080/api/projects?includeArchived=true'Use includeArchived=true when the UI needs both active and archived projects.
curl -X POST http://localhost:8080/api/projects \
-H 'Content-Type: application/json' \
-d '{
"name": "Launch Plan",
"description": "Demo workspace",
"metadata": {}
}'curl http://localhost:8080/api/projects/{projectId}The snapshot is intentionally lightweight. Tasks are loaded separately through cursor pagination, and comments are loaded per task.
curl -X PATCH http://localhost:8080/api/projects/{projectId} \
-H 'Content-Type: application/json' \
-d '{
"name": "Updated Launch Plan",
"description": "Updated description"
}'curl -X POST http://localhost:8080/api/projects/{projectId}/archivecurl -X POST http://localhost:8080/api/projects/{projectId}/unarchivecurl -X DELETE http://localhost:8080/api/projects/{projectId}Project delete is permanent in this MVP and cascades to tasks, dependencies, comments, and project events.
curl 'http://localhost:8080/api/projects/{projectId}/tasks?limit=50'Response:
{
"items": [],
"nextCursor": "opaque-cursor"
}Load the next page:
curl 'http://localhost:8080/api/projects/{projectId}/tasks?limit=50&cursor={nextCursor}'curl -X POST http://localhost:8080/api/projects/{projectId}/tasks \
-H 'Content-Type: application/json' \
-d '{
"title": "Design sync model",
"status": "todo",
"assignedTo": [],
"configuration": {
"priority": "high",
"description": "Emit project-scoped delta events instead of full project payloads.",
"tags": ["sync"],
"customFields": {}
},
"dependencies": []
}'Supported task statuses:
todo
in_progress
blocked
done
Patch only the fields that changed:
curl -X PATCH http://localhost:8080/api/projects/{projectId}/tasks/{taskId} \
-H 'Content-Type: application/json' \
-d '{
"status": "in_progress"
}'Update title and summary:
curl -X PATCH http://localhost:8080/api/projects/{projectId}/tasks/{taskId} \
-H 'Content-Type: application/json' \
-d '{
"title": "Implement WebSocket replay",
"configuration": {
"priority": "high",
"description": "Replay missed project events after reconnect.",
"tags": [],
"customFields": {}
}
}'Update dependencies:
curl -X PATCH http://localhost:8080/api/projects/{projectId}/tasks/{taskId} \
-H 'Content-Type: application/json' \
-d '{
"dependencies": ["blockingTaskId"]
}'The backend validates that dependencies exist, belong to the same project, are not self-links, are not duplicates, and do not create dependency cycles.
curl -X DELETE http://localhost:8080/api/projects/{projectId}/tasks/{taskId}Task delete is rejected if another task currently depends on it.
curl http://localhost:8080/api/projects/{projectId}/tasks/{taskId}/commentscurl -X POST http://localhost:8080/api/projects/{projectId}/tasks/{taskId}/comments \
-H 'Content-Type: application/json' \
-d '{
"content": "Looks good to me.",
"author": "Amy"
}'curl -X PATCH http://localhost:8080/api/projects/{projectId}/tasks/{taskId}/comments/{commentId} \
-H 'Content-Type: application/json' \
-d '{
"content": "Updated comment text."
}'Only comment content is editable. The original author and timestamp are preserved.
curl http://localhost:8080/api/metrics/projectsReturns aggregate project progress, status counts, completion percentages, blocked percentages, and comment totals without requiring the frontend to load every task for every project.
curl 'http://localhost:8080/api/projects/{projectId}/events?afterVersion=0'Clients use this after reconnect to recover missed updates.
Event shape:
{
"type": "task.updated",
"projectId": "project-id",
"entityId": "task-id",
"version": 12,
"payload": {},
"createdAt": "2026-07-11T12:00:00Z"
}Connect to project-scoped realtime updates:
ws://localhost:8080/api/projects/{projectId}/ws?lastSeenVersion={version}
Recommended client behavior:
- Load project snapshot and current task page.
- Track the latest event
version. - Connect WebSocket with
lastSeenVersion. - Apply incoming events idempotently by entity ID.
- On reconnect, call the event replay endpoint with the last seen version, then reconnect the socket.
Most validation failures return:
{
"error": "invalid input: explanation"
}Common cases:
- Invalid task status.
- Dependency does not exist.
- Dependency belongs to another project.
- Dependency would create a cycle.
- Cannot delete a task that is still blocking another task.