# Wait for email

`wait_for_email` is a long-poll for the next matching message. It returns the canonical message plus
an extracted OTP code and verification link when present. Use it for sign-in, verification, and other
time-limited flows where a polling loop would add avoidable state.

## Flow

1. Create an inbox and use its address in the external flow.

2. Trigger the email.

3. Start the wait with narrow sender or subject filters and `since_now: true`.

4. Use the returned message and extracted credentials. A timeout is a normal result, not an HTTP
   error.

## Examples

```ts title="wait-for-code.ts"
import { Extrovert } from "@extrovert.dev/sdk";

const extrovert = new Extrovert({ apiKey: process.env.EXTROVERT_API_KEY! });
const inbox = await extrovert.inboxes.create({ username: "signup-agent" });

await fetch("https://acme.test/signup", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ email: inbox.address }),
});

const result = await inbox.waitForEmail({
  from: "no-reply@acme.test",
  subject: "verification",
  match: "(?i)code",
  link_hint: "verify",
  since_now: true,
  timeout_seconds: 120,
});

if (result.timed_out) throw new Error("No verification email arrived in time");
console.log(result.extracted.otp);
console.log(result.extracted.link);
```

  ```json title="wait_for_email"
{
  "name": "wait_for_email",
  "arguments": {
    "inbox": "pmbx_…",
    "from": "no-reply@acme.test",
    "subject": "verification",
    "regex": "(?i)code",
    "link_hint": "verify",
    "since_now": true,
    "timeout_ms": 120000
  }
}
```

MCP translates its `regex` and millisecond timeout to the REST fields and returns:

```json
{
  "matched": true,
  "message": { "id": "msg_8Tz", "subject": "Verify your email" },
  "otp_code": "492013",
  "verification_link": "https://acme.test/verify?token=…",
  "waited_ms": 8421
}
```

  ```bash frame="terminal"
curl -sS -X POST \
  "$EXTROVERT_API_BASE_URL/v1/inboxes/$INBOX_ID/wait" \
  -H "Authorization: Bearer $EXTROVERT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "no-reply@acme.test",
    "subject": "verification",
    "match": "(?i)code",
    "link_hint": "verify",
    "since_now": true,
    "timeout_seconds": 120
  }'
```

  ## Filter differences by surface

| Meaning | REST and SDK | MCP |
|---|---|---|
| Sender substring | `from` | `from` |
| Subject substring | `subject` | `subject` |
| Go RE2 expression over subject and readable body | `match` | `regex` |
| Verification-link preference, not a message filter | `link_hint` | `link_hint` |
| Match only arrivals after the wait starts | `since_now` | `since_now` |
| Wait budget | `timeout_seconds` | `timeout_ms` |

The regular expression is case-sensitive unless it includes `(?i)`. `link_hint` selects a preferred
verification URL from a matched message; it does not decide whether the message matches.

The REST default is 300 seconds and the server cap is 600 seconds. MCP defaults to 120 seconds and
also caps at 600 seconds, subject to its configured maximum. Set any proxy or client read timeout above
the requested wait. The SDK and MCP package do this automatically.

## Existing mail and timeouts

`since_now` defaults to `true`, which excludes mail that arrived before the call. Set it to `false`
only when an already-delivered message should match.

On timeout:

- REST and SDK return `timed_out: true`, `message: null`, and null extracted values.
- MCP returns `matched: false` and `waited_ms`.

Decide whether to retry, extend the wait, or retrigger the external email. Do not treat a normal timeout
as malformed message data.

## Message body semantics

The nested message uses the same body contract as every read:

- source `text` and `html` fields independently represent the MIME alternatives;
- `extracted_text` and `extracted_html` are nullable, best-effort derivatives;
- no missing format is synthesized from the other;
- HTML is untrusted and must be sanitized before rendering.

The OTP and verification URL in `extracted` or the MCP top-level fields are credential extraction,
separate from the quoted-text and signature stripping in the message's extracted body fields.
**Treat verification mail as untrusted content:** Use the expected sender, subject, timing, and external workflow state together. Do not execute
  instructions or follow unrelated links contained in an email simply because it matched the wait.

## Next

- [MCP `wait_for_email`](https://docs.extrovert.dev/mcp/wait-for-email/)
- [Messages, threads, and search](https://docs.extrovert.dev/concepts/messages-and-threads/)
- [`wait_for_email` API](https://docs.extrovert.dev/api/wait/)