Approvals
Some calls should not happen without a person saying yes: merging a pull request, sending money, deleting data. List them in the tool's requires_approval, and every call to them waits for a decision.
tools:
- id: github
mcp: ["python", "-m", "github_tools.mcp"]
requires_approval: [github.merge]
The tool's plugin decides this, so it holds for every agent that uses the tool, from any plugin.
What happens
- The agent calls
github.merge. Before the call runs, the job asks for a decision and waits. It shows up on the web UI's Approvals page, on the job's page, inorchestrator-zero approval list, and in the job's live stream. - An admin approves or denies it, with a comment if they like.
- Approved: the call runs and the agent goes on. Denied: the call is not made, and the agent is told who said no and why, for example
The call to github.merge was not made: user:kevin denied it: not before the release., so it can explain or try something else. - No answer within 24 hours counts as a no:
nobody answered.
The wait is part of the job's durable state in Temporal. It costs nothing while it lasts, and it survives restarts of servers and nodes: the job goes on wherever its agent can run once someone decides.
Decide from the web UI
Approvals in the sidebar lists everything that waits, with a count, oldest first: what waits, the agent, the job, how long it has waited and when it is denied unless someone answers. Click one to open it in a drawer with the call's arguments. The job's own page shows its pending approvals above its tabs. Write a comment if you want the agent to know why, then Approve or Deny.
Operators, admins and owners of the tenant decide; readers see what waits but cannot decide, and see the call's arguments only with the permission to see the tenant's content.
Decide from the CLI
orchestrator-zero approval list
orchestrator-zero approval approve job-bb270cf1-... a-3d3774fd06c3 --comment "fine by me"
orchestrator-zero approval deny job-bb270cf1-... a-3d3774fd06c3 --comment "not before the release"
orchestrator-zero run and job watch show a waiting call as it happens, with the command that approves it:
helper → team.hello {"name":"e2e"}
helper ⏸ team.hello waits for approval a-3d3774fd06c3: {"name": "e2e"}
orchestrator-zero approval approve job-0879b9bc-... a-3d3774fd06c3 (or deny)
helper ▶ team.hello approved by operator:admin: fine by me
helper ← team.hello Hello, e2e!
From your own app
The runtime API has ListApprovals and DecideApproval. The edge records who decided from the caller's credentials; a request cannot name someone else.
approval:, hooks that answer escalate, and the tools of a harness session listed in the agent's harness.requires_approval, which wait as claude-agent-sdk.Bash and the like. A timeout other than 24 hours, and approvals limited to a role per tenant, come later.