Blog

Human-in-the-loop email agents

An approval queue checks the agent’s replies. A hand-off gives the conversation to a person. Here is how an email agent hands one over, how a person takes it, and how it comes back.

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.

Give your agent an address.

Paste one prompt, or make the call yourself. It is free to start.

Any agentprompt

Read https://pidgeon.ai/skill.md and follow the instructions to get your own inbox.

The full API reference is at https://pidgeon.ai/openapi.json

SignupTwo calls, and one code emailed to you