Skip to content
Agentic Control Plane

Policies

A policy decides what an agent’s tool calls may do: allow, flag, ask a person first, or deny. It can also rate-limit or redact, and it says whether the workspace enforces those decisions or only watches. These endpoints read and write that policy from code. They write the same documents the console does.

https://api.agenticcontrolplane.com/<workspace>/admin

Policy endpoints carry your workspace slug in the path, and it must match the slug in your key. Reads work with any key that carries the scope. Writes are human-only: a signed-in owner or admin. An API key is an agent’s credential, so it is rejected on every policy write. That way an agent can never change the rules it runs under. It can propose a rule for a person to confirm.

The four layers

Policy is stored in four layers, and the strictest applicable layer wins. For any tool and tier, the most restrictive permission set by any layer is the one that holds. A layer can tighten what a broader layer allows. It cannot loosen what a broader layer denies. (The console shows the same layers as Tool, Agent and User policies.)

Layer Endpoint Scope
Workspace /admin/workspacePolicy The baseline for everything in the workspace.
Role /admin/rolePolicies/{role} Per-role overrides for owner, admin, member.
Agent type /admin/agentTypePolicies/{key} Per-agent-identity overrides, keyed client::tier::name.
User /admin/userPolicies/{uid} Per-user overrides, including stricter rules a user sets on themselves.

On each call, ACP merges the layers that apply to the caller and the agent into one policy, takes the strictest permission per tool and tier, and checks the call against it. Within a layer, a more specific tool entry beats a broader one.

Above every layer sits the safety floor. Catastrophic commands and policy tamper are always blocked. Force-push, destructive SQL and curl | sh are held for approval unless an owner or admin turned that tier off.

This page documents the workspace layer in full. The other three take the same body shape at their own paths.

The policy object

{
  "mode": "enforce",
  "defaults": {
    "interactive": { "permission": "allow", "rateLimit": 100, "transform": "log" },
    "subagent":    { "permission": "allow", "rateLimit": 60,  "transform": "redact" },
    "background":  { "permission": "deny" },
    "api":         { "permission": "allow", "rateLimit": 30,  "transform": "redact" }
  },
  "tools": {
    "github.create_issue": {
      "interactive": { "permission": "allow", "rateLimit": 10, "transform": "log" }
    }
  }
}
Field Type Description
mode "enforce" | "audit" enforce applies decisions. audit records what your rules would have done without enforcing them. The safety floor still applies in audit mode. Start in audit, watch the logs, then flip to enforce. The console’s shadow replay gives the same would-have-happened view by running a proposed policy against your recorded calls before you apply it.
defaults object Per-tier rules applied to every tool unless a tools entry overrides them. Tiers: interactive, subagent, background, api.
tools object Per-tool overrides, each keyed by tool name, then by tier.

Each rule is:

Field Type Description
permission "allow" | "flag" | "ask" | "deny" Whether the call may proceed. ask holds the call for a person to approve, and is stored and returned as "step_up".
approvalTtlSeconds number Optional. How long an ask waits before it counts as denied. 30 to 604800 (7 days).
rateLimit number Optional. Max calls per window before the call is throttled.
transform "log" | "redact" log records the call as-is; redact strips detected secrets/PII from what’s logged and passed on.

PUT does not check rule bodies. A misspelled permission is stored as written, so check the GET response after you write, or use the console.

The tiers describe how the agent is running, not who wrote it: interactive (a person is watching), subagent (a delegated sub-task), background (autonomous or scheduled), and api (started by an API trigger). Tiers let you allow a tool interactively but deny it to a background run. On the api tier only deny is enforced, because nobody is there to answer an ask.

Read the workspace policy

GET /<workspace>/admin/workspacePolicy

Readable by any member.

curl -s "$ACP/acme/admin/workspacePolicy" \
  -H "Authorization: Bearer $ACP_KEY"

Returns the policy object, or {} if none is set (defaults apply).

Write the workspace policy

PUT /<workspace>/admin/workspacePolicy

Send the request with a signed-in owner or admin’s session token. A gsk_ key is rejected here, so the $ACP_KEY in the write examples below stands in for that credential.

A merge update. Send only what you want to change, and unset fields stay as they were. Owner/admin only.

curl -sX PUT "$ACP/acme/admin/workspacePolicy" \
  -H "Authorization: Bearer $ACP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "enforce",
    "defaults": {
      "background": { "permission": "deny" },
      "interactive": { "permission": "allow", "transform": "log" }
    }
  }'
{ "ok": true }

A common rollout: set "mode": "audit", run your agents, read the audit log to see which tools they actually touch, codify rules for those, then PUT { "mode": "enforce" }.

Clear the workspace policy

DELETE /<workspace>/admin/workspacePolicy

Removes the workspace layer entirely; the built-in defaults apply at the next call. Useful for resetting a test workspace. Owner/admin only.

curl -sX DELETE "$ACP/acme/admin/workspacePolicy" \
  -H "Authorization: Bearer $ACP_KEY"

The other three layers

rolePolicies, agentTypePolicies, and userPolicies take the same body shape at their own paths, with the layer’s key as the last path segment:

# Per-role: tighten what members can do
curl -sX PUT "$ACP/acme/admin/rolePolicies/member" \
  -H "Authorization: Bearer $ACP_KEY" -H "Content-Type: application/json" \
  -d '{ "defaults": { "interactive": { "permission": "allow", "rateLimit": 50 } } }'

# Per-agent-type: rein in one agent identity (client::tier::name)
curl -sX PUT "$ACP/acme/admin/agentTypePolicies/Claude%20Code::background::" \
  -H "Authorization: Bearer $ACP_KEY" -H "Content-Type: application/json" \
  -d '{ "tools": { "shell.exec": { "background": { "permission": "deny" } } } }'

# Per-user: a user can set stricter rules on themselves
curl -sX PUT "$ACP/acme/admin/userPolicies/uid_123" \
  -H "Authorization: Bearer $ACP_KEY" -H "Content-Type: application/json" \
  -d '{ "mode": "enforce", "defaults": { "api": { "permission": "deny" } } }'

Each layer supports GET, PUT, and DELETE the same way the workspace layer does. userPolicies allows self-edits (a member tightening their own rules) in addition to admin edits.

The conceptual model

This page is the reference. For why the layers merge this way, the tier model, and the detection behind redact, read Policies & scopes and how a call is decided.

Next

See what policy did → The policy model →

Running coding agents? ACP records each tool call its hook sees and makes the dangerous shell commands (a force-push, destructive SQL, curl | sh) wait for you. One command, free up to 5 agents:

macOS · Linux · WSL
curl -fsSL https://agenticcontrolplane.com/install.sh | bash
Windows · PowerShell
irm https://agenticcontrolplane.com/install.ps1 | iex

What the installer writes  ·  Getting started