AI agent workflows often involve waiting for external events before proceeding. Email verification is the most common example, but the pattern generalizes: email as a state machine, where each incoming message advances the workflow to the next state.

The Pattern

states = {
  "awaiting_verification": handle_verification_email,
  "awaiting_reply": handle_human_reply,
  "awaiting_approval": handle_approval_email,
  "complete": None
}

async def run_workflow(inbox_id):
    state = "awaiting_verification"
    while state != "complete":
        email = await wait_for_email(inbox_id)
        next_state_fn = states.get(state)
        if next_state_fn:
            state = await next_state_fn(email)

Real Example: Approval Workflow

  1. Agent sends a document to a human for review
  2. Agent waits for an email with "approved" or "rejected" in the subject
  3. If approved, agent proceeds with next steps
  4. If rejected, agent revises the document and resends
async def handle_approval_email(email):
    subject = (email.get("subject") or "").lower()
    if "approved" in subject:
        return "complete"
    elif "rejected" in subject:
        await revise_and_resend()
        return "awaiting_approval"  # Back to waiting
    return "awaiting_approval"  # Unknown email, keep waiting

Why Email Works Well as State

  • Email is asynchronous by nature, matching the async nature of agent workflows
  • Email threads preserve conversation history automatically
  • Humans are comfortable with email, making human-in-the-loop workflows natural
  • Email is universal: no special client or app needed on the human side

Durability

Unlike in-memory state, email-driven workflows survive process restarts. If your agent process crashes, the emails are still in the inbox waiting. The agent can resume by reading the inbox on restart.