`GET /api/inbox/board` — one server-paginated page of the union Posteingang plus the per-source facets and the caller-scoped actionable count (#976). The global `/api` approval layer already gates the account state.
const url = 'https://example.com/api/inbox/board';const options = {method: 'GET'};
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/inbox/boardParameters
Section titled “Parameters”Query Parameters
Section titled “Query Parameters”Source filter chip: workflow | tool | skill | governor | memory
| proposal | suggestion | notification. Absent = the full union.
Page size, clamped to [0, 100]; 0 reads counts/facets only (the badge
poll). Default 25.
Page offset into the merged union (clamped to the 200-row union depth).
Responses
Section titled “Responses”One page of the union inbox.
One page of the union inbox: merged newest-first rows, the depth-capped
union total for the current filter (see MAX_UNION_DEPTH), and the
workspace-wide facets.
object
The per-source totals plus the two derived numbers the shell consumes.
object
What waits on a decision THIS caller can take: workflow/tool gates and pending proposals for every member; skill/memory/suggestion queues only for workspace admins (their decide endpoints are admin-gated); governor only for the owner. THE badge number — notifications never count here.
TRUE total rows per visible source (uncapped — the chips). A source the caller cannot see (role/entitlement) is absent — no chip, no phantom count; a transiently-errored source stays listed at 0.
object
Unread notifications (display-only; read state is not actionability).
Workflow human gate waiting on the caller (one entry per RUN — parallel gates of the same run collapse to the oldest, matching the decide-by-run-id API).
object
Workflow human gate waiting on the caller (one entry per RUN — parallel gates of the same run collapse to the oldest, matching the decide-by-run-id API).
object
Interactive task tool/question/visual approval.
object
Interactive task tool/question/visual approval.
object
#712 UX-3 — the agent (harness) type of the owning task (e.g. claude). None
when the task was deleted.
#712 UX-3 — the owning task’s project name. None when the task has no project
or it was deleted.
#712 UX-3 — the owning task’s title, so the Freigaben inbox row shows WHICH
piece of work is asking (the id alone is an 8-hex prefix). None when the task
was since deleted (LEFT JOIN). Non-sensitive identifier; not redacted.
Induced-skill proposal in draft.
object
Induced-skill proposal in draft.
object
When the proposal was filed (#976 — the union inbox sorts newest-first; without this the row silently sank below month-old notifications).
Governor profile-version proposal (owner only).
object
Governor profile-version proposal (owner only).
object
When the proposal was opened (#976 — union-inbox sort key).
The candidate config, stored in AgentProfileInput shape so approval reuses
the validated update_profile writer. Runtime stays serde_json::Value; the
spec types it as [AgentProfileInput] (the FE reads name / fixed_model /
model_policy off it).
object
ADR 0045 §3.6 — default reasoning effort
(minimal | low | medium | high | max). Optional; preserves the current
value on update, leaves None (inherit the runner default) on create.
Issue #364 / ADR 0039 (D7); #708 who-3 — normal | workflow (ultracode
RETIRED to the effort axis). Optional; preserves the current value on update,
defaults to normal on create.
ADR 0045 §3.2 — opt this profile into the interactive tool-approval gate
(resolve_interactive_approvals_for_run). Optional; preserves the current
value on update, defaults to false on create. #708 W5 wired this WRITE
path (it was read-only before — the create/update never persisted it).
#1018 — per-profile DEFAULT for the harness turn limit (migration 340,
positive). DOUBLE-Option: absent preserves the current value on update / leaves
NULL (no default — the runner’s 500 applies) on create; an explicit null
clears it; a value sets it. “Absent = unchanged” is load-bearing — the
simple profiles tab sends a partial payload, and it must never null a
stored default (the #858 W4a ops-cap pattern).
ADR 0049 (#547 / #548) — opt-in gate for the agent memory.create MCP
tool. Optional; preserves the current value on update, defaults to false
(fail-closed) on create. A workspace admin sets this on the profile editor
to open the unreviewed direct-write path.
#858 W4a — per-profile override of the profile-hourly MCP Ops budget
(migration 320; range 1–1000). Same DOUBLE-Option semantics as
mcp_ops_per_task_cap. The workspace-daily Ops ceiling stays fixed.
#858 W4a — per-profile override of the per-task/per-tool MCP Ops budget
(migration 320; range 1–500). DOUBLE-Option: absent preserves the current
value on update / leaves NULL (the ADR 0028 default, 10) on create; an
explicit null clears the override back to the default; a value sets it.
Admin-gated — the profile PUT already requires workspace admin.
The script-run allowlist. Optional; absent preserves the current value on update
/ leaves None (OPEN — every script allowed) on create; a value (incl. [],
fail-closed) sets it. Mirrors capability_allowlist. #708 W9 wired this so a
workspace manifest round-trips it — the manifest apply supplies a fail-closed
Some([]) default (never the open None) when the manifest omits it.
Memory-governance review item (pending).
object
Memory-governance review item (pending).
object
#903 — the proposal’s own short TITLE (“Closeout: proposed_text,
which is the agent’s summary — so every row read as a sentence fragment.
NULL on pre-#903 rows; the frontend then falls back to the closeout evidence’s
task title and finally to the text snippet.
AI scaffold proposal (pending).
object
AI scaffold proposal (pending).
object
The target App, surfaced under the frontend’s app_id name (the row
stores it as the generic target_id). Only app.file_upsert carries one.
What an approve materialized ({ kind, resource_id }). Populated on the
approve response; null on list/get reads.
The proposing agent task id (when proposer_kind == "agent"); null for
a user-raised proposal.
Reviewer’s reject note. Not persisted on the row (it lives in the audit
trail), so it is null on reads.
Human one-liner derived from kind + payload (e.g. the script name or
“3 files”), so the inbox list reads without parsing the payload itself.
Governor recommendation (active).
object
Governor recommendation (active).
object
Support conversation (ADR 0067 Addendum A). Present in the feed, but never
actionable — it is work, not a decision, so it does not move the badge.
object
Support conversation (ADR 0067 Addendum A). Present in the feed, but never
actionable — it is work, not a decision, so it does not move the badge.
object
provider | local — WHICH SYSTEM owns the assignment. The UI must not
offer a local reassign control on a provider row; that assignment lives in
Chatwoot/Intercom and a local write would be silently overwritten on the
next delivery.
In-app notification (read or unread — read state rides the row).
object
In-app notification (read or unread — read state rides the row).
object
Body text; null for notifications carrying only a title.
Context JSON (never includes raw tokens) — an open map.
object
Email address; set for unregistered invitees; null for registered users.
When the notification was read; null while unread.
FK to the recipient user; null for a pre-registration email-only target.
Example
{ "items": [ { "source": "workflow" } ]}Structured client 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"}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 (also raised when ?source= names a source this caller cannot see)
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"}