Require approval
Let a run read, think and act on its own — but stop and ask you before it does anything it cannot take back.
An agentic run reads, decides and calls tools on its own. That is fine while it is reading. It stops being fine the moment it wants to send the email, post the message, or write the row.
Approvals is the switch for that boundary. Turn it on for a run and the run keeps going by itself — right up to the first action it cannot take back. That action is held before it happens: nothing has reached the outside world. It shows up in an operator's inbox with the tool name attached, and it waits for an operator to say yes or no.
Nothing is guessed about your intent. The run does not "try it and see". It stops.


What gets held, and what does not
Every tool the runtime knows about declares how it behaves if the same action were dispatched twice. The gate reads that declaration, and nothing else.
| The tool's declared class | What the gate does |
|---|---|
Staged — the action has no way of closing the loop on itself | Held. Waits for a decision. |
AtLeastOnce — no closing mechanism at all | Held. Waits for a decision. |
Token — the remote side accepts an idempotency key | Not held. |
Readback — the runtime can check whether it already happened | Not held. |
A read-only tool is normally declared Readback — for a read, the call is its own check — so it is not held, and a run under approvals still calls its read-only tools and reasons at full speed. The gate is keyed on the declared class, not on any separate notion of "read-only", so a tool that declares Staged is held whatever it actually does.
Tools reached through a connector are registered as Staged, because an outbound effect through someone else's API is treated as world-changing unless proven otherwise.
The four accepted values are Token, Readback, Staged and AtLeastOnce. A registration names its class, and Readback is not held. If your tool really does change something outside the run, say Staged.
One limit on the classes themselves
AtLeastOnce is more than a gate marker: the runtime refuses to dispatch an AtLeastOnce tool at all, because no run accepts at-least-once delivery today. Granting the approval is not on its own enough to make such a call fire.
A separate thing from tool grants
Whether a run is allowed to call a tool at all is a grant, resolved server-side before the run starts. Approvals sit downstream of that: the run already holds the grant, and the gate still stops the call. See Tools.
Turn it on
There are three places to switch the gate on, from narrowest to widest.
For an unattended trigger
This is the one that matters most. A trigger fires when nobody is watching — on a schedule. Gating the trigger means the unattended run still stops for a yes.
The run reads, reasons and prepares. Then it stops at the first held call and waits. A request carries no deadline of its own, but the run does not wait forever: if nobody decides within its wall-clock budget, the run fails.
For every run
The runtime also has a setting that requires approval for every new agentic chain, so the gate is on whether or not anybody remembered to ask for it.
When it is on that way, the per-run flag can only turn the gate further on. It is a boolean that defaults to off, and an off value falls back to the wider setting — so there is no way from the command line or the SDKs to opt one run out of a gate somebody else switched on. The resolved posture is fixed onto the run when it starts, so the rules cannot change under a run that is already going.
The inbox
Each held action waits in the approvals inbox with its tool and version attached, until an operator grants or denies it.
Held versus continuing — be precise
It helps to know exactly how much of the run is frozen.
- Everything before the call has already happened. The reasoning turns, the reads, the tool calls that were not held — all of that is done and recorded. A pending approval is not a rollback point.
- Only the one call is held. Nothing about it has reached the outside world.
- The hold belongs to that run. The wait is that run's, not the whole runtime's. There is no global lock, and other runs carry on.
- A grant releases the action once. A decision is final: you are not asked twice for the same request.
- A second grant on the same id does nothing. Deciding an already-resolved or unknown request changes nothing; the command line prints
not granted (unknown or already-resolved request). Same for deny. That makes retrying an operator action safe. - A deny ends that run. It stops and is set aside as failed. See Observability.
You are the only thing that moves it
A pending request has no auto-expiry of its own. If nobody grants or denies it, the run uses up its wall-clock budget and fails. For an unattended schedule, make checking the approvals inbox part of your routine.
Limits worth knowing
- The per-run gate applies to an app with one agentic step — the reason, act, observe loop where tool calls are proposed. An app with more than one agentic step is refused the gate outright, because a held call could not be traced to its step.
- There is no auto-approve rule engine, no per-tool allow list for approvals, and no approval delegation. It is one inbox and two verbs.