Skip to main content
The Governance API is the human-facing surface for Clawboo’s spend guardrails. You use it to set USD budget caps, read the forensic audit log, resume a paused run, and route delegation approvals. The budget kill-switch and audit are always on; by default all scopes are uncapped — a budget row only exists when you create one.
The atomic spend increment that actually pauses a run happens inside the executor loop (recordSpend under BEGIN IMMEDIATE), not over REST. These routes are the operator surface: set a cap, read the ledger, resume a paused scope, and view the audit trail.

Routes

MethodPathSummary
GET/api/governance/budgetsList every budget row
POST/api/governance/budgetsSet or raise a budget cap
POST/api/governance/budgets/:scope/:scopeId/resumeResume a paused scope
GET/api/governance/auditRead the forensic audit log
POST/api/governance/delegation-approvalRoute a risky action to the leader’s approval queue
GET/api/approvalsList persisted approval decisions
POST/api/approvalsPersist an approval decision

GET /api/governance/budgets

Lists every budget row, newest-updated first.

Response 200 OK

budgets
array
All budget rows.
A cap-mode budget auto-pauses the run at 100% of limitUsdCents (the kill-switch). A warn-mode budget records spend and emits warning events at the 80% and 100% crossings but never reaches paused.
curl http://localhost:18790/api/governance/budgets

POST /api/governance/budgets

Sets a budget cap for a scope, or raises an existing one. A new scope starts at spent 0 / active. Raising the cap above current spend un-pauses the scope.

Request body

scope
string
required
Budget scope. One of 'agent', 'mission', 'team', 'tenant'.
scopeId
string
required
The id of the scoped entity.
limitUsdCents
number
required
Spending cap in US cents. Must be a positive integer. A cap of 0 is rejected.
mode
string
'warn' (default) — track spend and emit warnings. 'cap' — auto-pause at the limit.
A cap of 0 is rejected. “Uncapped” is the absence of a budget row, not a zero limit. To make a scope uncapped, delete its budget row or leave it without one.

Response 200 OK

{ "budget": { "id": "...", "scope": "team", "scopeId": "...", "limitUsdCents": 500, "status": "active", "mode": "cap" } }
# $5.00 hard cap on a team (500 cents, auto-pause on breach)
curl -X POST http://localhost:18790/api/governance/budgets \
  -H 'Content-Type: application/json' \
  -d '{"scope":"team","scopeId":"<team-id>","limitUsdCents":500,"mode":"cap"}'

POST /api/governance/budgets/:scope/:scopeId/resume

Human override: forces a paused scope back to active. The kill-switch re-arms on the next spend crossing. Pass graceUsdCents to raise the cap above current spend so the run can make forward progress. The response includes willRepause: true when you resume an at/over-limit scope without enough grace.
scope
string
required
One of 'agent', 'mission', 'team', 'tenant'. An unknown value returns 400.
scopeId
string
required
The scoped entity’s id.

Request body (optional)

graceUsdCents
number
Additional headroom in cents. Use this to raise the limit above current spend so the next run isn’t immediately re-paused.

Response 200 OK

budget
object
The resumed budget row (same shape as the list).
willRepause
boolean
true when spentUsdCents >= limitUsdCents — the next cost event will re-pause this scope.
# Resume a paused team budget with $1.00 (100 cents) of extra headroom
curl -X POST http://localhost:18790/api/governance/budgets/team/<team-id>/resume \
  -H 'Content-Type: application/json' \
  -d '{"graceUsdCents":100}'

GET /api/governance/audit

Reads the append-only forensic audit log — installs, approvals, tool calls, budget events, cap hits, verifications, and circuit breaks — newest first. The audit is written in-process by the subsystems that emit events; there is no write endpoint.
agentId
string
Filter to one agent’s audit rows.
eventType
string
Filter by event type. Valid values: install, approval, tool_call, budget, cap_hit, verification, circuit_break. Ignored if the value is not one of these literals.
since
number
Lower bound on createdAt (epoch ms).
limit
number
Max rows returned. Clamped to 1,000. Default 200.

Response 200 OK

audit
array
Matching audit rows.
# Last 50 budget events for one agent
curl "http://localhost:18790/api/governance/audit?agentId=<agent-id>&eventType=budget&limit=50"

POST /api/governance/delegation-approval

Routes a delegated child’s risky action to the leader’s approval queue. If a prior sticky allow_always for the (leader, scope) pair exists, the call returns immediately. Otherwise it opens a pending row in the tool_call_approvals table and blocks until the leader resolves it or the TTL/poll-deadline expires.
This route awaits a human decision and can hang for the duration of the approval TTL. The TTL is configurable via CLAWBOO_APPROVAL_TTL_MS. An expired or timeout resolution is a denial — treat it as such.

Request body

leaderAgentId
string
required
The leader agent who must resolve this approval.
kind
string
Delegation kind, mapped to the scope key 'delegate:<kind>'. Defaults to 'code'.
targetAgentName
string
The name of the agent the work is delegated to (used in the approval prompt text).
task
string
The delegated task description (used in the approval prompt text).
taskId
string
The board task this delegation gates. Lets the TTL reaper unblock the task on expiry.

Responses

200 OK — a prior sticky allow_always short-circuits the prompt:
{ "resolution": "allow_always" }
200 OK — approval was awaited; the resolution is whatever the leader chose or a terminal expiry:
{ "resolution": "allow_once" | "allow_always" | "deny" | "expired" | "timeout" }
curl -X POST http://localhost:18790/api/governance/delegation-approval \
  -H 'Content-Type: application/json' \
  -d '{"leaderAgentId":"<leader-id>","kind":"code","task":"run the destructive migration"}'

GET /api/approvals

Lists persisted approval decisions from the approval_history table, newest first. This is a decision-history log distinct from the live delegation handshake above.
agentId
string
Filter to one agent. Omit for all agents.
limit
number
Max rows. Clamped to 1–200. Default 50.

Response 200 OK

{
  ok: true
  records: Array<{
    id: number
    agentId: string
    action: 'allow-once' | 'allow-always' | 'deny'
    toolName: string
    details: string | null   // JSON text
    createdAt: number
  }>
}
curl "http://localhost:18790/api/approvals?agentId=<agent-id>&limit=50"

POST /api/approvals

Persists a single approval decision into the approval_history table.

Request body

agentId
string
required
The agent this decision applies to.
action
string
required
The decision. One of 'allow-once', 'allow-always', 'deny'.
toolName
string
required
The tool name this decision covers.
details
object
Optional JSON object with additional context. Stored as a JSON string.

Response 200 OK

{ "ok": true, "record": { "id": 42, "agentId": "...", "action": "allow-always", "toolName": "web_search" } }
curl -X POST http://localhost:18790/api/approvals \
  -H 'Content-Type: application/json' \
  -d '{"agentId":"<agent-id>","action":"allow-always","toolName":"web_search"}'

See also