Lifecycle & policy reference
Every lifecycle endpoint with its role gate and exact error codes, the full policy YAML schema, the review cadences, and the advisory /policy/check contract.
Lifecycle endpoints
All under Authorization: Bearer (session JWT or org API key). Error bodies carry {"code", "detail"}; any transition on a decommissioned agent returns 409 terminal_state.
| Method / path | Min role | Rules | Errors |
|---|---|---|---|
POST /agents/:id/approve | security | draft → approved, or re-approval of a suspension over 30 days old. Body: {approved_by, note?}. | 403 segregation_of_duties (approver = creator), 409 illegal_transition (wrong state, or suspension still under 30 days — use reactivate) |
POST /agents/:id/activate | developer | approved → active. Body: {actor}. | 409 approval_required (from draft), 409 illegal_transition |
POST /agents/:id/suspend | security | active → suspended. Body: {actor, reason}; reason must be non-empty. Scope edits are refused until reactivation. | 400 reason_required, 409 illegal_transition; later scope edits: 409 scope_frozen on PATCH /agents/:id |
POST /agents/:id/reactivate | security | suspended → active, only while the suspension is under 30 days old. Body: {actor}. | 409 reapproval_required (over 30 days), 409 illegal_transition |
DELETE /agents/:id?actor= | security | Any state → decommissioned. Terminal: revokes all active tokens and audits a final snapshot of the agent row. | 409 terminal_state (already decommissioned) |
POST /agents/:id/review | security | Not a transition. Sets last_review = now, advances next_review by the risk cadence. Body: {actor}. | 409 terminal_state |
Initial status at registration: production → draft; dev and staging → active. Registration itself is a layer 1 concern.
Policy YAML schema
| Key | Type | Default | Meaning |
|---|---|---|---|
policies.defaults.deny_unlisted | bool | true | A tool in neither allow nor deny is denied. Switch off to allow unlisted tools (decisions then say allow_unlisted). |
policies.defaults.high_risk_requires_review | bool | true | Flags high-risk agents for mandatory review in governance reporting. |
policies.<agent> | map | — | One block per agent, keyed by agent id or name (id matched first). No block = the agent keeps its server-level decision only. |
policies.<agent>.<server> | map | — | Rules for one MCP server. Only allow, deny, and blast_radius are accepted — any other key fails validation at load. |
…<server>.allow | list[str] | [] | Tool names permitted on this server. |
…<server>.deny | list[str] | [] | Tool names denied on this server. Deny beats allow. |
…<server>.blast_radius | map[str, 0–1] | {} | Declared severity per tool. Logged with every call; drives layer 4's approval threshold. |
Where the YAML lives: your org's own document, edited on the Policy page or via GET/PUT/DELETE /policy/source (write access needs the security role; saves are strictly validated and live within ~5 seconds; delete reverts to the platform default). The platform default itself is files on disk: every *.yaml/*.yml in the policies directory, merged in filename order (later wins), hot-reloaded on mtime change, strictly validated at load. The optional top-level enforcement: section belongs to layer 4 and is documented with the gateway and enforcement — absent, everything observes.
Review cadence
| Risk level | Cadence | Sweep behavior |
|---|---|---|
high | 30 days | next_review initialised from last_review (or created_at) + 30d where missing |
medium | 90 days | Overdue agents get one review_overdue audit event per missed deadline |
low | 180 days | Agents past expires_at get one expired audit event per expiry; decommissioned agents are skipped |
POST /policy/check
Advisory evaluation of the exact logic the gateway runs — min role viewer, enforces nothing, and every check writes a policy_checked audit event. Omit tool for a server-level decision.
{
"agent_id": "AGENT_ID", // required — must belong to your org (else 404)
"mcp_server": "github-mcp", // required
"tool": "delete_repo" // optional — tool-level decision from the YAML store
}{
"permitted": false,
"reason": "tool delete_repo is denied on github-mcp",
"agent_id": "AGENT_ID",
"policy_rule": "github-mcp.deny" // the winning rule; null on status denials
}policy_rule is one of: denied_mcp, default_deny, allowed_mcp, <server>.deny, <server>.allow, deny_unlisted, allow_unlisted — or null when the denial came from the agent's lifecycle status.