Questions · Updated 2026-09-28
How do I require human approval before an AI agent sends email?
Require human approval by routing the agent's send through the approval queue: a held send is a row at GET /agent-actions in state pending until a person calls POST /agent-actions/{id}/approve or POST /agent-actions/{id}/reject. The agent is answered approval_required and its own key cannot approve.
Require human approval before an AI agent sends email by letting the send be held instead of sent: the held send is a row at GET /agent-actions with state pending, a person releases it with POST /agent-actions/{id}/approve or refuses it with POST /agent-actions/{id}/reject, and the agent is answered approval_required. The key that asked cannot approve, and no API key can make either decision.
What is held today
A send is held when the loop guard flags it, and an agent can ask for approval itself: over MCP, request_approval puts a send into the same queue before any attempt to send it. That is the way to gate a send you already know needs a person. What the loop guard measures is on Agent email loop detection, and Agent email approvals describes each field on a queue row.
The person's side
GET /agent-actions lists every held action, newest first, and state filters it to pending, approved, rejected or killed. Approving is POST /agent-actions/{id}/approve; its summary is "Execute the held send through the normal accept path. A person signed in to the console decides; the action row records who and when." The response carries message_id, and that message then appears in GET /emails like any other. Rejecting is POST /agent-actions/{id}/reject with a reason; the summary says "the reason is preserved with the row".
curl -sS -X POST https://api.agentisend.com/agent-actions/9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42/reject \
-H "Authorization: Bearer $AGENTISEND_API_KEY" \
-H "Idempotency-Key: $(uuidgen)"201
{
"created_at": "2026-09-04T09:14:00Z",
"decided_at": "2026-09-04T09:14:00Z",
"decided_by": "string",
"decision_reason": "string",
"id": "9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42",
"kind": "string",
"payload": {},
"preview": {}
}A full_access key calling either one gets human_action_required, whose message is "This is a person’s decision, so an API key cannot make it."; a sending_access key is stopped earlier with restricted_api_key. Register a webhook with POST /webhooks for agent.approval_requested, agent.approved and agent.rejected, so a held send pages someone instead of waiting to be noticed.
The agent's side
The catalogue message for approval_required is "This action requires human approval before it executes." The fix is "Do not send it again: a person approves or rejects it in the console under Agents → Approvals, and approving sends it — the message then appears in GET /emails. action_id in this error names the held send; the key that asked cannot approve itself, and a retry waits on the same approval." retryable is false. The agent's correct move is to record action_id and wait for the decision: the agent.approved or agent.rejected webhook, that row's state on GET /agent-actions (a full_access read), or list_agent_actions over MCP. A resubmission is not a faster approval.