# Browser agents stuck at email verification: a working fix

A browser agent stops at "we sent a code to your email" because it has no
inbox it can read. Give it one: a Botmail address it types into the form,
plus a tool that blocks until the email lands and returns the code. With the
Botmail CLI that tool is one command:

```sh
npx botmail wait --from github.com --extract code
```

Below are working versions for Browser Use, Playwright, and agents like
Claude that drive a computer or browser with a shell or MCP tools.

## Before you start

Claim an address once and approve it from the email Botmail sends you:

```sh
npx botmail claim ada-signups --human you@example.com
```

The key is saved in `~/.config/botmail/config.json`, so later `botmail`
commands on that machine need no setup. On a server, set `BOTMAIL_KEY`
instead.

Two flags matter for automation:

- `--since 5m` also accepts matching mail that arrived in the last five
  minutes. Without it, `wait` only sees mail that arrives after it starts,
  and a fast sender can beat it.
- `--json` prints `{"code": "482913", "from": …, "message_id": …}`. Output
  is JSON anyway when stdout isn't a terminal, and a timeout exits with code
  7 and a JSON error on stderr.

## Browser Use (Python)

Browser Use lets you add your own actions with `Tools()`. This one gives the
agent a code-reading step. Tested with `browser-use` 0.13:

```python
import asyncio
import json

from browser_use import ActionResult, Agent, ChatBrowserUse, Tools

ADDRESS = "ada-signups@botmail.pro"
tools = Tools()


@tools.registry.action("Wait for the verification code a website emailed to the agent's address")
async def get_email_code(sender_domain: str) -> ActionResult:
    proc = await asyncio.create_subprocess_exec(
        "npx", "-y", "botmail", "wait",
        "--from", sender_domain, "--extract", "code",
        "--since", "5m", "--timeout", "3m", "--json",
        stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
    )
    out, err = await proc.communicate()
    if proc.returncode != 0:
        return ActionResult(error=json.loads(err)["error"]["message"])
    return ActionResult(extracted_content=json.loads(out)["code"])


agent = Agent(
    task=f"Sign up at https://example.com with the email {ADDRESS}. "
    "When it asks for a verification code, call get_email_code with sender_domain='example.com'.",
    tools=tools,
    llm=ChatBrowserUse(),
)
asyncio.run(agent.run())
```

Browser Use builds the action's parameters from the type hints, so the model
fills in `sender_domain` itself. `--since 5m` matters here: the agent only
calls the action after it has submitted the form.

## Playwright

In a scripted flow you know exactly when the email is triggered, so start
waiting just before the click and await the result after it:

```ts
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import { test, expect } from "@playwright/test";

const run = promisify(execFile);

async function waitForCode(from: string, to: string): Promise<string> {
  const { stdout } = await run("npx", ["-y", "botmail", "wait",
    "--from", from, "--to", to, "--extract", "code", "--timeout", "2m", "--json"]);
  return JSON.parse(stdout).code;
}

test("sign up with email verification", async ({ page }) => {
  const email = `ada-signups+run${Date.now()}@botmail.pro`;
  await page.goto("https://example.com/signup");
  await page.getByLabel("Email").fill(email);

  const code = waitForCode("example.com", email); // start before the click
  await page.getByRole("button", { name: "Continue" }).click();

  await page.getByLabel("Verification code").fill(await code);
  await expect(page.getByText("Welcome")).toBeVisible();
});
```

The `+run…` tag routes to the same mailbox, and `--to` makes each run wait
only for its own email, so parallel workers don't steal each other's codes.
Some sites reject `+` in addresses; drop the tag and `--to` for those.

For links instead of codes (magic links, "confirm your email" buttons), use
`--extract link` and `page.goto()` the result.

## Claude and other computer-use agents

Agents that drive a browser and also have a terminal, such as Claude Code or
a computer-use setup with a shell tool, can run the same command. Tell them:

```text
Sign up at example.com with ada-signups@botmail.pro. When it asks for a code, run `npx botmail wait --from example.com --extract code --since 5m` in the terminal and type the code it prints.
```

Agents with no shell but with Botmail's MCP server
(`https://botmail.pro/mcp`) use tools instead: `wait_for_mail` blocks up to
30 seconds and returns new mail with a preview, then `read_conversation`
shows the full message so the model can read the code. Keep the cursor it
returns and pass it back, so a code that arrives between calls isn't missed.

## When it still doesn't work

- **No code found.** `--extract code` looks for 4 to 8 digits, or 6 to 8
  capital letters and digits, near words like "code" or "verify". Codes
  such as `7X4-K9Q` aren't recognized; run `botmail wait --from example.com`
  without `--extract` and read the text.
- **The email never arrives.** Some senders take a minute or two, so raise
  `--timeout`. If the site showed an error for the address, it may not
  accept that domain.
- **CAPTCHAs and phone checks** aren't email problems. Hand those steps to a
  person.

Only sign up where you're allowed to. Botmail's
[acceptable use policy](https://botmail.pro/acceptable-use) forbids creating accounts to get
around a site's limits. For the code-extraction details, see
[Wait for a verification code](https://botmail.pro/guides/wait-for-verification-code); for
the bigger picture, see
[Verification emails for AI agents](https://botmail.pro/guides/ai-agent-verification-emails).

## Questions

### How do I handle email verification in Browser Use?

Register a custom action with Tools() that runs npx botmail wait --from <site domain> --extract code --since 5m --json and returns the code in an ActionResult. Give the agent its Botmail address in the task.

### How do I get an email verification code in a Playwright test?

Start npx botmail wait --from <domain> --to <address> --extract code before clicking the button that sends the email, then await it and fill the code. A plus tag per run keeps parallel tests apart.

### Why does my agent miss the verification email?

botmail wait only watches mail that arrives after it starts. If the agent submits the form first, add --since 5m so mail that already arrived also counts.

---

Source: https://botmail.pro/guides/browser-agent-email-verification
Agent instructions: https://botmail.pro/skill.md
All guides: https://botmail.pro/llms.txt
