Almost every piece of email-waiting code written for AI agents looks the same: a loop that checks for new messages, sleeps for a few seconds, and repeats. It works. It is also inefficient, slow, fragile, and completely unnecessary. There is a better pattern, and it is already built for you.

The Polling Problem

Here is what the typical polling loop looks like:

const startTime = Date.now();
const timeout = 150_000; // 2.5 minutes
const interval = 5_000;  // check every 5 seconds
let otp: string | null = null;

while (!otp && Date.now() - startTime < timeout) {
  await sleep(interval);  // 30 wasted requests if OTP takes 2.5 min

  const messages = await fetchMessages(inboxId, { after: startTime });
  for (const msg of messages) {
    const match = msg.body.match(/d{6}/);
    if (match) {
      otp = match[0];
      break;
    }
  }
}

if (!otp) throw new Error("OTP timeout"); // no useful error context

The problems with this approach accumulate quickly:

  • Up to 5 seconds of latency: if the OTP arrives right after a poll, you wait nearly the full interval before proceeding
  • 30 wasted HTTP requests: over a 2.5 minute window with 5-second polls, most of those calls return nothing
  • OTP regex is your problem: you have to write and maintain the extraction logic yourself
  • Complex retry logic: what happens if the fetch fails mid-loop? Error handling gets complicated fast
  • Timeout errors lack context: when it times out, you get no information about why the email never arrived

How Long-Poll Works

Long-polling inverts the model. Instead of your code asking "is there an email yet?" on a timer, you make a single HTTP request to the server and leave the connection open. The server holds the connection until an email arrives, then immediately responds with the result. From your code's perspective, it is just an await.

If the email arrives 300 milliseconds after you make the request, the await resolves in 300 milliseconds. If it arrives 25 seconds later, you wait exactly 25 seconds. No polling. No sleep timers. No wasted requests. One HTTP call, one response.

// Before: polling loop (30+ requests, up to 5s latency, OTP regex DIY)
let otp = null;
while (!otp) {
  await sleep(5000);
  const msgs = await fetchMessages(inboxId);
  otp = msgs.flatMap(m => m.body.match(/d{6}/) ?? []).at(0);
}

// After: single await (instant resolution, OTP extracted for you)
const { otp } = await agentmailr.messages.waitForOTP({
  inboxId: inbox.id,
  timeout: 30_000,
});

What You Get Back

The AgentMailr long-poll endpoints return structured, pre-processed responses:

  • /otp — returns { otp: "847291" }. Just the extracted code. Ready to type into a form field.
  • /wait — returns the full parsed email object: subject, from, to, text body, HTML body, extracted links, detected OTP, and timestamps.
  • Both endpoints resolve instantly when the email arrives — no artificial latency.
  • Both endpoints return a clean timeout error if no email arrives within the specified window.

Start Free

Replace your polling loops with a single await. Free to start, no credit card required.