Skip to main content

Human-in-the-Loop Approvals

AgentFlow’s approval system gates sensitive tool executions with human review. When an agent or direct tool run reaches a tool that requires approval, execution pauses until an authorized user approves or denies the action.

How it works

  1. A tool is registered or configured with require_approval=True.
  2. During execution, the agent or direct run proposes a call to that tool.
  3. AgentFlow creates an approval request with the tool ID, tool name, proposed arguments, conversation context, and timeout.
  4. The reviewer approves, approves with modified_arguments, denies, cancels, or lets the request expire.
  5. If approved, the tool executes with the reviewed arguments. If denied, the agent is informed and can adjust.

Configuring approval on tools

Via SDK

tool = await client.agents.tools.create(
    agent_id,
    name="send_contract",
    description="Send a contract for signature.",
    function_code="""
async def send_contract(contract_id: str, recipient: str) -> dict:
    return {"status": "queued", "contract_id": contract_id, "recipient": recipient}
""",
    require_approval=True,
)

await client.agents.tools.set_approval(
    agent_id,
    tool.id,
    enabled=True,
    timeout=300,
)
There is no public agent.register_tool(...) or tool.set_approval(...) SDK object API. Use client.agents.tools.create(...) for runtime tool creation and client.agents.tools.set_approval(...) for existing assigned tools.

Via REST API

PUT /api/v1/agent/{agent_id}/tools/{tool_identifier}/approval
{
  "enabled": true,
  "timeout_seconds": 300
}
tool_identifier may be the persisted tool ID or the tool name.

Approval API

List pending approvals

GET /api/v1/approvals/pending?page=1&page_size=20&tool_name=send_contract
{
  "approvals": [
    {
      "id": "appr_abc123",
      "tool_id": "tool_xyz",
      "tool_name": "send_contract",
      "arguments": {
        "recipient": "[email protected]",
        "contract_id": "contract_456"
      },
      "status": "pending",
      "created_at": "2026-01-15T10:30:00Z",
      "expires_at": "2026-01-15T10:35:00Z"
    }
  ],
  "total": 1,
  "pending": 1,
  "expired": 0,
  "page": 1,
  "page_size": 20
}

Respond to an approval

POST /api/v1/approvals/{approval_id}/respond
{
  "status": "approved",
  "reason": "Verified contract details are correct",
  "modified_arguments": {
    "recipient": "[email protected]"
  }
}
Status values: approved, denied. modified_arguments is optional and only applies to approved responses. When supplied, those reviewed arguments replace the originally proposed arguments for execution. SDK equivalent:
await client.approvals.respond(
    "appr_abc123",
    decision="approved",
    reason="Fixed recipient before sending",
    modified_arguments={"recipient": "[email protected]"},
)

Bulk operations

Respond to multiple approvals at once:
POST /api/v1/approvals/bulk-respond
{
  "approval_ids": ["appr_abc123", "appr_def456"],
  "status": "approved",
  "reason": "Batch approved after review"
}
Bulk responses apply one decision to all listed requests. Use individual responses when each request needs different edited arguments.

Cancel a pending approval

DELETE /api/v1/approvals/{approval_id}?reason=No+longer+needed

Monitoring and analytics

Per-tool statistics

GET /api/v1/approvals/tools/{tool_id}/stats
{
  "tool_id": "tool_xyz",
  "tool_name": "send_contract",
  "total_requests": 47,
  "approved_count": 42,
  "denied_count": 3,
  "timeout_count": 1,
  "bypassed_count": 0,
  "auto_approved_count": 0,
  "cancelled_count": 1,
  "success_rate": 0.89,
  "avg_response_time": 12.5
}

Dashboard summary

GET /api/v1/approvals/dashboard/summary
{
  "total_pending": 3,
  "expiring_soon": 1,
  "total_today": 12,
  "approval_rate": 0.91,
  "avg_response_time": 8.3
}

Expiration and cleanup

Approval requests have a configurable timeout, defaulting to 300 seconds. Expired requests are tracked separately and can be cleaned up:
POST /api/v1/approvals/cleanup-expired
Approval APIs live under /api/v1/approvals/...; there is no /api/approvals/... public route.