A queue for approval is not a hand-off
Most "human in the loop" setups for email agents are an approval queue: the agent writes a reply, and a person checks it before it goes. That is useful, and it is not the hard case. The hard case is the conversation the agent should stop answering altogether: the customer who wants a refund above what it may approve, the supplier who is angry, the reply that is really a legal question.
For that, the conversation has to change hands. A person has it now, the agent has stepped back, and everything watching knows.
The owner is a field on the thread
In Pidgeon every conversation has an owner: agent, human or automation. Handing one over is one call, because it is one intention:
curl https://pidgeon.ai/api/v1/threads/$THREAD_ID \
-X PATCH \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"owner_type": "human",
"status": "human_review",
"reason": "They want a refund above what I can approve."
}'
The people who can answer as that address get a notification and an email with the reason. In the inbox the conversation sits under Waiting for a person with a Take it button. They reply as the same address, from the web inbox or from Apple Mail with an app password, and a reply takes the conversation over even if nobody pressed anything.
Your code hears all of it: thread.assigned and thread.updated fire on every change, and thread.updated carries the previous owner beside the new one. Hand it back with owner_type: "agent" and the agent finds it among its own conversations. Over MCP, the update_thread tool does the same.
The other side keeps writing to the same address, in the same thread. Whether to tell them a person has joined is your call.
An inbox per role, and a key for each
Builders who run several agents tend to land on the same shape:
- One inbox per agent role.
support@,billing@,scout@, each created in one call. Mail arriving at an agent address starts a conversation the agent owns, so each agent's queue is the list of its own threads. - A key per agent. Scope each agent's key to its own addresses, and it cannot read another agent's conversations or the inbox a person works from.
- An inbound policy per inbox. Give an address its own allow list, so the billing agent only hears from customers on file. A sender on the list counts only when its DMARC passes, so a forged From is a stranger.
Start
The hand-off guide walks through all of it against your own address in about five minutes: mail arrives, the agent gets stuck, a person takes it and hands it back. To have a person check each reply instead, see approving drafts.