Skip to content
Agentic Control Plane

Approvals

The hook sends each tool call to the gateway before it runs. When the rule for that tool and tier says ask, the call is held until a human answers. This page is what that looks like from both ends.

In the terminal

The agent does not get the tool’s output. It gets one line:

[ACP] Approval required: <reason>

The reason is the rule that fired. The agent can carry on with other work, wait, or stop — that is up to the agent. Nothing about the held call runs until someone answers.

Compare deny, which is final:

[ACP] Denied by policy: <reason>

ask is for calls you want to see before they run. deny is for calls that should not run at all. Both are set per tool (Bash.rm, Bash.git-push, WebFetch.<host>) and per agent tier (interactive, subagent, background, api). See Policies & scopes.

In the console

The request appears on the Approvals page of the console, and the workspace owner gets an email. The request shows the tool, the input, the agent, the tier, and the reason it was held.

You can approve or deny it.

  • Approve grants a single retry of the exact action within the grant window. The agent retries the call; if the tool, input, and agent match, the call runs once. A different command, or the same one after the window closes, is held again.
  • Deny blocks it. The agent sees [ACP] Denied by policy: <reason> on its retry.

When nobody answers

Unanswered requests expire at the end of the hold window and count as denied. The hold is 24 hours by default (David-set, 2026-09-02) so an unattended agent’s ask can wait for whenever you get to the email or the console; set it per agent type or per tool with approvalTtlSeconds on the policy entry (30 seconds to 7 days). The row stays on Activity with that outcome, so a call that was never answered is visible as exactly that, not as a silent gap.

Unattended agents can wait for you. In claude -p, point --permission-prompt-tool at mcp__acp__acp_permission_prompt (the ACP MCP server the installer registers). Each prompt is evaluated from policy at the background tier; an ask creates the approval, notifies you, and holds the call by long-polling GET /aarm/approvals/:id/wait until you decide or the hold ends. Approved runs the call, everything else is a deny with a reason the model sees.

If you run agents unattended, this is the posture to plan around: a background agent that hits an ask rule waits for a human who may not be there. Either answer promptly, or set that tool to allow or deny for the unattended tier so there is nothing to wait on.

Turning an answer into a rule

Answering one request does not change the rule. If you find yourself approving the same call repeatedly, set a standing rule instead:

  • From any row on Activity or Sessions, choose Ask next time, Deny next time, or Allow always. That writes a rule for that tool and tier.
  • Agents can propose a rule from the denial they hit (acp_propose_rule). Proposals wait on the Policies page until you confirm them. Nothing an agent proposes is enforced until a human clicks.

Audit mode

New workspaces start in audit mode: everything is recorded and nothing is held or blocked. In that mode the agent sees [ACP shadow] That was <tool> &mdash; <why> for calls the starter policy would have paused. Switch to enforce on the Policies page when you are ready for ask and deny to take effect. Details in Audit mode vs. enforce mode.

Run agents? ACP lets you see, control, and price every tool call they make — allow/ask/deny policy, per-session cost, and a full audit log for Claude Code, Cursor, Codex, and OpenClaw, in one command:

curl -sf https://agenticcontrolplane.com/install.sh | bash

Getting started →  ·  see your first governed call →  ·  free up to 5 agents  ·  prefer fully on-device? add --local