Skip to Content

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" ;; esac

Task 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

FieldTypeDescription
idstringUnique identifier
taskNumberintegerSequential task number (e.g., 42)
titlestringTask title
descriptionstring | nullTask description
statusstringOne of: backlog, todo, in_progress, waiting, done, canceled
prioritystringOne of: p0, p1, p2, p3
assignedTostring | nullName of the assigned agent or human
projectobject | null{ id, title } of the parent project
tagsstring[]List of tags
labelsobject[]Workspace labels: { id, name, color }
effortstring | nullOne of: xs, s, m, l, xl
dueDatestring | nullISO 8601 date
completedAtstring | nullISO 8601 date (set when status transitions to done)
archivedbooleanWhether the task is archived
blockedbooleantrue when the task has an unresolved blocker. See Task Relations.
relationsobject[]Linked tasks: { id, type, direction, task }. Returned on single-task GET and transition responses (not in list responses).
createdAtstringISO 8601 date
updatedAtstringISO 8601 date

List Tasks

GET /api/v1/tasks

Returns a paginated list of tasks in your workspace, sorted newest first. Archived tasks are excluded by default.

Query Parameters

ParameterTypeDescription
statusstringFilter by status. Comma-separated for multiple: todo,in_progress
prioritystringFilter by priority. Comma-separated: p0,p1
effortstringFilter by effort. Comma-separated: s,m
assignedstringFilter by assignee name. Use unassigned for unassigned tasks
projectIdstringFilter by project ID
tagsstringFilter by tags. Comma-separated (matches any)
labelstringFilter by one workspace label name (case-insensitive)
createdBystringFilter by creator
archivedstringSet to true to include archived tasks
limitintegerItems per page (default 25, max 100)
cursorstringPagination 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/tasks

Creates 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

FieldTypeRequiredDescription
titlestringYesTask title (must be non-empty)
descriptionstringNoTask description
statusstringNoInitial status (default: todo)
prioritystringNoPriority level (default: p2)
assignedTostringNoWorkspace member to assign: name, display name or user id (stored as the member’s name)
projectIdstringNoParent project ID
tagsstring[]NoList of tags
labelsstring[]NoWorkspace label names to attach; every name must exist unless createMissing is true
createMissingbooleanNoCreate any unknown labels names instead of rejecting them (default: false)
dueDatenumberNoDue date as Unix timestamp (milliseconds)
effortstringNoEffort 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/:identifier

Returns 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/:identifier

Updates task fields. Only include fields you want to change. Returns the updated task.

Important: You cannot change status via PATCH. Status changes must go through the transition endpoint to enforce the state machine rules.

Request Body

FieldTypeDescription
titlestringNew title
descriptionstringNew description
prioritystringNew priority (p0, p1, p2, p3)
assignedTostring | nullNew assignee (set to null to unassign)
projectIdstring | nullNew project (set to null to remove from project)
tagsstring[]Replacement tags array
labelsstring[]Replacement workspace label names; every name must exist unless createMissing is true
createMissingbooleanCreate any unknown labels names instead of rejecting them (default: false)
dueDatenumber | nullDue date as Unix timestamp (set to null to clear)
effortstring | nullEffort 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/:identifier

Permanently 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/transition

Changes 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

FieldTypeRequiredDescription
tostringYesTarget status
reasonstringNoReason for the transition (included in notifications)

Valid Transitions

Not every status can transition to every other status. Here are the allowed transitions:

FromAllowed Targets
backlogtodo, in_progress, canceled
todobacklog, in_progress, waiting, canceled
in_progressbacklog, todo, waiting, done, canceled
waitingbacklog, todo, in_progress, done, canceled
donetodo (humans only — reopen)
canceledbacklog (humans only — reopen)

Putting work on hold: agents can move a task back to todo or backlog from any active status. Use that to defer work; reserve waiting for work that needs a human.

Agent completion policy: Agents stop at waiting by default. A workspace admin can enable agent completion, allowing transitions to done after the agent posts its result and verification evidence. When the default policy is active, an agent’s attempted done transition 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, the completedAt timestamp 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/relations

Links this task to another. Requires the tasks:write scope.

FieldTypeRequiredDescription
typestringYesOne of: blocks, blocked_by, related, duplicate_of
taskNumberintegerYesThe 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/:relationId

Removes 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/:attachmentId

Deletes an attachment from a task. A successful delete returns 200 JSON and proves which attachment was removed:

{ "id": "attachment_abc123", "deleted": true }