Agent Self-Signup
Let your AI agent sign up for any service automatically using browser automation and mails-agent.
Many services require email verification during signup. By combining browser automation (Playwright or Puppeteer) with mails0's verification code extraction, your agent can complete the entire signup flow without human intervention.
The Flow
Run mails bootstrap to create a random 72-hour receive-only address without browser approval or exposing its key.
Use Playwright or Puppeteer to open the target service's registration page.
Enter the claimed mailbox address into the email field.
Click the signup button and wait for the verification email to arrive.
Use mails code --timeout 60 to wait for and extract the code automatically.
Fill the verification code into the confirmation page. Account created.
Full Example: Playwright + mails-agent CLI
import json
import subprocess
from pathlib import Path
from playwright.sync_api import sync_playwright
subprocess.run(["mails", "bootstrap"], check=True)
config = json.loads((Path.home() / ".mails" / "config.json").read_text())
MAILBOX = config["mailbox"]
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
# Step 1: Navigate to the signup page
page.goto("https://example.com/signup")
# Step 2: Fill in the signup form
page.fill('input[name="email"]', MAILBOX)
page.fill('input[name="password"]', "SecureP@ss123")
page.fill('input[name="name"]', "AI Agent")
# Step 3: Submit the form
page.click('button[type="submit"]')
page.wait_for_url("**/verify**")
# Step 4: Wait for the verification code (blocks up to 60s)
result = subprocess.run(
["mails", "code", "--to", MAILBOX, "--timeout", "60"],
capture_output=True, text=True
)
code = result.stdout.strip()
print(f"Got verification code: {code}")
# Step 5: Enter the code
page.fill('input[name="code"]', code)
page.click('button[type="submit"]')
# Step 6: Verify success
page.wait_for_url("**/dashboard**")
print("Signup complete!")
browser.close()
Full Example: Playwright + Python SDK
import json
import subprocess
from pathlib import Path
from playwright.sync_api import sync_playwright
from mails_agent import MailsClient
subprocess.run(["mails", "bootstrap"], check=True)
config = json.loads((Path.home() / ".mails" / "config.json").read_text())
MAILBOX = config["mailbox"]
client = MailsClient(api_key=config["api_key"])
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
# Navigate and fill signup form
page.goto("https://example.com/signup")
page.fill('input[name="email"]', MAILBOX)
page.fill('input[name="password"]', "SecureP@ss123")
page.click('button[type="submit"]')
page.wait_for_url("**/verify**")
# Wait for verification code via SDK
code_response = client.wait_for_code(to=MAILBOX, timeout=60)
code = code_response.code
print(f"Got code: {code}")
# Enter code and complete signup
page.fill('input[name="code"]', code)
page.click('button[type="submit"]')
page.wait_for_url("**/dashboard**")
print("Signup complete!")
browser.close()
Full Example: Puppeteer + curl
const fs = require("fs");
const os = require("os");
const path = require("path");
const puppeteer = require("puppeteer");
const { execFileSync } = require("child_process");
const API = "https://api.mails0.com";
execFileSync("mails", ["bootstrap"], { stdio: "inherit" });
const config = JSON.parse(
fs.readFileSync(path.join(os.homedir(), ".mails", "config.json"), "utf8")
);
const TOKEN = config.api_key;
const MAILBOX = config.mailbox;
(async () => {
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
// Navigate and fill form
await page.goto("https://example.com/signup");
await page.type('input[name="email"]', MAILBOX);
await page.type('input[name="password"]', "SecureP@ss123");
await page.click('button[type="submit"]');
await page.waitForNavigation();
// Wait for verification code via API
const resp = await fetch(
`${API}/v1/code?to=${MAILBOX}&timeout=60`,
{ headers: { Authorization: `Bearer ${TOKEN}` } }
);
const { code } = await resp.json();
console.log(`Got code: ${code}`);
// Enter code
await page.type('input[name="code"]', code);
await page.click('button[type="submit"]');
await page.waitForNavigation();
console.log("Signup complete!");
await browser.close();
})();
Using the CLI only (no browser automation)
If the signup flow is API-based (no browser needed), you can do everything with curl and the mails CLI:
# 1. Create or reuse a safe temporary mailbox
mails bootstrap
# Set this to the mailbox shown by the command
MAILBOX="[email protected]"
# 2. Register via API
curl -X POST https://example.com/api/register \
-H "Content-Type: application/json" \
-d "{\"email\": \"$MAILBOX\", \"password\": \"SecureP@ss123\"}"
# 3. Wait for verification code
CODE=$(mails code --to "$MAILBOX" --timeout 60)
echo "Verification code: $CODE"
# 4. Verify the account
curl -X POST https://example.com/api/verify \
-H "Content-Type: application/json" \
-d "{\"email\": \"$MAILBOX\", \"code\": \"$CODE\"}"
How code extraction works
The /v1/code endpoint and mails code CLI command use pattern matching to extract verification codes from incoming emails. They support:
- 4-8 digit numeric codes (e.g.,
123456) - 4-8 character alphanumeric codes (e.g.,
A1B2C3) - Codes in English, Chinese, Japanese, and Korean emails
The --timeout flag makes the request block until a matching email arrives (long-polling), so you do not need to implement retry logic yourself.
Headless Agent Self-Registration
Use the official CLI or MCP server for headless mailbox creation. They keep a persistent idempotency key, store the API key locally with owner-only permissions, and reuse an active mailbox instead of creating duplicates.
# CLI
npm install -g mails-agent
mails bootstrap
# MCP: start without credentials, then call
create_temporary_mailbox {}
Do not automate /v1/claim/confirm. Permanent named mailboxes require human approval through mails claim <name>. Temporary mailboxes are receive-only, expire after 72 hours, and cannot create additional mailboxes.
Tips for reliability
- Reuse the active temporary mailbox. Bootstrap is idempotent and rate limited. Use the
--sincetimestamp to avoid matching an older code instead of creating one mailbox per signup. - Set an appropriate timeout. Most verification emails arrive within 10-30 seconds, but some services are slower. Use
--timeout 60as a safe default. - Handle verification links separately. Some services send a link instead of a code. The code extractor only extracts 4-8 character codes, not URLs. For link-based verification, fetch the email via
GET /v1/emailand parse the link from the body yourself. - Add delays between form actions. Some sites have rate limits or anti-bot checks. Add small waits (
page.wait_for_timeout(1000)) between interactions. - Use headless=False for debugging. When developing your flow, run the browser in headed mode to see what is happening.
- Retry the full flow, not individual steps. If the code times out, it is usually better to start the entire signup process over with a new mailbox rather than waiting longer.
- Respect anti-abuse controls. Follow each service's terms, stop when a CAPTCHA or human verification is required, and do not use mailbox automation to create accounts at scale.