# Wait for a verification code by email: CLI, REST and JSON

To receive a one-time code by email from code, give the agent or script a
Botmail address and block until the email arrives. The CLI does it in one
line and prints only the code:

```sh
npx botmail wait --from github.com --extract code --timeout 5m
# 482913
```

This page is the reference for that command and for doing the same over the
REST API with `curl`. For why agents need this at all, see
[Verification emails for AI agents](https://botmail.pro/guides/ai-agent-verification-emails).

## The CLI command

`botmail wait` long-polls your mailbox and returns the first incoming email
that matches every filter you give it.

| Flag | What it does |
| --- | --- |
| `--from github.com` | Sender filter. Matches `noreply@github.com` and subdomains like `mail.github.com`. |
| `--subject 'code\|verify'` | Case-insensitive regular expression on the subject |
| `--to ada+run7@botmail.pro` | Only mail sent to this exact address, useful with `+` tags |
| `--extract code` | Print just the code; keep waiting if the email has none |
| `--extract link` | Print just the most likely verification or sign-in link |
| `--extract links` | Print every link in the email |
| `--since 10m` | Also accept matching mail from the last 10 minutes |
| `--timeout 2m` | Give up after this long (default 5 minutes). Accepts `90`, `30s`, `5m`, `1h`. |
| `--json` | Force JSON output (automatic when stdout isn't a terminal) |

Without `--since`, only mail that arrives after the command starts counts.
Start it before you trigger the email, or add `--since`.

### JSON output

When piped or run with `--json`, the result is one object:

```json
{"message_id":"msg_…","thread_id":"thr_…","from":"noreply@github.com","subject":"[GitHub] Please verify your device","received_at":"2026-10-12T09:33:50Z","code":"482913"}
```

`--extract link` returns `link` and `links` instead of `code`; with no
`--extract` you get `text`, the message body. If time runs out, the command
exits with code 7 and writes this to stderr:

```json
{"error":{"code":"timeout","message":"No matching mail within 120 s.","hint":"Raise --timeout, loosen --from/--subject, or add --since 10m to include mail that already arrived.","exit":7}}
```

Other exit codes: 3 for a missing or invalid key, 5 for network errors.

### How code extraction works

`--extract code` scans the subject and the body for candidates and scores
them. It accepts 4 to 8 digits, two groups of three digits (`381-204` or
`381 204`, printed as `381204`), and 6 to 8 characters mixing capital
letters and digits (`K7Q2PX`). A candidate scores higher when the same or a
neighbouring line contains a word like "code", "verification", "OTP",
"sign in" or "2FA", when it's exactly six digits, when it sits on a line of
its own, and when it's in the subject. Years like 2026 are ignored.

If no candidate scores high enough, the command treats that email as "not
the one" and keeps waiting. Formats it won't recognize include codes with
lowercase letters and mixed codes split by a hyphen, such as `7X4-K9Q`. For
those, drop `--extract`, read `text` from the JSON, and parse it yourself.

### Links instead of codes

`--extract link` ignores links that look like unsubscribe, privacy, terms,
help or support pages and images. It then picks the first link containing a
hint such as `verify`, `confirm`, `magic`, `login`, `token`, `reset` or
`invite`, or the only remaining link if there's exactly one. HTML-only
emails work too: Botmail converts them to text, and a button becomes its
label followed by the URL in parentheses.

## The same thing over REST

Any language can do this with two endpoints. Without `after`,
`GET /v1/events` returns your current position immediately. Pass it back as
`after` with `wait=30` and the request blocks until an event arrives or 30
seconds pass. `wait` can be at most 30.

```sh
curl -s "https://botmail.pro/v1/events" -H "Authorization: Bearer $BOTMAIL_KEY"
# {"events":[],"next_after":6}

curl -s "https://botmail.pro/v1/events?after=6&wait=30&types=message.received" \
  -H "Authorization: Bearer $BOTMAIL_KEY"
```

Each `message.received` event carries `message_id`, `thread_id` and
`data.from` and `data.subject`, but not the body. Fetch the message for
`text`:

```sh
curl -s "https://botmail.pro/v1/messages/msg_…" -H "Authorization: Bearer $BOTMAIL_KEY" | jq -r .text
```

Put together, with `curl` and `jq`:

```sh
#!/usr/bin/env bash
# Usage: wait_code.sh github.com  -> prints the first 6-digit code from that sender
set -euo pipefail
API="${BOTMAIL_URL:-https://botmail.pro}/v1"
AUTH="Authorization: Bearer $BOTMAIL_KEY"
FROM="$1"

after=$(curl -sf "$API/events" -H "$AUTH" | jq -r .next_after)   # start from now
deadline=$((SECONDS + 300))
while (( SECONDS < deadline )); do
  page=$(curl -sf "$API/events?after=$after&wait=30&types=message.received" -H "$AUTH")
  after=$(jq -r .next_after <<<"$page")
  for id in $(jq -r --arg f "$FROM" '.events[] | select(.data.from | endswith($f)) | .message_id' <<<"$page"); do
    code=$(curl -sf "$API/messages/$id" -H "$AUTH" | jq -r .text | grep -oE '\b[0-9]{6}\b' | head -1 || true)
    if [[ -n "$code" ]]; then echo "$code"; exit 0; fi
  done
done
echo "no code within 5 minutes" >&2; exit 1
```

Always carry `next_after` forward, even when `events` is empty, so nothing
is skipped or seen twice. The key needs only the `read` scope, and works
before you've approved the address, so an agent can receive codes while it
waits for you.

## From an MCP client

Agents connected to `https://botmail.pro/mcp` call `wait_for_mail`. It
blocks up to 30 seconds (25 by default), returns new mail with a preview and
a `cursor`, and the agent reads the full message with `read_conversation`.
There's no server-side code extraction over MCP; the model reads the code
from the text.

Need this inside a browser automation? See
[Browser agents stuck at email verification](https://botmail.pro/guides/browser-agent-email-verification).
All CLI commands are on the [CLI page](https://botmail.pro/cli).

## Questions

### How do I extract a verification code from an email automatically?

Run npx botmail wait --from <sender domain> --extract code. It blocks until a matching email arrives and prints the most likely one-time code, or JSON with the code when piped.

### Is there an API to receive OTP emails?

Yes. Long-poll GET /v1/events?after=<seq>&wait=30&types=message.received on Botmail's REST API, then fetch GET /v1/messages/{id} and read the code from the text field.

### What happens if the code email never arrives?

botmail wait gives up after --timeout (5 minutes by default), exits with code 7 and prints a JSON error with a hint on stderr.

### Can it extract magic links instead of codes?

Yes. --extract link prints the most likely verification or sign-in link and skips unsubscribe, privacy and help links. --extract links prints every link.

---

Source: https://botmail.pro/guides/wait-for-verification-code
Agent instructions: https://botmail.pro/skill.md
All guides: https://botmail.pro/llms.txt
