STEP 8 OF 10
Runs & Boundaries
Every time an agent does something, real or dry-run, it becomes an AgentExecution row you can
open and read step by step. This guide covers /runs, the per-execution timeline, and the
Boundaries tab that shows what an agent is actually allowed to do.
Starting a run
An execution is dispatched to the tenant's data-plane runtime, not run in-process. There is no
in-process fallback: if the runtime is unreachable, the execution comes back status: failed with
the real error, never a silently degraded answer.
curl -X POST "https://your-control-plane-host/api/v1/executions?tenant_id=$TENANT_ID" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"agent_id": "<agent_id>", "purpose": "manual", "input": {"content": "Summarize open matters."}}'
The response is 202 with the execution row (steps included, though usually empty at the moment
you get the response back — the runtime is still working). Poll it, or subscribe to the stream:
curl "https://your-control-plane-host/api/v1/executions/{execution_id}" -H "Authorization: Bearer $TOKEN"
curl "https://your-control-plane-host/api/v1/executions/{execution_id}/stream" -H "Authorization: Bearer $TOKEN"
A run can be refused before it ever reaches the runtime — 429/409/412 with a machine-readable
code: budget_exceeded (the tenant's monthly cap), rate_limit_exceeded, or cost_cap_exceeded
(a per-agent guardrail, see below). runtime_not_configured means the tenant has no data-plane
runtime registered yet.
The step timeline
Every execution's steps array is ordered and typed. The kinds you'll actually see:
memory_read— the runtime pulled context from one or more of the 5 memory layers before planningllm_turn— one call to the model, this is where tokens and cost accruetool_call— the agent invoked a tool (a connector action, a KG search, a gateway action request, ...)policy_decision— the agent's policy graph allowed or denied a tool call;allow: falsemeans the model asked for something outside its boundaries and was refused, not that the run failedapproval— a gateway action the agent requested is sitting in the human approval queuememory_write— the runtime wrote a new episodic trace or updated a semantic entityoutput— the final answererror— something genuinely broke
GET /executions/{id} returns the full list. Each AgentExecution also carries tokens_in,
tokens_out, cost_usd, and pricing_status (priced or unpriced — an unpriced run is never
shown as $0, the model just wasn't in the pricing table) and trace_chain_verified: the runtime
emits a hash-chained trace, and the control plane verifies it wasn't tampered with in transit
before it's ever displayed.
Workflow runs nest agent runs
A /workflows/{id}/execute run that includes an agent node doesn't call the model itself — it
dispatches a real child AgentExecution for that node, waits for it to finish, and folds its cost
and tokens into the workflow total. GET /executions?kind=workflow lists workflow runs with their
child executions attached; opening a workflow run in the dashboard shows every node's real
sub-timeline, not a summary.
curl "https://your-control-plane-host/api/v1/executions?kind=workflow&tenant_id=$TENANT_ID" \
-H "Authorization: Bearer $TOKEN"
curl "https://your-control-plane-host/api/v1/executions/cost?tenant_id=$TENANT_ID" \
-H "Authorization: Bearer $TOKEN"
/executions/cost is what the Cost tab and /runs list read: spend by day, by model, by agent,
and the tenant's unpriced_count so a stale pricing table never hides real spend.
The dashboard
/runs lists every execution (agent and workflow, kind filterable) with cost, tokens, duration,
and status. /runs/{id} is the timeline view — a resizable, collapsible three-pane layout (file
tree of steps on the left, detail pane in the middle, a right rail for metadata) with a fullscreen
toggle for reading a long multi-step run without the rest of the dashboard chrome. Layout state
(pane widths, which panels are collapsed) persists per browser.
Boundaries
/agents/{id} → Boundaries answers "what can this agent actually do" as a three-column table —
Can do, Needs approval, Cannot do — built from the agent's real policy graph, not from
documentation someone has to keep in sync by hand.
curl "https://your-control-plane-host/api/v1/workforce/agents/{agent_id}/boundaries" \
-H "Authorization: Bearer $TOKEN"
Every row carries an enforced_at value telling you where the rule is actually checked:
runtime_policy— the agent's own policy graph inside the data-plane runtime (tool allowlists, PII redaction)gateway— the Action Gateway, before a real-world action executes (Authority Matrix rules, connector-scoped actions)control_plane— checked by the control plane before dispatch (cost caps, rate limits)advisory— recorded, not enforced (a guardrail field that's set but has nothing wired to block on it yet)
Rows that have fired recently link to the exact run step where they last did — click through from
Boundaries straight into a policy_decision or approval step on /runs/{id}.
Boundaries are set per agent via AgentGuardrails:
curl -X PUT "https://your-control-plane-host/api/v1/workforce/agents/{agent_id}/guardrails" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"tool_allowlist": ["search_knowledge_graph", "ask_company_brain"],
"approval_required_tools": ["request_gateway_action"],
"max_cost_per_run_usd": 0.50,
"max_runs_per_hour": 20,
"pii_redaction": true,
"output_schema": null
}'
tool_allowlist: null means no explicit narrowing (the agent gets everything its definition
grants); an explicit list is the only thing that can produce a real policy_decision: allow=false
— narrow it below what the agent actually tries to use and its next run will show a real denial,
not a simulated one.
What's next
Step 9 — Governance: The Rule Book: boundaries like the Authority Matrix rules you saw above don't have to be typed in by hand — they can come from an actual policy document you upload.