Tasks and boards through the API
List boards and columns, search, create, update, move and assign tasks and comment, with the real fields, pagination behavior and errors of axuz_workspace_api.
Tasks and boards live in axuz_workspace_api, at https://api-workspace.axuz.net (staging; production uses axuz.club). All routes sit under /teams/{teamId} and answer JSON. This page describes the contract read from the handlers, not guessed.
Model
A team contains boards. A board may be tied to a channel (in the interface “Channel”; in the database timelines), in which case each channel has at most one board. Each board has columns ordered by position, and each task belongs to one board and one column.
Task fields (Task): id, team_id, board_id, column_id, parent_task_id, root_task_id, title, description, position (fractional number), priority, status, start_at, due_at, is_all_day, created_by, created_at, updated_at, deleted_at, version.
priority:low,normal,high,urgent.status:open,in_progress,done,blocked,cancelled.start_atanddue_at:YYYY-MM-DDwhenis_all_dayistrue; otherwise RFC 3339 in UTC.- Moving to another column does not change
status: they are independent fields.
Board: id, team_id, timeline_id, name, description, created_by, created_at, updated_at. Column: id, team_id, board_id, name, position, created_at, updated_at.
List boards and columns
GET/teams/{teamId}/boards
Returns {"items":[Board]}, newest first. Optional filter ?timeline_id=<uuid>; a channel outside your reach gives 404 channel not found. Without the filter, only boards of channels the caller can reach appear.
/teams/{teamId}/boards/{id}/columns
Returns {"items":[Column]}. To find a channel’s board, use:
/teams/{teamId}/timelines/{timelineId}/board
Response 200 {"board":Board,"columns":[Column]}. A channel without a board returns 404 with {"error":"board_not_found"}: it is not a service failure, it is the signal to “create the board”. The POST on the same route (postTeamsByTeamIdTimelinesByTimelineIdBoard, optional body {"name"}) is idempotent: 201 if it created one, 200 with the existing board.
curl -sS "https://api-workspace.axuz.net/teams/$AXUZ_TEAM_ID/boards/$AXUZ_BOARD_ID/columns" \
-H "Authorization: Bearer $AXUZ_TOKEN"
Creating a board (POST /teams/{teamId}/boards, postTeamsByTeamIdBoards) accepts name, description and timeline_id. Always send name: with an empty name the service asks an AI model for one and, failing that, uses Quadro de <channel name> or Quadro YYYY-MM-DD. Creating a column (POST /teams/{teamId}/boards/{id}/columns) requires name and accepts position from 0 to 999999; renaming or repositioning is PATCH /teams/{teamId}/columns/{id} with name and/or position (an empty body gives 400 nothing to update).
List and search tasks
GET/teams/{teamId}/boards/{id}/tasks
Returns {"items":[Task]} with all live tasks of the board, ordered by column and position. Filter status on the client (the example drops done and cancelled).
/teams/{teamId}/tasks/search
Parameters: q (substring of the title, case-insensitive; empty returns the most recent) and limit (default 10, maximum 25). Each hit has only id, title, board_id, board_name and status. Search looks across the whole team but only shows tasks from channels the caller can reach. To read a whole task, use GET /teams/{teamId}/tasks/{id} (getTeamsByTeamIdTasksById), which gives 404 task not found if it does not exist or is out of reach.
Create a task
POST/teams/{teamId}/tasks
curl -sS -X POST "https://api-workspace.axuz.net/teams/$AXUZ_TEAM_ID/tasks" \
-H "Authorization: Bearer $AXUZ_TOKEN" \
-H "Content-Type: application/json" \
-d '{"board_id":"'"$AXUZ_BOARD_ID"'","column_id":"'"$AXUZ_COLUMN_ID"'","title":"Call the supplier","priority":"high","due_at":"2026-11-03"}'
board_id, column_id and title are required. Optional: description, priority, status, parent_task_id (subtask), start_at, due_at, is_all_day, position. Response 201 with the Task. Real errors: 400 invalid board_id, invalid column_id, title required, invalid priority, invalid status, invalid due_at; 404 board not found when the board does not exist or is out of reach.
This POST is not idempotent: repeating it creates another task. Handle that on your side (the chat message id).
Update, move and assign
PATCH/teams/{teamId}/tasks/{id}
Every field is optional, but at least one is required (400 nothing to update): column_id, title, description, position, priority, status, due_at, start_at. To clear a date send clear_due_at: true or clear_start_at: true. is_all_day only applies with set_all_day: true.
Moving a task to another column is a PATCH with column_id:
curl -sS -X PATCH "https://api-workspace.axuz.net/teams/$AXUZ_TEAM_ID/tasks/$AXUZ_TASK_ID" \
-H "Authorization: Bearer $AXUZ_TOKEN" \
-H "Content-Type: application/json" \
-d '{"column_id":"'"$AXUZ_COLUMN_ID"'"}'
To choose the position within the column use POST /teams/{teamId}/tasks/{id}/reorder (postTeamsByTeamIdTasksByIdReorder) with before_task_id and/or after_task_id; it answers {ok, position, task}. A missing neighbor gives 404 neighbor task not found.
PATCH and reorder require being the task’s author or a channel admin; otherwise 403 forbidden: not author nor channel admin. This matters for bots: since the integration principal is not implemented in that service yet, we cannot state how this rule will apply to a bot.
Assign: POST /teams/{teamId}/tasks/{id}/assignees with {"user_id":"<uuid>"} (postTeamsByTeamIdTasksByIdAssignees). It answers 201 if it assigned now and 200 if already assigned (idempotent). It only accepts active team members (422 user is not a team member). List: GET …/assignees returns {"items":[{task_id,user_id,assigned_by,assigned_at}]}; remove: DELETE …/assignees/{userId} gives 204, even if it did not exist.
Comment
POST/teams/{teamId}/comments
Comments are polymorphic. For a task: {"target_type":"task","target_id":"<task uuid>","body":"text","body_format":"markdown"} (body_format is optional, default markdown). Response 201 with id, team_id, author_id, target_type, target_id, body, body_format, created_at. An empty body gives 400 body required; a missing or out-of-reach task gives 404.
List: GET /teams/{teamId}/comments?target_type=task&target_id=<uuid> (getTeamsByTeamIdComments), in chronological order, {"items":[…]}. The integration token can only comment and list with target_type=task.
Real errors
The body is always {"error":"<message>"}.
| Status | Message | When |
|---|---|---|
400 | invalid body, invalid <field>, nothing to update, title required | Bad JSON, an id that is not a UUID, invalid enum, empty PATCH |
401 | unauthorized | Missing or invalid credential |
403 | forbidden: not author nor channel admin | PATCH or reorder without being author/admin |
404 | board not found, task not found, column not found, channel not found | It does not exist or is out of your reach (no distinction) |
404 | board_not_found | GET /timelines/{id}/board of a channel without a board |
422 | user is not a team member | Assigning someone outside the team |
429 | rate_limited | Mutations above 60 per minute; see Retry-After |
5xx | internal error | Internal failure; retry only GET and PATCH |
Runnable example
examples/bot-node/ is a generic bot in plain Node (native fetch, no dependencies): it lists open tasks, creates one, moves it to another column by name and comments. The HTTP client is isolated in axuz-client.mjs (Bearer, retry with backoff, typed error), the logic in bot.mjs, a fictional chat adapter in chat-adapter.mjs, and configuration comes only from environment variables (config.mjs).
export AXUZ_BOT_TOKEN="axz_bot_..." # from your vault, never from the repository
export AXUZ_TEAM_ID="<team uuid>"
export AXUZ_BOARD_ID="<board uuid>"
node examples/bot-node/index.mjs
# type: /tarefas /nova Call the client /mover <taskId> Fazendo /comentar <taskId> done
The commands are in Portuguese (/tarefas, /nova, /mover, /comentar) because the example bot’s chat messages are; the column name in /mover is the one on your board. The test (tests/examples/bot-node.test.ts) runs the same code against a local mock HTTP server, with no network and no secrets.
What does not exist yet
- Accepting
axz_bot_on the routes above and attributing the action to the bot (author,PATCHrule, rate-limit key). - Keyset pagination on boards, columns, tasks and comments.
Idempotency-KeyonPOST /tasksandPOST /comments.- Delete for the bot: by design it will never exist.