`GET /api/workspaces/{id}/backlog/board` (ADR 0045 Stage 1). RBAC: `Read`. The per-project Kanban board (`Vec<ProjectBacklogBoard>` — per-project columns, so the per-card schema stays project-id-free).
const url = 'https://example.com/api/workspaces/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/backlog/board';const options = {method: 'GET', headers: {cookie: 'supacloud_session=<supacloud_session>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url https://example.com/api/workspaces/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/backlog/board \ --cookie supacloud_session=<supacloud_session>Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Workspace ID whose per-project board to build
Responses
Section titled “Responses”Per-project backlog board (one entry per project)
ADR 0045 Stage 1 — one project’s slice of the cross-project workspace board. The
workspace board endpoint returns Vec<ProjectBacklogBoard> (per-project columns)
rather than a flat merged column list, so the per-card schema (BacklogCard,
which carries no project_id) stays stable. columns reuses the existing
BacklogColumn board contract verbatim (one column per state in board order).
object
One board column: a state plus the cards in it (priority ASC NULLS LAST then
created_at — the order list_backlog_items already returns).
object
One backlog card on the board (ADR 0039 S8 CONTRACT). updated_at is RFC3339.
gates_green means precisely “a human drag → done will be ACCEPTED right
now” (BE3): the INDEPENDENT audit passed (a backlog.awaiting_human_merge
audit row exists) AND the item is CURRENTLY in_review (the state = 'in_review' filter — the merge gate refuses to release any non-in_review
item, BE1 — so a parked card carrying a stale marker reads false). It is NOT “merged” and
NOT “awaiting release”; the FE uses it to decide whether to offer the drag →
done gesture. The reviewer bar (required_reviewers) is orthogonal — a green
item may still need N human approvals, which the drag → done records.
object
#532 (U2) — the classification SCOPE axis (small|medium|large), None
until classified.
The issue SOURCE (linear | notion | a git-provider name), #708 X3 (was tracker).
Examplegenerated
[ { "columns": [ { "items": [ { "external_id": "example", "gates_green": true, "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "kind": "example", "last_error": "example", "priority": 1, "readiness": "example", "run_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "scope": "example", "source": "example", "state": "example", "title": "example", "updated_at": "example" } ], "state": "example" } ], "project_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "project_name": "example" }]Authentication required
The canonical JSON body of every error response — the single source of truth
the frontend binds to. Every AppError serializes as this exact shape, and
the generated OpenAPI component ApiErrorBody (with its ErrorCode enum) is
what the frontend error schema is generated from, so there is no hand-written
error schema on either end.
object
Machine-readable, stable error code.
Present only on a quota-exceeded 403 — the inline upgrade-CTA payload.
object
The entitlement feature key that was hit, e.g. apps.max_count.
The plan’s limit for this key.
Where to send the user to upgrade.
Current usage (count or bytes, per the key).
Human-readable message (the server’s English text; the client may localize
by code).
Example
{ "code": "not_found"}Permission denied
The canonical JSON body of every error response — the single source of truth
the frontend binds to. Every AppError serializes as this exact shape, and
the generated OpenAPI component ApiErrorBody (with its ErrorCode enum) is
what the frontend error schema is generated from, so there is no hand-written
error schema on either end.
object
Machine-readable, stable error code.
Present only on a quota-exceeded 403 — the inline upgrade-CTA payload.
object
The entitlement feature key that was hit, e.g. apps.max_count.
The plan’s limit for this key.
Where to send the user to upgrade.
Current usage (count or bytes, per the key).
Human-readable message (the server’s English text; the client may localize
by code).
Example
{ "code": "not_found"}Structured server error
The canonical JSON body of every error response — the single source of truth
the frontend binds to. Every AppError serializes as this exact shape, and
the generated OpenAPI component ApiErrorBody (with its ErrorCode enum) is
what the frontend error schema is generated from, so there is no hand-written
error schema on either end.
object
Machine-readable, stable error code.
Present only on a quota-exceeded 403 — the inline upgrade-CTA payload.
object
The entitlement feature key that was hit, e.g. apps.max_count.
The plan’s limit for this key.
Where to send the user to upgrade.
Current usage (count or bytes, per the key).
Human-readable message (the server’s English text; the client may localize
by code).
Example
{ "code": "not_found"}