Axuz Developers

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_at and due_at: YYYY-MM-DD when is_all_day is true; 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.

GET/teams/{teamId}/boards/{id}/columns

Returns {"items":[Column]}. To find a channel’s board, use:

GET/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).

GET/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>"}.

StatusMessageWhen
400invalid body, invalid <field>, nothing to update, title requiredBad JSON, an id that is not a UUID, invalid enum, empty PATCH
401unauthorizedMissing or invalid credential
403forbidden: not author nor channel adminPATCH or reorder without being author/admin
404board not found, task not found, column not found, channel not foundIt does not exist or is out of your reach (no distinction)
404board_not_foundGET /timelines/{id}/board of a channel without a board
422user is not a team memberAssigning someone outside the team
429rate_limitedMutations above 60 per minute; see Retry-After
5xxinternal errorInternal 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, PATCH rule, rate-limit key).
  • Keyset pagination on boards, columns, tasks and comments.
  • Idempotency-Key on POST /tasks and POST /comments.
  • Delete for the bot: by design it will never exist.

Updated