# Approvals — ACP

What a held tool call looks like in the terminal and in the console, ask vs deny, the grant window and single retry, expiry, the email, and how an answer becomes a standing rule.

# 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 &mdash; 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 &amp; scopes](/docs/policies).

## 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](/docs/policies#audit-mode-vs-enforce-mode).

## Related

- [Policies &amp; scopes](/docs/policies) &mdash; the four outcomes, tiers, and setting a rule from a row.
- [How a call is decided](/docs/governance-model) &mdash; the per-call pipeline.
- [Activity](https://cloud.agenticcontrolplane.com/activity) &mdash; every held, approved, expired, and denied call, with its reason.
