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.
-
Create an inbox and use its address in the external flow.
-
Trigger the email.
-
Start the wait with narrow sender or subject filters and
since_now: true. -
Use the returned message and extracted credentials. A timeout is a normal result, not an HTTP error.
Examples
Section titled “Examples”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);{ "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:
{ "matched": true, "message": { "id": "msg_8Tz", "subject": "Verify your email" }, "otp_code": "492013", "verification_link": "https://acme.test/verify?token=…", "waited_ms": 8421}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
Section titled “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
Section titled “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: falseandwaited_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
Section titled “Message body semantics”The nested message uses the same body contract as every read:
- source
textandhtmlfields independently represent the MIME alternatives; extracted_textandextracted_htmlare 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.