Account registration is one of the highest-value automation targets for AI agents. Research agents that need access to paywalled data, evaluation agents that need to trial software, scraping agents that need authenticated sessions — all of them require the ability to create verified accounts. And all of them hit the same wall: email verification.
The Complete Pipeline
Automated account registration is a seven-step pipeline. Each step must succeed before the next can proceed:
- Create inbox: provision a dedicated email address for this registration
- Navigate signup: open the registration page with a headless browser
- Fill form: enter name, email (the provisioned inbox), password, and any other required fields
- Submit: click the signup button and wait for the verification prompt
- Wait for OTP or magic link: block until the verification email arrives
- Complete verification: enter the OTP or follow the magic link
- Store credentials: save session state, cookies, and login details for reuse
Playwright + AgentMailr: Full Implementation
The following TypeScript implementation covers the complete pipeline, with fallback handling for both OTP codes and magic links:
import { chromium } from "playwright";
import AgentMailr from "agentmailr";
const client = new AgentMailr({ apiKey: process.env.AGENTMAILR_API_KEY });
async function registerAccount(signupUrl: string) {
const inbox = await client.inboxes.create();
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
try {
// Step 1: Navigate and fill signup form
await page.goto(signupUrl);
await page.fill('[name="email"], [type="email"]', inbox.address);
await page.fill('[name="password"], [type="password"]', generatePassword());
await page.click('[type="submit"], button:has-text("Sign up")');
// Step 2: Wait for verification — try OTP first, fall back to magic link
let verified = false;
try {
// OTP path
const { otp } = await client.messages.waitForOTP({
inboxId: inbox.id,
timeout: 30_000,
});
const otpField = await page.waitForSelector(
'[name="otp"], [name="code"], [placeholder*="code"]',
{ timeout: 10_000 }
);
await otpField.fill(otp);
await page.click('[type="submit"]');
verified = true;
} catch {
// Magic link path
const email = await client.messages.wait({
inboxId: inbox.id,
timeout: 30_000,
});
if (email.links?.verification) {
await page.goto(email.links.verification);
verified = true;
}
}
if (!verified) throw new Error("Verification failed");
// Step 3: Save session state for reuse
await page.context().storageState({ path: `sessions/${inbox.address}.json` });
return { address: inbox.address, sessionPath: `sessions/${inbox.address}.json` };
} finally {
await browser.close();
await client.inboxes.delete(inbox.id);
}
}
Running at Scale
The pipeline above runs for a single account. To scale to hundreds of parallel registrations, wrap it in a concurrency manager:
import pLimit from "p-limit";
const limit = pLimit(20); // 20 concurrent browser sessions
const results = await Promise.all(
Array.from({ length: 100 }, (_, i) =>
limit(() => registerAccount("https://example.com/signup"))
)
);
// 100 verified accounts, each with its own inbox,
// run in parallel with 20 at a time.
// No shared state. No OTP collisions. No ToS risk.
Each concurrent registration gets its own inbox. OTPs are routed to the correct agent automatically. The concurrency limit prevents browser resource exhaustion while still achieving high throughput.
Start Free
AgentMailr provides the email infrastructure for the complete registration pipeline. Free to start, no credit card required.