require_approval
on a tool and every call to it pauses the run, shows the user what the agent
is about to do, and waits for a decision. Approve, and the call runs exactly
as proposed. Decline, and the agent is told so and carries on.
When to require approval
Anything that writes, charges, sends, or is hard to undo: creating a booking, issuing a refund, sending an email, deleting a record. Not reads — an approval prompt on every lookup trains users to click through without looking, which defeats the one on the refund.Turning it on
require_approval is a flag on the tool’s entry in an agent’s tool list,
not on the tool itself. The same tool can require approval in one agent and
not in another.
Approvals are honoured for API tools, MCP tools and connected-app actions
held by the agent. Tools that arrive through a per-user MCP connection built
at request time are not yet gated.
What the user sees
When the agent decides to call a gated tool, the platform pauses it before the call runs and asks the model for one thing first: a short sentence, in the user’s language, saying what the call will do with its decisive details — the recipient and subject of an email, what a deletion removes. That sentence is the headline of the approval card, next to Approve and Decline. The proposed arguments are shown beneath it, so the user is deciding on the real call, not a paraphrase. The agent is told to treat the pause as the consent step. It does not ask permission in chat first and then call the tool: it calls the tool, and the card collects the decision. You do not need to write any of this into your own prompt — the platform adds the guidance whenever an agent holds a gated tool.Deciding
Approve resumes the suspended run against the exact agent version that paused, and the tool runs with the arguments the user saw. Decline skips the call; the model receives “Tool call was not approved by the user”, and is instructed to treat that as a decision rather than an error — acknowledge it and continue without repeating the call unless asked. In the conversation history a declined call is recorded as denied, with the reason the model gave, so a replay of the thread shows what was proposed and that it did not happen.Durability
The paused run is persisted as a snapshot keyed by its run id, so it outlives the HTTP request that started it. A client that drops mid-pause can reconnect to the stream withGET /v1/chat/stream inside the disconnect grace window
and replay the approval chunk. Beyond that window an unanswered run is
abandoned; the thread is intact and the user can simply ask again.
From the API
A gated call arrives on the chat stream as adata-tool-call-approval chunk
instead of a tool result. It carries everything needed to answer it:
POST /v1/chat again with the
same threadId, the chunk’s agentVersionId (which skips routing so the
continuation reaches the agent that paused), and an approval object. On a
continuation messages may be empty:
The same
approval field works on POST /v1/agents/{id}/run and
POST /v1/copilots/{id}/run. Those request/response endpoints report the
pause in their collected output — the approval chunk is present and there is
no finishReason — and are continued the same way. The SDK handles all of
this for you; the shapes above matter when you drive the API yourself.
Design guidance
- Argument names are user-facing. They appear on the card, so
guest_emailreads better thanp_email_v2. If a parameter is an internal id the user cannot judge, make sure the reason sentence names the thing it refers to. - Gate the write, not the read that precedes it. Let the agent look up the order freely and pause only on the refund.
- Don’t double-ask. If your prompt tells the agent to “always confirm before sending”, it will confirm in chat and then pause again on the card. The card is the confirmation.
- Pinned configuration still applies. Values fixed on the agent’s copy of the tool are merged in when the approved call runs, so the user approves the arguments the model chose and your pins fill in the rest.