Approvals

Pause risky agent work for human review before a gated tool or sub-agent runs, then resume the same execution in place.

Approvals

Approvals enable human-in-the-loop workflows where an agent execution pauses before a sensitive action and waits for a human decision. This is essential for high-stakes operations — sending customer communications, moving money, modifying data — where an automated decision needs human oversight.

The Approvals page shows the review queue and approval health for human-in-the-loop workflows, including pending decisions, outcomes, and decision speed.

Human-in-the-Loop Overview

In API v2 approvals are execution-scoped. A tool or sub-agent attached to an agent version with requires_approval causes the execution to pause at waiting_approval right before that call runs, instead of proceeding automatically.

1Input
2Agent Execution
3Gated tool / sub-agent
4Waiting Approval
5Approve or Deny

Approving resumes the same execution and lets the gated call proceed. Denying resumes the execution with a denial the agent must handle — nothing is discarded, and the run continues to a normal terminal state.

In Studio, the agent can pause a chat response before a gated action. The reviewer sees the original request, the pending call, and clear Deny or Approve actions before the run continues.

Requiring Approval

Approval gating is configured per attachment on the agent version. Set requires_approval on a tool or sub-agent, and set an approval_policy to control who may decide.

Technical detailsGate a tool and set the approval policy
from promptrails import (
    PromptAgentConfig,
    ApprovalPolicy,
    ToolAttachment,
)
 
client.agents.create_version(
    agent_id="your-agent-id",
    version="1.1.0",
    config=PromptAgentConfig(prompt_id="your-prompt-id"),
    tools=[ToolAttachment(mcp_tool_id="send-email-tool", requires_approval=True)],
    approval_policy=ApprovalPolicy(mode="admins"),
    set_current=True,
    message="Require approval before sending customer email",
)
FieldDescription
tools[].requires_approvalPause the execution before this tool call runs
sub_agents[].requires_approvalPause before delegating to / handing off to this sub-agent
approval_policy.modeWho may decide: admins (default), assigned, or any_member
approval_policy.member_idsSpecific members allowed to decide when mode is assigned
Technical detailsApproval lifecycle and API details

Approval Flow

1. Execution Reaches a Gated Call

When the agent is about to run a tool or sub-agent marked requires_approval, the execution:

  1. Sets its status to waiting_approval
  2. Records approval_expires_at (when the pending decision lapses)
  3. Fires a webhook event (execution.waiting_approval)

2. A Human Reviews

Paused runs surface in:

  • The PromptRails dashboard (Approvals page)
  • The execution-scoped approval inbox (/api/v1/executions/approval-inbox)
  • Webhook notifications

The reviewer sees the agent, the execution input and progress so far, the pending call, and the expiry.

3. Decision

Approve or deny the paused execution. The endpoints are execution-scoped:

# List runs parked at waiting_approval
inbox = client.executions.approval_inbox()
 
for execution in inbox.data:
    print(execution.id, execution.agent_id, execution.approval_expires_at)
 
# Approve — resumes the run and lets the gated call proceed
client.executions.approve("execution-id", reason="Response looks accurate")
 
# Deny — resumes the run with a denial the agent must handle
client.executions.deny("execution-id", reason="Do not send external email")

JavaScript SDK

const inbox = await client.executions.approvalInbox()
 
for (const exec of inbox.data) {
  await client.executions.approve(exec.id, { reason: 'looks good' })
  // or: await client.executions.deny(exec.id, { reason: 'denied' })
}

Go SDK

inbox, _ := client.Executions.ApprovalInbox(ctx, nil)
for _, exec := range inbox.Data {
    _, _ = client.Executions.Approve(ctx, exec.ID, &promptrails.DecideParams{Reason: "looks good"})
    // or: client.Executions.Deny(ctx, exec.ID, &promptrails.DecideParams{Reason: "denied"})
}

4. Execution Continues

  • Approved: the gated call runs and the execution proceeds to completed (or failed).
  • Denied: the agent receives the denial and continues without the gated call, reaching a normal terminal state.
FieldTypeDescription
statusstringwaiting_approval while paused
approval_expires_attimestampWhen the pending decision lapses
parent_execution_idKSUIDSet when the gated call is a child in the tree
trace_idstringLink to the execution trace

See Executions for the full execution tree and status model.

Webhook Notifications

When an execution pauses or a decision is made, webhook events fire:

EventDescription
execution.waiting_approvalAn execution is paused for approval
execution.approvedA paused execution was approved
execution.deniedA paused execution was denied

These can notify reviewers via Slack, email, or other channels.

Best Practices

  • Gate the risky call, not the whole agent — Mark only the sensitive tool or sub-agent with requires_approval so routine steps are not blocked.
  • Choose the right policy — Use admins for high-stakes actions and assigned with member_ids when specific reviewers own a workflow.
  • Watch the expiryapproval_expires_at prevents stale approvals from blocking the pipeline; set up webhook notifications so reviewers act in time.
  • Give reviewers context — Ensure the execution input and progress carry enough information to make an informed decision.
  • Always provide a reason — Record a reason when denying to help improve the agent.
  • Agents — Attaching tools and sub-agents to agents
  • Agent Versioningrequires_approval and approval_policy
  • Executions — Execution status, trees, and the approval inbox
  • Agent Triggers — Trigger-driven executions with approvals