Tasks

Everything on the task board, over REST.

Every field on the admin Tasks page is reachable through /api/tasks. Calls require a Bearer token and inherit the caller's RBAC — if you can see a task in the admin, your token can read it; if you can't, it won't. Timestamps are ISO-8601 UTC; errors are JSON.

Resource shape

A task row looks like this on the wire:

{
  "id": 1042,
  "org_id": 1,
  "title": "Review Q2 roadmap",
  "description": "Walk through the draft with the product team",
  "status": "in_progress",
  "priority": "high",
  "position": 0,
  "label": null,
  "category_code": "general",
  "effort": "m",
  "task_type": "task",
  "assigned_employee_id": 17,
  "assigned_team_id": null,
  "assigned_to": null,
  "parent_task_id": null,
  "initiative_id": 4,
  "due_at": "2026-05-10T17:00:00Z",
  "reminder_offset": "1d",
  "tags": ["q2", "product"],
  "created_at": "2026-04-21T14:44:12.123Z",
  "updated_at": "2026-04-21T14:44:12.123Z",
  "created_by": 42,
  "updated_by": 42,
  "deleted_at": null
}

Enum fields

FieldValues
statustodo, in_progress, reviewing, blocked, done, cancelled
prioritylow, medium, high, urgent
effortxs, s, m, l, xl, xxl
task_typefeature, epic, story, task, question
category_codeFrom the task_category enum: general, system_error, bug, improvement, documentation, release_notes
reminder_offset5m, 10m, 15m, 30m, 1h, 2h, 4h, 1d, 2d, 5d, 10d

Three assignee columns, one assignment

A task is assigned to exactly one of assigned_employee_id, assigned_team_id, or assigned_to (the raw users FK for non-employee actors). The admin UI picks the right column based on the lookup the user opens; the API enforces the "one of" rule on write.

Endpoint summary

MethodPathPurpose
GET/api/tasksList with filters
GET/api/tasks/:idFetch one task
POST/api/tasksCreate
PATCH/api/tasks/:idPartial update
POST/api/tasks/:id/moveKanban move (status + position, reconciles adjacent sort)
DELETE/api/tasks/:idSoft-delete (sets deleted_at)
GET/api/tasks/:taskId/commentsList comments, chronological
POST/api/tasks/:taskId/commentsCreate a comment (optional parent_id for reply)
PATCH/api/tasks/:taskId/comments/:idEdit author's own comment
DELETE/api/tasks/:taskId/comments/:idSoft-delete a comment

List tasks

curl 'https://api.wrk.ing/api/tasks?status=in_progress&assigned_to_me=true' \
  -H "Authorization: Bearer $WRKING_TOKEN"

Query parameters

ParameterTypeEffect
statusenumFilter by single status value
labelstringExact match on the free-text label field
category_codeenumExact match on the category_code enum
due_beforeISO 8601Tasks with due_at strictly before this timestamp
assigned_to_mebooleanTasks where any of the three assignee columns resolves to the caller
initiative_idintegerTasks linked to a specific initiative
parent_task_idintegerChildren of a given epic or parent task
limit / offsetintegerPagination

Create a task

curl -X POST https://api.wrk.ing/api/tasks \
  -H "Authorization: Bearer $WRKING_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Review Q2 roadmap",
    "description": "Walk through the draft with the product team",
    "priority": "high",
    "due_at": "2026-05-10T17:00:00Z",
    "reminder_offset": "1d",
    "assigned_employee_id": 17
  }'

A successful create returns 201 Created with the full task row, fires task.post_create on the event stream, and triggers the triage-task prompt hook — a light AI pass that suggests a priority level, a category, and an ideal-owner description based on the task text.

Update a task

curl -X PATCH https://api.wrk.ing/api/tasks/1042 \
  -H "Authorization: Bearer $WRKING_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "status": "done" }'

Any writable field can be included. Setting status to done emits the task.completed lifecycle event in addition to the usual task.post_update — downstream automations (celebration posts, reporting rollups) subscribe to the former.

Move a task on the kanban

curl -X POST https://api.wrk.ing/api/tasks/1042/move \
  -H "Authorization: Bearer $WRKING_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "status": "in_progress", "position": 0 }'

Use this rather than PATCH when you need the server to re-sort adjacent tasks. position is interpreted as the zero-indexed target slot in the target column; the server reconciles the surrounding rows in the same transaction.

Soft-delete a task

curl -X DELETE https://api.wrk.ing/api/tasks/1042 \
  -H "Authorization: Bearer $WRKING_TOKEN"

Returns 204 No Content. deleted_at is stamped and the record drops out of every query that does not explicitly include deleted rows. Child rows (reminders, linked comments, event entries) are NOT cascaded — if you need a hard purge, use the admin bulk-cleanup tool rather than this endpoint.

Threaded comments

Each task carries a discussion thread. Comments are flat rows with an optional parent_id that lets the client assemble a reply tree of any depth — the server does not enforce nesting rules.

curl -X POST https://api.wrk.ing/api/tasks/1042/comments \
  -H "Authorization: Bearer $WRKING_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "body": "Picked this up — checking the logs now.", "parent_id": null }'

Every comment records author_user_id, created_at, and updated_at so edit history is available to reviewers. Soft-delete works the same way as on tasks.

Lifecycle events

The tasks resource emits the following lifecycle events:

EventWhen
task.post_createA new task is inserted (REST, admin UI, workflow, or pack seed).
task.post_updateAny field changes, including status moves.
task.completedStatus transitions into done.
task.comment.createdA new comment is posted on a task.

Subscribe via webhooks or SSE.

Prompt hooks on tasks

Two prompt templates ship wired to the task lifecycle. Both return structured JSON, which downstream UI can render directly as badges, blocker chips, or owner suggestions without client-side parsing.

TemplateHookBehaviour
triage-task task.post_create Suggests a priority level, a short category label, and a one-sentence description of the ideal owner.
detect-task-blockers task.post_update Evaluates stall signals (overdue, no assignee, unresolved dependencies, repeated status thrashing) and proposes unblocking actions.

Both templates are editable through the admin UI under AI → Prompts, and can be disabled per-org without touching code.