Approval policy and the sandbox answer different questions, and confusing them is the root of many mistakes. The sandbox answers "can the command technically do this inside the current boundary". The approval policy answers "when must Codex stop before an action and ask". The first is about physical possibility, the second about the moment of control. One does not replace the other: a strict sandbox without a sensible approval policy leaves the agent without stops where a human is needed, and vice versa.
The approval policy is set explicitly, and it has several modes with different behavior. untrusted automatically runs a limited trusted set of commands, while others require confirmation. on-request works inside the sandbox and asks for confirmation when leaving the boundary. never shows no prompts at all: an action either passes by policy or fails. The choice of mode is a choice of how often and on what exactly the agent stops to ask.
It helps to see the base setup side by side once. Below is approval_policy with the value on-request and approvals_reviewer, setting who reviews the request: user or auto_review. You return to this form when tuning when exactly the work should stop for a human decision. The default value is worth choosing for the context: interactive local work and non-interactive CI require a different frequency of stops, and one policy rarely suits both equally.
Auto-review is a separate review mechanism, and it matters to know its boundaries precisely. It does not expand the sandbox: the reviewer considers only those actions that already require confirmation rather than opening new capabilities. Meanwhile a parse failure and a policy failure close safely - toward denial, not permission. The price of auto_review is extra model calls and spend: review costs tokens, so it is enabled where it really pays off in control.
It helps to see the granular form of the policy too once. Below is the approval_policy object with separate switches: confirming leaving the sandbox, rules, MCP elicitations, a permissions request and a skill call. Granularity lets you allow or automatically reject specific classes of actions separately rather than with one coarse mode. You return to this form when you need to finely separate what passes on its own, what asks and what is rejected without a question.
A granular policy is useful exactly in that it makes the boundary explicit and explainable. Instead of "the agent seems to ask about the dangerous" you get a concrete list: this requires confirmation, and this is rejected automatically. Such a policy is easier to review and harder to weaken by accident, because each class of actions is named separately. But the price of clarity is that each switch must be understood: allowing a class of actions, you are responsible for what it opens.
| Policy | Behavior |
|---|---|
| untrusted | Auto-runs a limited trusted set; the rest - a prompt |
| on-request | Works inside the sandbox, asks for leaving the boundary |
| never | No prompts; an action passes by policy or fails |
| Granular | Separate allow/auto-reject per class of actions |
The bundle of sandbox and approvals works only together, and this is worth keeping in mind. The sandbox sets the physical ceiling of the possible, approvals the moments where a human intervenes inside that ceiling. Approving an action does not expand the sandbox, and a refusal to confirm does not make a command technically impossible - these are different planes. Security is born of their composition: a narrow technical boundary plus meaningful stop points, not one of the two mechanisms cranked to the max.
The typical failures around approvals are predictable. Confusing the sandbox and the approval policy and expecting from one what the other gives. Setting never in interactive work and losing the stops where a human is needed. Deciding that auto_review expands the sandbox or replaces verification - though it only reviews what already requires approval. And enabling the reviewer without accounting for its spend. Separate the technical boundary and the stop moment, choose the mode for the context and remember: approval does not expand the sandbox.
approval_policy = "on-request"
approvals_reviewer = "user"
# An alternative for eligible approvals (does not expand the sandbox):
# approvals_reviewer = "auto_review" # adds model calls and usage
# Granular form:
approval_policy = { granular = {
sandbox_approval = true, rules = true, mcp_elicitations = true,
request_permissions = false, skill_approval = false
} }