Permissions and approvals
Every tool call an agent makes carries one of three levels. The level decides whether the call runs on the spot or stops and waits for a person. You set it once, per tool, and the same answer governs the agent in production and the Agnt Wizard while you are building.
act, ask, always_ask
| Level | Behaviour | Reach for it when |
|---|---|---|
act | Runs automatically without asking you. | Reads, and writes you have decided you never want to see. |
ask | Pauses for your approval before it runs. | The default for writes. Reversible, but you want to know. |
always_ask | Always pauses, even in autopilot. Use for sensitive actions. | Deletes, sends, payments — anything you cannot take back. |
The difference between ask and always_ask only shows up where there is an autopilot mode. A deployed agent gates both. The Agnt Wizard has a per-workspace switch between asking and acting; in acting mode it still stops for always_ask, which is the whole point of the third level.
First hit wins, in this order
- 01
Per-agent override
A level pinned on this one agent for this one tool. Beats everything else. - 02
Workspace override
A level set for the tool across the whole workspace. One override governs both the deployed-agent gate and the builder's live calls, because they share one tool-name space. - 03
The agent's default level
A fallback carried on the agent itself, used before any spec default. Agents built before the permission system shipped carry act here, so their behaviour did not change under them. - 04
The spec default
The authored level for the operation: x-agnt-permission on the vendor spec, the reviewed permission-overrides file for curated specs, or the connector registry entry. - 05
The heuristic
Last resort. A write HTTP method becomes ask; a read verb in the tool name (get, list, search, read, status and friends) becomes act.
clearing an override
The calls that always run inline
Curated data-source tools and the first-party utility tools bypass the gate entirely. They show in the permissions card as fixed automatic rows with no selector, and an override written against them is ignored.
- Curated data sources are read-only lookups we pay the upstream for. Many of them use POST for a read, so method is not a safety signal here, and none of them change anything in your accounts.
- First-party utilities — the workspace database, workspace files, observability, agent invocation, the tool-discovery controls — carry their own policy layers (read-only mode, table allowlists) that an approval prompt would not improve. A database you connected yourself is exempt on the same grounds — what it may do is decided by the credential you connected it with: the grants on that Postgres role, or the project allowlist on a Supabase connection.
- Every vendor API call — connections, connectors, remote MCP servers, inbox and messaging — is gated.
A remote server annotates its own tools
A remote MCP server has no curated spec, so its own MCP tool annotations play that role. A tool the server marks read-only inherits act; a tool it marks destructive inherits always_ask. Declaring a tool destructive is optional and costs the server nothing to omit, so we honour it when it is there.
Unannotated tools fall back by source. On a server you connected yourself the fallback is act, because connecting a server and handing it a credential was already the trust decision. On a built-in server the fallback is the name heuristic. Either way, per-tool and per-agent overrides sit above it.
The call is frozen, not abandoned
When the gate stops a call, the exact arguments are frozen and the request lands in that agent's Approvals tab with a one-line summary of what it wants to do. The agent ends its turn rather than blocking. Approving re-dispatches the frozen call server-side and resumes the session with the real result; rejecting resumes it with the decision, so the agent can say so and move on.
| Status | Means |
|---|---|
pending | Waiting on a person. A single reminder goes out at the halfway mark. |
approved | A person said yes; the frozen call is on its way. |
executed | The call ran and the session resumed with its result. |
failed | Approved, but the call itself errored. The agent sees the error. |
rejected | A person said no. Nothing ran. |
expired | Nobody answered in time. The request is visibly retracted rather than left as a dead button. |
late approvals
Sensitive operations get a shorter fuse than routine ones: the expiry is set per danger level when the request is staged.
Asking a question, and asking permission
escalate_to_human asks a blocking question. The agent ends its turn, the question is delivered to the conversation it is running in and shows on its dashboard page, and the human's answer is what resumes the session. Nothing executes on resolution — the answer is the result.
On Slack, slack_request_approval posts a message with Approve and Decline buttons. The click resumes the same conversation with the decision, and the agent then performs the action itself or stands down. Use it for sign-off before an irreversible step; use escalate_to_human when what you need is an answer.
When the session has no chat binding of its own, delivery falls to the agent's default channel. Left on automatic, that resolves to a confirmed iMessage binding first and the Slack conversation second; pinning the agent to Slack reverses the order. A request is delivered to exactly one place, never broadcast.
Related: tools and tool families for what an agent can call in the first place.