Approval Workflows (HITL)
Production - Human-in-the-loop approval for sensitive agent actions
The Human-in-the-Loop (HITL) approval system requires human review and approval before agents execute sensitive actions. This includes database modifications, external API calls, high-cost LLM operations, and any action flagged by guardrails.
12.2.8.1Approval Architecture
The approval system consists of three components:
| Component | File | Purpose |
|---|---|---|
ApprovalHandler | src/agents/hitl.py | Evaluates whether a tool call requires approval |
ApprovalWorkflow | src/agents/hitl.py | Manages the lifecycle of approval requests |
PostgresApprovalStore | src/agents/approval/postgres_approval_store.py | Persists approval requests to PostgreSQL |
Approval Flow
Agent wants to execute tool
|
v
ApprovalHandler.check_and_request_approval()
|
+-- Is tool in approval-required list? --No--> Execute directly
|
Yes
|
v
Create ApprovalRequest
|
v
Notify reviewer(s) via callback URL
|
v
Agent enters WAITING_APPROVAL status
|
v
Reviewer approves/rejects via API
|
+-- Approved --> Execute tool
|
+-- Rejected --> Return rejection to agent12.2.8.2ApprovalRequest Model
@dataclass
class ApprovalRequest:
id: str
session_id: str
tenant_id: str
agent_id: str
tool_call: ToolCall
status: ApprovalStatus # PENDING, APPROVED, REJECTED, EXPIRED
created_at: datetime
reviewer_id: str | None = None
reviewed_at: datetime | None = None
notes: str | None = None
expires_at: datetime | None = None
class ApprovalStatus(str, Enum):
PENDING = "pending"
APPROVED = "approved"
REJECTED = "rejected"
EXPIRED = "expired"12.2.8.3API Endpoints
# List pending approvals
curl http://localhost:8000/api/v1/agents/approvals/pending?tenant_id=acme-corp
# Approve a request
curl -X POST http://localhost:8000/api/v1/agents/approvals/{request_id}/approve \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: acme-corp" \
-d '{
"reviewer_id": "user-admin-123",
"notes": "Approved - query is read-only and within scope"
}'
# Reject a request
curl -X POST http://localhost:8000/api/v1/agents/approvals/{request_id}/reject \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: acme-corp" \
-d '{
"reviewer_id": "user-admin-123",
"notes": "Rejected - query accesses restricted schema"
}'Approval Response
{
"id": "approval-uuid-789",
"status": "approved",
"tool_call": {
"name": "execute_sql",
"arguments": {
"sql": "SELECT customer_name, email FROM customers WHERE region = 'EMEA'"
}
},
"reviewer_id": "user-admin-123",
"notes": "Approved - query is read-only and within scope",
"reviewed_at": "2025-01-15T10:35:00Z"
}