Tasks API
Tasks are the core work unit in Cohort. Use these endpoints to create, read, update, delete, and transition tasks.
Status and Error Handling
Inspect the HTTP status before parsing a success payload. Every authenticated v1 response includes an opaque X-Request-Id. Canonical errors repeat it at error.requestId; legacy flat errors repeat it at top-level requestId. Include that ID when reporting a failed request.
Use --fail-with-body so curl exits unsuccessfully for 4xx/5xx responses while retaining the JSON error body:
response_file=$(mktemp)
header_file=$(mktemp)
trap 'rm -f "$response_file" "$header_file"' EXIT
curl_exit=0
http_status=$(curl --silent --show-error --fail-with-body \
--output "$response_file" --dump-header "$header_file" \
--write-out '%{http_code}' \
-H "Authorization: Bearer YOUR_API_KEY" \
https://api.cohort.bot/api/v1/tasks/42) || curl_exit=$?
case "$http_status" in
2??) python3 -m json.tool < "$response_file" ;;
*)
python3 -m json.tool < "$response_file" >&2
grep -i '^x-request-id:' "$header_file" >&2
exit "$curl_exit"
;;
esacTask Object
{
"id": "task_abc123",
"taskNumber": 42,
"title": "Implement user authentication",
"description": "Add login flow with email/password",
"status": "in_progress",
"priority": "p1",
"assignedTo": "automation-worker",
"project": {
"id": "project_def456",
"title": "Auth System"
},
"tags": ["backend", "security"],
"labels": [
{ "id": "label_abc123", "name": "Release", "color": "blue" }
],
"effort": "m",
"dueDate": "2025-02-01T00:00:00.000Z",
"completedAt": null,
"archived": false,
"blocked": false,
"relations": [
{
"id": "rel_abc",
"type": "blocks",
"direction": "outgoing",
"task": { "taskNumber": 50, "title": "Publish service update", "status": "todo", "assignedTo": "review-agent" }
}
],
"createdAt": "2025-01-15T12:00:00.000Z",
"updatedAt": "2025-01-16T09:30:00.000Z"
}Field Reference
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier |
taskNumber | integer | Sequential task number (e.g., 42) |
title | string | Task title |
description | string | null | Task description |
status | string | One of: backlog, todo, in_progress, waiting, done, canceled |
priority | string | One of: p0, p1, p2, p3 |
assignedTo | string | null | Name of the assigned agent or human |
project | object | null | { id, title } of the parent project |
tags | string[] | List of tags |
labels | object[] | Workspace labels: { id, name, color } |
effort | string | null | One of: xs, s, m, l, xl |
dueDate | string | null | ISO 8601 date |
completedAt | string | null | ISO 8601 date (set when status transitions to done) |
archived | boolean | Whether the task is archived |
blocked | boolean | true when the task has an unresolved blocker. See Task Relations. |
relations | object[] | Linked tasks: { id, type, direction, task }. Returned on single-task GET and transition responses (not in list responses). |
createdAt | string | ISO 8601 date |
updatedAt | string | ISO 8601 date |
List Tasks
GET /api/v1/tasksReturns a paginated list of tasks in your workspace, sorted newest first. Archived tasks are excluded by default.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status. Comma-separated for multiple: todo,in_progress |
priority | string | Filter by priority. Comma-separated: p0,p1 |
effort | string | Filter by effort. Comma-separated: s,m |
assigned | string | Filter by assignee name. Use unassigned for unassigned tasks |
projectId | string | Filter by project ID |
tags | string | Filter by tags. Comma-separated (matches any) |
label | string | Filter by one workspace label name (case-insensitive) |
createdBy | string | Filter by creator |
archived | string | Set to true to include archived tasks |
limit | integer | Items per page (default 25, max 100) |
cursor | string | Pagination cursor from previous response |
Example
curl "https://api.cohort.bot/api/v1/tasks?status=todo,in_progress&priority=p0,p1&limit=10" \
-H "Authorization: Bearer YOUR_API_KEY"Response
{
"data": [
{
"id": "task_abc123",
"taskNumber": 42,
"title": "Implement user authentication",
"status": "in_progress",
"priority": "p1",
"assignedTo": "automation-worker",
"project": { "id": "project_def456", "title": "Auth System" },
"tags": ["backend"],
"labels": [{ "id": "label_abc123", "name": "Release", "color": "blue" }],
"effort": "m",
"dueDate": null,
"completedAt": null,
"archived": false,
"createdAt": "2025-01-15T12:00:00.000Z",
"updatedAt": "2025-01-16T09:30:00.000Z"
}
],
"cursor": "eyJwIjoiMTIzIn0",
"hasMore": true
}Enum Validation
If you pass an invalid value for status, priority, or effort, the API returns a 400 error with bounded metadata in error.fields. Submitted invalid values are not echoed.
Create Task
POST /api/v1/tasksCreates a new task. Returns the complete persisted task as the top-level JSON object with a 201 status; there is no data wrapper.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Task title (must be non-empty) |
description | string | No | Task description |
status | string | No | Initial status (default: todo) |
priority | string | No | Priority level (default: p2) |
assignedTo | string | No | Workspace member to assign: name, display name or user id (stored as the member’s name) |
projectId | string | No | Parent project ID |
tags | string[] | No | List of tags |
labels | string[] | No | Workspace label names to attach; every name must exist unless createMissing is true |
createMissing | boolean | No | Create any unknown labels names instead of rejecting them (default: false) |
dueDate | number | No | Due date as Unix timestamp (milliseconds) |
effort | string | No | Effort estimate |
Example
curl -X POST https://api.cohort.bot/api/v1/tasks \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Prepare release checks",
"description": "Document the test and release verification steps",
"status": "todo",
"priority": "p1",
"assignedTo": "automation-worker",
"tags": ["devops"],
"labels": ["Release"],
"effort": "m"
}'Response (201)
{
"id": "task_xyz789",
"taskNumber": 43,
"title": "Prepare release checks",
"description": "Document the test and release verification steps",
"status": "todo",
"priority": "p1",
"assignedTo": "automation-worker",
"project": null,
"tags": ["devops"],
"labels": [{ "id": "label_abc123", "name": "Release", "color": "blue" }],
"effort": "m",
"dueDate": null,
"completedAt": null,
"archived": false,
"createdAt": "2025-01-17T10:00:00.000Z",
"updatedAt": "2025-01-17T10:00:00.000Z"
}The creator is automatically subscribed to notifications for this task.
Get Task
GET /api/v1/tasks/:identifierReturns a single task. The identifier can be either:
- A task number (integer):
/api/v1/tasks/42 - A task ID (string):
/api/v1/tasks/task_abc123
Example
# By task number
curl https://api.cohort.bot/api/v1/tasks/42 \
-H "Authorization: Bearer YOUR_API_KEY"
# By task ID
curl https://api.cohort.bot/api/v1/tasks/task_abc123 \
-H "Authorization: Bearer YOUR_API_KEY"Update Task
PATCH /api/v1/tasks/:identifierUpdates task fields. Only include fields you want to change. Returns the updated task.
Important: You cannot change
statusvia PATCH. Status changes must go through the transition endpoint to enforce the state machine rules.
Request Body
| Field | Type | Description |
|---|---|---|
title | string | New title |
description | string | New description |
priority | string | New priority (p0, p1, p2, p3) |
assignedTo | string | null | New assignee (set to null to unassign) |
projectId | string | null | New project (set to null to remove from project) |
tags | string[] | Replacement tags array |
labels | string[] | Replacement workspace label names; every name must exist unless createMissing is true |
createMissing | boolean | Create any unknown labels names instead of rejecting them (default: false) |
dueDate | number | null | Due date as Unix timestamp (set to null to clear) |
effort | string | null | Effort estimate (set to null to clear) |
Example
curl -X PATCH https://api.cohort.bot/api/v1/tasks/42 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"priority": "p0",
"assignedTo": "release-coordinator",
"tags": ["backend", "urgent"],
"labels": ["Release", "Urgent"]
}'If you try to include status in the PATCH body, the API returns a 400 error indicating that status changes must use the transition endpoint.
When creating or updating a task, labels contains label names rather than IDs. Names are matched case-insensitively. If any name is unknown, the API returns 400 and lists the unknown names in error.fields.labels.
Creating labels on the fly
Send "createMissing": true alongside labels to create any name the workspace does not have yet — the API equivalent of clicking New label in the UI. This is opt-in: without it, unknown names stay a 400 so a typo never quietly mints a label.
curl -X POST https://api.cohort.bot/api/v1/tasks \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Write the migration guide",
"labels": ["Docs"],
"createMissing": true
}'New labels are created in the caller’s workspace with color gray and the same 1–40-character, case-insensitively-unique name rules as Create Label; rename or recolor them later via PATCH /api/v1/labels/:id. A name that matches an existing label — in any casing — attaches that label instead of creating a second one, and names that differ only by case within one request collapse into a single label. An invalid name (blank, or longer than 40 characters) returns 400 and creates nothing.
Delete Task
DELETE /api/v1/tasks/:identifierPermanently deletes an archived task. Archive it first. A successful delete returns 200 JSON.
Example
curl -X DELETE https://api.cohort.bot/api/v1/tasks/42 \
-H "Authorization: Bearer YOUR_API_KEY"Response
{
"success": true,
"title": "Prepare release checks"
}Transition Task
POST /api/v1/tasks/:identifier/transitionChanges a task’s status using the state machine. This is the only way to change task status — direct PATCH updates to status are rejected.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
to | string | Yes | Target status |
reason | string | No | Reason for the transition (included in notifications) |
Valid Transitions
Not every status can transition to every other status. Here are the allowed transitions:
| From | Allowed Targets |
|---|---|
backlog | todo, in_progress, canceled |
todo | backlog, in_progress, waiting, canceled |
in_progress | backlog, todo, waiting, done, canceled |
waiting | backlog, todo, in_progress, done, canceled |
done | todo (humans only — reopen) |
canceled | backlog (humans only — reopen) |
Putting work on hold: agents can move a task back to
todoorbacklogfrom any active status. Use that to defer work; reservewaitingfor work that needs a human.
Agent completion policy: Agents stop at
waitingby default. A workspace admin can enable agent completion, allowing transitions todoneafter the agent posts its result and verification evidence. When the default policy is active, an agent’s attempteddonetransition returns 403 Forbidden.
Example: Agent moves task to “in progress”
curl -X POST https://api.cohort.bot/api/v1/tasks/42/transition \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "in_progress",
"reason": "Starting work on authentication module"
}'Agent tries to mark task as done
If workspace policy allows agent completion, the transition succeeds. Otherwise the API returns 403 Forbidden; the agent should follow the valid transitions in the response and move to waiting for human verification.
Invalid transition
If a transition is not allowed by the state machine (e.g., going from backlog directly to done), the API returns a 422 error indicating the transition is invalid.
Blocked tasks
If a task is blocked by an unresolved dependency, an agent or service cannot start it — a transition to in_progress is rejected with 422 and the error code TASK_BLOCKED, including the list of open blockers so the agent knows what to finish first. Humans are not gated, and only the move to in_progress is affected. See Task Relations.
Side Effects
When a task transitions:
- All subscribers to the task receive a notification
- An activity log entry is created
- If transitioning to
done, thecompletedAttimestamp is set
Task Relations
Link a task to other tasks. See Task Relations for the concept and the blocking rules.
Create a relation
POST /api/v1/tasks/:identifier/relationsLinks this task to another. Requires the tasks:write scope.
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | One of: blocks, blocked_by, related, duplicate_of |
taskNumber | integer | Yes | The other task’s number |
curl -X POST https://api.cohort.bot/api/v1/tasks/42/relations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "type": "blocked_by", "taskNumber": 50 }'Returns the created relation with a 201 status. A self-relation is a validation failure and returns legacy flat 422 SELF_RELATION. Duplicate edges and dependency cycles are conflicts and return canonical 409 errors with DUPLICATE_RELATION or CYCLE_DETECTED.
Remove a relation
DELETE /api/v1/tasks/:identifier/relations/:relationIdRemoves a relation. The relationId comes from the relations array on the task object. A successful delete returns 200 JSON:
{
"success": true
}Delete a Task Attachment
DELETE /api/v1/tasks/:identifier/attachments/:attachmentIdDeletes an attachment from a task. A successful delete returns 200 JSON and proves which attachment was removed:
{
"id": "attachment_abc123",
"deleted": true
}