Human-in-the-Loop Approvals
When a policy escalates an action, it is queued for human review instead of being executed immediately. A team member can then approve or reject the action from the dashboard or via API. Approved actions are executed immediately; rejected actions are discarded.
Flow
- Agent submits intent via
POST /api/execute - Policy engine decides to escalate
- Intent is queued as a Pending Approval (7-day TTL)
- Agent receives
"decision": "escalated"response - Human reviews in the Approvals dashboard
- On Approve: action is executed via connector and logged
- On Reject: action is discarded and logged
Using the Dashboard
The Approvals page shows all pending approvals as cards with:
- Action & channel — what the agent wants to do
- Content preview — the message or text being sent
- Parameters — action-specific details (ticket ID, email recipient, etc.)
- Risk score — content verification score if applicable
- Policy matches — which policies triggered the escalation
- Agent context — which agent, session, and user
- Approve / Reject buttons
Already-reviewed approvals are shown dimmed with the reviewer name and timestamp.
Approvals API
List pending approvals
curl https://your-domain.com/api/approvals \
-H "Authorization: Bearer hg_sk_your_api_key"Response:
{
"approvals": [
{
"executionId": "exec_escalated456",
"action": "send_email",
"channel": "email",
"content": "Hi, your refund of $500 has been issued.",
"params": {
"to": "customer@gmail.com",
"subject": "Refund confirmation",
"body": "Hi, your refund of $500 has been issued."
},
"riskScore": 0.35,
"policyMatches": ["Escalate external emails"],
"context": {
"agent": "my-assistant",
"sessionId": "sess_abc123",
"userId": "user_jane"
},
"status": "pending",
"createdAt": "2025-03-15T14:30:00.000Z"
}
]
}Approve a pending action
On approval, the action is executed immediately via the matching connector (e.g., sends the email, replies to the ticket). A new execution record is created with decision: "approved".
curl -X POST https://your-domain.com/api/approvals/exec_escalated456 \
-H "Authorization: Bearer hg_sk_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "decision": "approve" }'Response:
{
"executionId": "exec_escalated456",
"decision": "approved",
"result": {
"success": true,
"data": { "emailId": "em_abc123" }
}
}Reject a pending action
The action is discarded. The agent can check the execution status to know it was rejected.
curl -X POST https://your-domain.com/api/approvals/exec_escalated456 \
-H "Authorization: Bearer hg_sk_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "decision": "reject" }'Response:
{
"executionId": "exec_escalated456",
"decision": "rejected"
}End-to-End Example
Here is a complete walkthrough: create a policy, trigger an escalation, and approve it.
Step 1: Create an escalation policy
curl -X POST https://your-domain.com/api/policies \
-H "Authorization: Bearer hg_sk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Escalate large refunds",
"trigger": ["issue_refund"],
"conditions": [
{ "field": "param", "operator": "gt", "value": 200, "paramName": "refund_amount" }
],
"decision": "escalate",
"priority": 7
}'Step 2: Agent submits a $500 refund intent
curl -X POST https://your-domain.com/api/execute \
-H "Authorization: Bearer hg_sk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"action": "issue_refund",
"params": {
"refund_amount": 500,
"order_id": "ORD-9876",
"reason": "Customer requested cancellation"
},
"context": { "agent": "my-assistant", "userId": "user_jane" }
}'Response — escalated because refund_amount (500) > 200:
{
"executionId": "exec_refund789",
"decision": "escalated",
"riskScore": 0,
"matchedPolicies": ["Escalate large refunds"],
"message": "Action requires human approval"
}Step 3: Manager approves in the dashboard (or via API)
curl -X POST https://your-domain.com/api/approvals/exec_refund789 \
-H "Authorization: Bearer hg_sk_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "decision": "approve" }'The refund action is now executed (or the agent is notified to proceed).
Step 4: Check the execution log
curl https://your-domain.com/api/executions \
-H "Authorization: Bearer hg_sk_your_api_key"You will see two execution records: the original "escalated" one and the "approved" one created after human approval.
Agent Workflow Resumption
When a CrewAI agent's action is escalated, the workflow can pause and resume automatically using the GroundTruthAwaitApprovalTool. The tool polls GET /api/approvals/{execution_id} until the approval is resolved or a timeout is reached.
Poll approval status
curl https://your-domain.com/api/approvals/exec_escalated456 \
-H "Authorization: Bearer hg_sk_your_api_key"Returns the approval object with its current status:
{
"approval": {
"executionId": "exec_escalated456",
"action": "reply_ticket",
"status": "approved",
"reviewedBy": "admin@example.com",
"reviewedAt": "2025-03-15T15:05:00.000Z"
}
}Webhook notifications
When you have a webhook URL configured, GroundTruth fires execution.approved and execution.rejected events when a human reviews an approval. This lets external orchestrators react in real time without polling.
{
"id": "evt_...",
"type": "execution.approved",
"created_at": "2025-03-15T15:05:00Z",
"data": {
"executionId": "exec_escalated456",
"action": "reply_ticket",
"decision": "approved",
"reviewedBy": "admin@example.com",
"sessionId": "sess_abc123"
}
}Using the Python tool
See the CrewAI integration guide for full details on GroundTruthAwaitApprovalTool, including a complete multi-step workflow example.
Permissions
| Action | Who can do it |
|---|---|
| Submit intents (POST /api/execute) | Any authenticated user or API key |
| View executions & approvals | Any authenticated user or API key |
| Approve / reject | Owner, Admin, or API key |
| Create / edit / delete policies | Owner, Admin, or API key |