Skip to main content
Home›Docs›API Reference›Boards and workspaces
API Reference

Boards and workspaces

Manage workspaces and their runtime defaults, kanban boards with columns and tasks, agent task dispatch and approvals, task comments, and the Aria goal-check sweep.

Boards and workspaces

Workspaces isolate each team's data — boards, agents, files, runtime configuration — from other teams. Boards are kanban-style task queues inside a workspace: tasks live in columns, and a task assigned to an agent can be dispatched as an agent run directly from the board.

All endpoints require authentication via X-API-Key: pk_live_... or Authorization: Bearer <jwt-or-key>. The active workspace is resolved from the X-Workspace-Id header (or the workspace_id query parameter) and falls back to your Personal workspace when omitted. Read endpoints require the API-key scope mcp:agents:read; write endpoints require mcp:agents:write — JWT callers are gated by workspace role instead, noted per endpoint. Board endpoints operate on the active workspace and return 404 for resources outside it.

Workspaces

Workspace object fields:

Field Type Description
id string (UUID) Workspace identifier
name string Display name
slug string URL-safe unique slug
plan string Billing plan (free on creation)
auto_approve boolean Whether agent task proposals skip manual approval
summoro_workspace_id string (UUID) | null Mirrored control-plane workspace id
created_at, updated_at string (ISO 8601) Timestamps

POST /v1/workspaces/ensure-personal

Return the caller's personal workspace, creating it (with an owner membership) when none exists. Despite being a POST, it requires only the read scope mcp:agents:read.

Request body (optional): name (string) — defaults to "<user-id-prefix> Personal".

Response — a workspace object.

curl -X POST "https://api.perceive8.com/v1/workspaces/ensure-personal" \
  -H "X-API-Key: pk_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"name": "Ada Personal"}'

GET /v1/workspaces

List workspaces the caller is a member of, newest first. Response — an array of workspace objects.

POST /v1/workspaces

Create a workspace owned by the caller. JWT callers need the admin or owner role in the active workspace.

Request body

{
  "name": "Sales",
  "slug": "sales"
}

name (string, required, 1–255); slug (string, optional, max 63 — slugified from name when omitted; a random suffix is appended on collision).

Response — the created workspace object.

GET /v1/workspaces/{workspace_id}

Get a workspace by id. The caller must be a member (404 otherwise; 400 on a malformed UUID). Response — a workspace object.

PATCH /v1/workspaces/{workspace_id}

Rename a workspace. JWT callers need admin/owner; membership required. Body: name (string, required, 1–255). Response — the updated workspace object.

GET /v1/workspaces/{workspace_id}/runtime/config

Get the workspace's runtime environment defaults, applied to agent runtimes that don't override them. Query parameter: runtime_kind (string, default openclaw).

Response — a flat object of environment variable name → value; {} when unset.

PUT /v1/workspaces/{workspace_id}/runtime/config

Replace the env defaults for a (workspace, runtime_kind) pair. JWT callers need admin/owner.

Request body

{
  "runtime_kind": "openclaw",
  "env": { "LOG_LEVEL": "debug" }
}

runtime_kind (string, default openclaw); env (object of string → string, default {}) — stored as a full replacement.

Response — the stored env object.

Boards

Board object fields: id (integer), workspace_id (UUID), title, description (string | null), color (string), creator (string | null), created_at, updated_at (ISO 8601), columns (array of column objects on create, otherwise null).

GET /v1/boards

List boards in the active workspace, newest first. Response — an array of board objects.

POST /v1/boards

Create a board. Write access required.

Request body

{
  "title": "Outbound Q3",
  "description": "Sequences and follow-ups",
  "color": "bg-blue-500",
  "creator": "[email protected]",
  "columns": [{ "title": "To do", "sort_order": 0 }]
}
  • title (string, required, 1–255)
  • description, creator (string, optional)
  • color (string, default bg-blue-500)
  • workspace_id (UUID, optional — must equal the active workspace when sent, else 403)
  • columns (array of {title, sort_order}, optional — when omitted, five defaults are seeded: Backlog, Blocked, In Progress, In Review, Done)

Response — the board object including the created columns array.

curl -X POST "https://api.perceive8.com/v1/boards" \
  -H "X-API-Key: pk_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"title": "Outbound Q3"}'

GET /v1/boards/{board_id}

Get a board with its columns, tasks, and workspace members.

Response — an object with:

Field Description
board The board object
columns Column objects ordered by sort_order, each with a tasks array of task objects
users Workspace members as {id, email, full_name}

PATCH /v1/boards/{board_id}

Update a board. Write access required. Body: any subset of title, description, color (400 when empty). Response — the updated board object.

DELETE /v1/boards/{board_id}

Delete a board together with its columns and tasks. Write access required. Response — {"ok": true}.

Columns

Column object fields: id (integer), board_id (integer), title, sort_order (integer), created_at, updated_at (ISO 8601).

POST /v1/boards/{board_id}/columns

Create a column. Write access required. Body: title (string, required, 1–255), sort_order (integer, default 0). Response — the created column object.

PATCH /v1/boards/{board_id}/columns/{column_id}

Update title and/or sort_order. Write access required; 400 when no fields are sent. Response — the updated column object.

DELETE /v1/boards/{board_id}/columns/{column_id}

Delete a column and all tasks in it. Write access required. Response — {"ok": true}.

Tasks

Task object fields:

Field Type Description
id integer Task identifier
column_id integer Column the task sits in
title string Task title
description string | null —
assignee string | null Free-form display assignee (send "" on PATCH to clear)
due_date string (date) | null YYYY-MM-DD
priority string Free-form, default medium
sort_order integer Position within the column
assignee_type string | null user or agent
assignee_agent_id string (UUID) | null Owning agent; required when assignee_type is agent
trigger object {"type": "manual"} (default) or {"type": "schedule"}
schedule_cron string | null 5-field cron expression for recurring runs
status string queued (default), running, needs_review, done, failed, cancelled
current_run_id string (UUID) | null Latest agent run
analysis_id, report_id string (UUID) | null Linked analysis / report
source_action_item_id string | null Originating action item id
category string | null Server-populated category
risk_tier string | null low, internal, outward, pipeline (server-populated)
integration_kind string | null Server-populated integration type
integration_payload object | null Server-populated integration data
execution_result object | null Last run outcome (last_run_outcome, reply_preview, error, finished_at)
approval_status string not_required (default), pending, approved, rejected
approval_decided_by string (UUID) | null User who decided
approval_decided_at string (ISO 8601) | null Decision timestamp
created_at, updated_at string (ISO 8601) Timestamps

POST /v1/boards/{board_id}/tasks

Create a task in a column of the board. Write access required; 404 when the column is not on this board.

Request body

{
  "column_id": 41,
  "title": "Draft renewal email for Acme",
  "priority": "high",
  "due_date": "2026-08-29",
  "assignee_type": "agent",
  "assignee_agent_id": "a12b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
  "trigger": { "type": "schedule" },
  "schedule_cron": "0 9 * * 1"
}
  • column_id (integer, required), title (string, required, 1–500)
  • description, assignee (string, optional); due_date (date, optional)
  • priority (string, default medium), sort_order (integer, default 0)
  • assignee_type (user | agent) with assignee_agent_id (UUID of a non-archived agent in the workspace; required when agent)
  • trigger — {"type": "manual"} (default) or {"type": "schedule"}; other types are rejected with 400
  • schedule_cron — must be a 5-field cron expression, else 400

Response — the created task object.

PATCH /v1/boards/{board_id}/tasks/{task_id}

Update a task. Write access required. Accepts any subset of the create fields plus status; 400 when no fields are sent.

  • assignee: "" clears the display assignee (stored as null).
  • assignee_agent_id without assignee_type: "agent" returns 400.
  • Reassigning away from an agent clears schedule_cron and resets trigger to manual (explicit values in the same request win).
  • Setting trigger to {"type": "manual"} clears schedule_cron.

Response — the updated task object.

POST /v1/boards/{board_id}/tasks/{task_id}/move

Move a task to another column and/or position. Write access required. Body: column_id (integer, required) and sort_order (integer, required). Response — the updated task object.

DELETE /v1/boards/{board_id}/tasks/{task_id}

Delete a task. Write access required. Response — {"ok": true}.

POST /v1/boards/{board_id}/tasks/{task_id}/dispatch

Dispatch an agent-assigned task as an agent run. Any workspace member can dispatch (JWT); API keys need mcp:agents:write. The run executes synchronously: the response returns after the run finishes, with the task's status and execution_result written back. A done task is auto-moved to the board's Done column.

Response — {"task": <updated task object>, "run_id": "<agent-run UUID>"}.

Errors: 400 when the task is not agent-assigned; 409 when it is already running, its approval was rejected, or approval is pending and the caller is not an approver (an owner/admin/manager's dispatch doubles as the approval); 503 when the dispatch backend is not configured.

curl -X POST "https://api.perceive8.com/v1/boards/12/tasks/87/dispatch" \
  -H "X-API-Key: pk_live_xxxxxxxxxxxx"

POST /v1/boards/{board_id}/tasks/{task_id}/approve

Approve a task with approval_status: "pending" so it can be dispatched. JWT callers need the owner, admin, or manager role; API keys need mcp:agents:write. 409 when the task is not awaiting approval. Response — the updated task object with approval_status: "approved" and approval_decided_by/approval_decided_at set.

POST /v1/boards/{board_id}/tasks/{task_id}/reject

Reject an approval-pending task; rejected tasks cannot be dispatched. Same authorization and 409 semantics as approve. Response — the updated task object with approval_status: "rejected".

GET /v1/boards/{board_id}/tasks/{task_id}/comments

List comments on a task, oldest first.

Response — an array of comment objects: id (UUID), task_id (integer), author_type (user | agent), author_user_id (UUID | null), author_agent_id (UUID | null), author_email, author_full_name, author_agent_name (string | null), body (string), created_at (ISO 8601).

POST /v1/boards/{board_id}/tasks/{task_id}/comments

Add a comment as the current user. Write access required. Agent comments are written by the platform, never through this endpoint. Body: body (string, required, 1–10000). Response — the created comment object.

Aria goal check

Aria goal check is a periodic sweep that reviews a workspace's boards and proposes — and optionally creates and dispatches — tasks toward the workspace's goals. These endpoints manage the sweep settings, trigger runs manually, and inspect run history. The path workspace_id must match the caller's active workspace (403 otherwise).

GET /v1/workspaces/{workspace_id}/aria-goal-check/settings

Get the goal-check settings. Any workspace member.

Response

{
  "enabled": true,
  "frequency_hours": 4,
  "mode": "balanced",
  "last_run_at": "2026-08-18T06:00:00+00:00"
}

Defaults when never configured: enabled: true, frequency_hours: 4, mode: "balanced", last_run_at: null.

PATCH /v1/workspaces/{workspace_id}/aria-goal-check/settings

Update the settings. Requires the admin or owner role. Body: any subset of enabled (boolean), frequency_hours (integer, 1–168), mode (conservative | balanced | aggressive); 400 when empty. Response — the settings object, same shape as the GET.

POST /v1/workspaces/{workspace_id}/aria-goal-check/trigger

Manually queue a goal-check run. Requires the admin or owner role. The background worker skips the run when the sweep is disabled, when the last run is newer than frequency_hours, or when the workspace is not a main workspace.

Response — {"task_id": "<UUID>", "message": "Goal check triggered"}.

GET /v1/workspaces/{workspace_id}/aria-goal-check/runs

List recent goal-check runs, newest first. Any workspace member. Query parameters: limit (integer, default 20), offset (integer, default 0).

Response — an array of run summaries:

Field Type Description
id string (UUID) Run identifier
started_at string (ISO 8601) Start timestamp
finished_at string (ISO 8601) | null End timestamp (null while running)
status string running, completed, or failed
proposed_actions_count integer Proposed task actions
created_task_count integer Tasks created
dispatched_task_count integer Tasks dispatched to agents
model string | null LLM used for proposals
error string | null Failure detail when failed

GET /v1/workspaces/{workspace_id}/aria-goal-check/runs/{run_id}

Get one run with its snapshot and proposed tasks. Any workspace member; 400 on a malformed run_id, 404 when the run belongs to another workspace.

Response — the shared run fields (id, started_at, finished_at, status, model, error) plus:

Field Type Description
workspace_id string (UUID) Workspace the run swept
snapshot object | null Workspace state captured at run start
proposed_tasks array Proposed task actions (objects)
created_task_ids array (integer) Created task ids
dispatched_task_ids array (integer) Dispatched task ids
input_tokens, output_tokens integer | null LLM token usage