Autonomy Levels
Autonomy levels are how Opbox controls what an AI Agent or external MCP client is allowed to do. Every API key carries an autonomyLevel (0-3) set at creation. The MCP router enforces it before dispatching any tool call.
The Four Levels
| Level | Meaning | Examples |
|---|---|---|
| L0 | Read-only | search, list_*, get_*, agent_queue_list, get_notifications, execute_sql (read-only), search_system_docs |
| L1 | Read + internal writes | create_invite, mark_notifications_read, agent_queue_claim, doc-gen patcher reads, add_subtask, add_comment |
| L2 | Read + mutations with external side effects | call_api_endpoint (GET only), run_saved_query, billable workflow runs, sending notifications |
| L3 | Full | All registered tools, subject to per-tool gate flags (some tools are L3-only by their declaration) |
Default for new keys: L0. Admins must explicitly raise the level when minting the key.
Enforcement
Tool registration declares its required level. The MCP router checks before dispatch:
if (tool.minAutonomyLevel > ctx.autonomyLevel) {
return { error: 'AUTONOMY_LEVEL_REQUIRED', ... }
}
The same autonomy enforcer is used by both the in-process Agent Worker and the MCP HTTP router, so the rule is identical regardless of execution path.
Denied calls are audit-logged. The AUTONOMY_LEVEL_REQUIRED security event records the tool name, the calling key, and the level required vs supplied.
Why Tiers Are Coarse
The four levels are deliberately coarse. We considered per-tool grants but rejected them:
- Operator cognitive load - admins can reason about "read-only" or "full" much faster than ticking 405 boxes.
- Forward compat - new tools land regularly. A coarse tier auto-classifies them via
minAutonomyLevel. A fine-grained ACL would require a manual edit on every release. - Audit clarity - "this key is L1" is a stronger story than "this key has these 47 tools."
Per-tool gating still happens, but inside each level. For example, execute_sql is L0 (it's read-only) but also requires the workspace ADMIN role at the API boundary - so even an L3 key from a non-admin user can't run it.
Choosing the Right Level
| Use case | Level | Rationale |
|---|---|---|
| Developer using Claude Code on their laptop for read-only investigation | L0 | Can't accidentally write. Safe default. |
| Personal automation - drafting comments, taking notes | L1 | Internal writes only - no external side effects. |
| Agent Bridge running production doc-gen | L1 or L2 | Doc-gen needs agent_queue_claim (L1). Cloud fire-and-forget hits webhooks (L2). |
| CSP firm overseer running cross-workspace audits | L0 | Read-only across the whole client base. Lots of leverage, no risk. |
| Full automation - end-to-end matter completion | L3 | Trusted internal tooling only. |
OpenClaw Bidirectional Auth
When the OpenClaw provider is configured, the same bearer that opbox uses to call OpenClaw also authenticates inbound MCP calls (OpenClaw → opbox). The autonomy level for those inbound calls is set separately on the org config:
| Setting | Default | Effect |
|---|---|---|
Inbound autonomy level (openclawInboundAutonomyLevel) | L3 (full) | What tools OpenClaw can invoke when calling /api/mcp/* with the bearer. L0-L3 same matrix as above. |
The OpenClaw bearer authenticates as the OpenClaw principal user (auto-seated as OPBOX_AGENT role on the org when OpenClaw is enabled). All the same role / autonomy / cell-locking gates apply - the bearer just supplies an alternative credential, not new privileges. Audit log entries carry apiKeyId = openclaw:<orgId> to distinguish bidirectional activity from cp_live_* MCP keys.
Crossing Tenant Boundaries
The _targetWorkspaceId cross-workspace swap does not change the autonomy level. An L0 overseer key remains read-only against subordinate workspaces. The autonomy level is a property of the key, not of the target.
This means an overseer can run "look at my client base read-only" without having to mint a separate per-client key per workspace.
L3 Per-Tool Gates
Some tools are L3-only by declaration regardless of caller. Examples:
bulk_update_dashboard- bulk widget rewrite.set_dashboard_config- replace entire config.delete_table/delete_column- destructive schema changes.update_document(full structured-JSON replace) - destructive content overwrite.
These are L3-only because their blast radius is large enough that a "full automation" justification is the right minimum bar.
Rotating Levels
You can change the autonomy level on an existing key without rotating the secret:
PATCH /api/agent/api-keys/[id]
{ "autonomyLevel": 2 }
Requires ADMIN/OWNER role + CSRF. The change is audited. New tool calls immediately respect the new level - in-flight calls finish on the old level.
API Key Creation
| Endpoint | Method | Purpose |
|---|---|---|
POST /api/agent/api-keys | Mint a new key | ADMIN/OWNER only. JSON body with name and autonomyLevel. Returns the cleartext key once - it's not retrievable again. |
GET /api/agent/api-keys | List existing keys (masked) | ADMIN/OWNER. |
PATCH /api/agent/api-keys/[id] | Update name / autonomy / disabled state | ADMIN/OWNER + CSRF. |
DELETE /api/agent/api-keys/[id] | Revoke | ADMIN/OWNER + CSRF. |
The cleartext key is shown once at creation. Copy it to your env / MCP config immediately. It's stored hashed in the DB; the cleartext is unrecoverable.
What L0 Can Still Do
Even L0 (the most restrictive level) is meaningfully powerful in Opbox:
- Full search across CRM, matters, documents, KB.
- Read-only SQL against the workspace database.
- Page through audit logs (admin-gated separately).
- Run RAG queries against extracted content.
- Read the full doc-gen briefing for any matter step.
- Cross-tenant reads if the home workspace has oversight.
A common production pattern: one L0 overseer key per analyst, doing the entire reading job; a small L3 service account for the unattended automations.
See Also
- Agent Worker - default L0 on creation.
- Security & Limits - the broader safety story.
- MCP Setup - how to mint and configure keys.
- BYOK Credentials - the orthogonal credential dimension.