> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.rightbrain.ai/v-1/docs/production/approvals/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.rightbrain.ai/_mcp/server. # Approvals (human-in-the-loop) > Gate agent tool calls with human-in-the-loop approvals. Configure per-tool action controls, pause runs for review, and approve, reject, or resume over the API. Some tool calls are too consequential to run unsupervised: sending an email, writing to a CRM, moving money. Rightbrain lets you put a human in the loop on a per-tool basis. When a gated tool is called, the run pauses and raises an approval request. Approval records the decision; the API client then resumes the run to execute the approved tool. Approvals are configured on the **agent revision**, so they are versioned alongside the rest of the agent's wiring. Changing an approval policy creates a new revision, and rolling back restores the previous policy. ## Per-tool action controls Every tool attached to an agent revision — a Task, an integration tool, an MCP tool, or a registered tool — carries an **action control**: **`action_mode`** `'auto_run' | 'require_approval'` — default: auto\_run `auto_run` lets the agent call the tool without interruption. `require_approval` pauses the run and raises an approval request before the tool executes. --- **`rejection_behavior`** `'end_run' | 'return_rejection_to_model'` — default: end\_run What happens when a request is rejected. `end_run` stops the run and marks it failed with `termination_reason: "approval_rejected"`. `return_rejection_to_model` keeps the run paused until your client calls `/resume`; the agent then receives the rejection and can reconsider its approach. --- **`approval_percent`** `integer` — default: 100 Percentage of eligible calls that require approval, from 1 through 100. It applies only when the effective `action_mode` is `require_approval`. Calls outside the sampled percentage execute without human approval. --- You set these by configuring tool action controls on the agent revision. > **Tip** > > Reach for `return_rejection_to_model` when you want a reviewer to redirect the agent ("don't email this customer, log a note instead") rather than abort the whole run. Use `end_run` for hard stops. Task tools use `approval_percent` directly. Integration and MCP attachments use `default_approval_percent`, with per-tool overrides in `tool_approval_percents`. Use 100 when every eligible call must be reviewed. Sampling is deterministic for a run, action control, and tool-call ID. ### Gate selectively The point of per-tool controls is that you gate the side effect, not the whole agent. Attach every tool an email agent needs — read the inbox, look up the customer, draft the reply, send it — but leave the reads and the draft on `auto_run` and set only the **send** tool to `require_approval`. Reading and drafting are preparatory and safe to run unattended; sending is the irreversible step a person should sign off on. The agent works right up to the edge of the consequential action, then waits. ## What happens during a run When an agent calls a tool set to `require_approval`: #### The run pauses The run status becomes `waiting_for_human`. The agent stops before the tool executes. #### An approval request is raised Rightbrain creates an approval request identifying the tool, with a sanitized argument preview, a hash of the full arguments, and the agent's reason for requesting the call when available. #### A human reviews it A reviewer inspects the request and approves or rejects it over the API. #### The request is resolved Approval records the decision, after which the client calls `/resume` to execute the tool and continue the run. Rejection follows the tool's `rejection_behavior`. A resumed run keeps its session and files. A single run can pause and resume multiple times if it calls more than one gated tool. ## Finding and resolving requests List approval requests project-wide, or scope them to a single agent: | Purpose | Endpoint | | ---------------------------------- | ------------------------------------------------------------ | | List pending requests in a project | `GET …/task-agent/approval-request?status=pending` | | List requests for one agent | `GET …/task-agent/{agent_id}/approval-request` | | Get a single request | `GET …/task-agent/{agent_id}/approval-request/{id}` | | Approve | `POST …/task-agent/{agent_id}/approval-request/{id}/approve` | | Reject | `POST …/task-agent/{agent_id}/approval-request/{id}/reject` | | Resume a paused run | `POST …/task-agent/{agent_id}/approval-request/{id}/resume` | All paths are under `https://app.rightbrain.ai/api/v1/org/{org_id}/project/{project_id}`. ### List and approve a request Set `RB_API_KEY`, `ORG_ID` and `PROJECT_ID` in your environment. Run each command separately in the same terminal, reviewing the request before approving it. #### Find pending requests List requests awaiting a decision. Choose the request to review and its agent; pending requests may belong to different runs. **`list-requests.sh (lines 1-2)`** ```bash list-requests.sh (lines 1-2) curl --fail-with-body "https://app.rightbrain.ai/api/v1/org/$ORG_ID/project/$PROJECT_ID/task-agent/approval-request?status=pending" \ -H "Authorization: Bearer $RB_API_KEY" ``` #### Review the proposed action Replace the placeholders with that request's agent ID and approval request ID. Read its status, tool, sanitized argument preview and agent reason before deciding. The agent reason is untrusted model-authored text. A human should check the intended action against the original request before running the approval command. **`review-request.sh (lines 1-5)`** ```bash review-request.sh (lines 1-5) export AGENT_ID="{agent_id}" export REQUEST_ID="{approval_request_id}" curl --fail-with-body \ "https://app.rightbrain.ai/api/v1/org/$ORG_ID/project/$PROJECT_ID/task-agent/$AGENT_ID/approval-request/$REQUEST_ID" \ -H "Authorization: Bearer $RB_API_KEY" ``` #### Record the approval Run this only after the reviewer approves the proposed action. The empty JSON body is required; you can include a `decision_note` to explain the decision. This records approval but leaves the run paused. To reject, use `/reject` instead and follow the rejection behavior described below. **`approve-request.sh (lines 1-5)`** ```bash approve-request.sh (lines 1-5) curl --fail-with-body -X POST \ "https://app.rightbrain.ai/api/v1/org/$ORG_ID/project/$PROJECT_ID/task-agent/$AGENT_ID/approval-request/$REQUEST_ID/approve" \ -H "Authorization: Bearer $RB_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` #### Resume the same run Resume using the same approval request ID. This executes the approved tool and streams the rest of the existing run. Inspect the stream for `done`, `error`, or another `approval_required` event. Use the [agent event reader](/docs/api/run-agents) in application code; the end of an HTTP stream alone does not prove completion. **`resume-run.sh (lines 1-6)`** ```bash resume-run.sh (lines 1-6) curl --fail-with-body -N -X POST \ "https://app.rightbrain.ai/api/v1/org/$ORG_ID/project/$PROJECT_ID/task-agent/$AGENT_ID/approval-request/$REQUEST_ID/resume" \ -H "Authorization: Bearer $RB_API_KEY" \ -H "Content-Type: application/json" \ -H "Accept: text/event-stream" \ -d '{}' ``` #### Complete files **`list-requests.sh`** ```bash title="list-requests.sh" curl --fail-with-body "https://app.rightbrain.ai/api/v1/org/$ORG_ID/project/$PROJECT_ID/task-agent/approval-request?status=pending" \ -H "Authorization: Bearer $RB_API_KEY" ``` **`review-request.sh`** ```bash title="review-request.sh" export AGENT_ID="{agent_id}" export REQUEST_ID="{approval_request_id}" curl --fail-with-body \ "https://app.rightbrain.ai/api/v1/org/$ORG_ID/project/$PROJECT_ID/task-agent/$AGENT_ID/approval-request/$REQUEST_ID" \ -H "Authorization: Bearer $RB_API_KEY" ``` **`approve-request.sh`** ```bash title="approve-request.sh" curl --fail-with-body -X POST \ "https://app.rightbrain.ai/api/v1/org/$ORG_ID/project/$PROJECT_ID/task-agent/$AGENT_ID/approval-request/$REQUEST_ID/approve" \ -H "Authorization: Bearer $RB_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` **`resume-run.sh`** ```bash title="resume-run.sh" curl --fail-with-body -N -X POST \ "https://app.rightbrain.ai/api/v1/org/$ORG_ID/project/$PROJECT_ID/task-agent/$AGENT_ID/approval-request/$REQUEST_ID/resume" \ -H "Authorization: Bearer $RB_API_KEY" \ -H "Content-Type: application/json" \ -H "Accept: text/event-stream" \ -d '{}' ``` The `approve`, `reject`, and `resume` endpoints all require a JSON body. Send an empty object (`{}`) when you have nothing to add; `approve` and `reject` both accept an optional `decision_note`. Approving records the decision — it does not by itself continue the run. Call the `/resume` endpoint next to execute the approved tool and continue the run; `/resume` streams the remainder as Server-Sent Events, just like the original run call. Another gated call can pause it again. To reject instead, call `/reject`. With `end_run`, the run immediately fails and cannot be resumed. With `return_rejection_to_model`, the run stays `waiting_for_human`: call the approval request's `/resume` endpoint to let the agent handle the rejection. The rejected tool does not execute. ### Recovering from a lost response Read the approval request and run status before retrying a decision or resume. Repeating an already recorded decision returns `409`; resuming an already completed or failed run also returns `409`. If a decision succeeded but the run is still `waiting_for_human`, resume that request. Do not start a replacement run to recover a lost response: that would create separate work. Handle the resumed stream using the [agent event reader](/docs/api/run-agents). ```mermaid sequenceDiagram participant App as Your app participant Run as Agent run participant Rev as Reviewer App->>Run: POST …/run (streams SSE) Run->>Run: model calls a gated tool Run-->>App: status waiting_for_human Rev->>Run: POST …/approve (records the decision) Rev->>Run: POST …/resume (separate call) Run-->>Rev: streams the rest of the run Run->>Run: tool executes, run completes ``` > **Note** > > Approve (or reject) and resume are separate operations: the decision is auditable on its own, and the resume replays into the same session with the run's files intact. ## The approval audit trail Every approval decision is part of the run's permanent record. A completed run carries the IDs of the approval requests it raised (`approval_request_ids`) and the embedded requests themselves (`approval_requests`) — each with its status, the tool it gated, who requested it (`requested_by_user_id`), who decided it (`decided_by_user_id`), and any `decision_note` the reviewer left. The full approval-request response also includes `agent_reason` when the model supplied a rationale for the proposed call. If a rejection ended a run, the run's `termination_reason` records why. The trail includes the sanitized argument preview, with recognized secret fields and credential-shaped values redacted. It does not embed the full tool arguments. Treat `agent_reason` as untrusted model-authored text, not as proof that the action is safe or necessary. See [Observability & audit](/docs/production/observability). ## Related #### [Versioning & revisions](/docs/production/revisions) Approval policies live on the agent revision and roll back with it. #### [Triggers & runs](/docs/concepts/triggers-and-runs) How runs start and how they stream events. #### [Run agents via API](/docs/api/run-agents) Start agent runs and handle the SSE event stream. #### [Connections](/docs/concepts/connections) Separate the run actor from the account used for an approved action. #### [Observability & audit](/docs/production/observability) Inspect approval decisions with run identity and tool activity. #### [API Reference: Agents](/api-reference/api-reference/agents) Full approval-request endpoint reference. > Build reliable AI agents that run inside your existing tools and workflows. Rightbrain developer documentation.