Disposable Email for Signup Automation: The Complete Developer Guide (2026)

Every app that sends a verification email presents the same challenge for developers, QA engineers, and automation teams: how do you automate the part where a human is supposed to read an email and type a code?

This is the complete guide to solving that problem in 2026. We cover every angle — scripted automation, test framework integration, AI agent workflows, CI/CD pipelines, and no-code options — using FreeCustom.Email, the only disposable email provider with an official CLI, official SDKs, WebSocket real-time delivery, and a dedicated OTP extraction endpoint.


What Signup Automation Actually Needs

Before picking a tool, understand exactly what a signup automation requires from a disposable email service:

Requirement

Why it matters

Programmatic inbox creation

Can't rely on a fixed address; parallel tests need isolated inboxes

Custom inbox address

Some apps validate the format or domain; random addresses must be controllable

Real-time or fast delivery

Tight CI timeouts; no minutes of polling

OTP extraction

No regex maintenance; works across different email formats

Parallel isolation

Multiple tests must not share inboxes or receive each other's codes

Cleanup

Long-running CI must not accumulate thousands of stale inboxes

CI-compatible auth

No browser popup in a headless runner

Rate limits that match scale

1 req/sec won't cut it for 50 parallel jobs

FreeCustom.Email is the only disposable email service that satisfies every item on this list.


The Core Three-Step Pattern

All signup automation — regardless of language, framework, or tool — follows one pattern:

1. Create fresh inbox          → get an address
2. Submit address in signup    → app sends verification email
3. Extract OTP / link          → complete verification

The difference between approaches is how smoothly each step executes.


Method 1: fce CLI (Fastest for Scripts and Local Dev)

The fce CLI is the simplest entry point. Install it once and every step is one command:

bash

# Install
curl -fsSL freecustom.email/install.sh | sh

# Authenticate (once, saves to keychain)
fce login

# Full signup automation in 4 lines:
INBOX=$(fce inbox add random)
curl -s -X POST https://myapp.com/signup -d "email=$INBOX&password=Test1234!"
OTP=$(fce otp "$INBOX" | grep "OTP   ·" | awk '{print $NF}')
curl -s -X POST https://myapp.com/verify -d "email=$INBOX&otp=$OTP"

OTP output from fce otp:

────────────────────────────────────────────────
  OTP
────────────────────────────────────────────────

  OTP   ·  212342
  From  ·  noreply@myapp.com
  Subj  ·  Your verification code
  Time  ·  20:19:54

No regex. No email parsing. No second browser tab. For more on the CLI: freecustom.email/api/cli — full install options including Homebrew, Scoop, Chocolatey, npm, and Go at github.com/DishIs/fce-cli.

fce dev — the one-command shortcut

For local development, fce dev creates an inbox and starts watching it simultaneously:

bash

fce dev
# → dev-fy8x@ditcloud.info created
# → WebSocket open, emails appear in real time
# → First OTP highlighted automatically

This is the fastest way to test a signup flow manually while building your app. See How to Receive OTP Without Using Your Real Email (2026) for more on this use case.


Method 2: JavaScript / TypeScript SDK

For Node.js applications and test suites, the official SDK gives clean async/await OTP extraction:

bash

npm install freecustom-email

Automated signup flow

typescript

import { FreecustomEmailClient } from 'freecustom-email';
import fetch from 'node-fetch';

const fce = new FreecustomEmailClient({ apiKey: process.env.FCE_API_KEY! });
const APP = process.env.APP_URL!;

async function automateSignup(): Promise<{ email: string; otp: string }> {
  // Step 1: Fresh inbox
  const email = `auto-${Date.now()}@ditmail.info`;
  await fce.inboxes.register(email);
  console.log(`Created inbox: ${email}`);

  // Step 2: Submit signup
  const signupRes = await fetch(`${APP}/api/signup`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email, password: 'AutoTest123!' }),
  });
  if (!signupRes.ok) throw new Error(`Signup failed: ${signupRes.status}`);
  console.log('Signup submitted');

  // Step 3: OTP — no polling, no regex, built-in wait
  const otp = await fce.otp.waitFor(email, { timeout: 30_000 });
  console.log(`OTP: ${otp}`);

  // Step 4: Complete verification
  const verifyRes = await fetch(`${APP}/api/verify`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email, otp }),
  });
  if (!verifyRes.ok) throw new Error(`Verify failed: ${verifyRes.status}`);
  console.log('Verified!');

  await fce.inboxes.unregister(email);
  return { email, otp };
}

automateSignup().then(console.log);

Parallel signup automation

typescript

async function runParallel(count: number) {
  const tasks = Array.from({ length: count }, (_, i) =>
    automateSignup().catch(err => ({ error: err.message, i }))
  );
  const results = await Promise.all(tasks);
  const passed = results.filter(r => !('error' in r)).length;
  console.log(`${passed}/${count} passed`);
  return results;
}

runParallel(10);  // 10 parallel signup flows

Because each signup creates its own inbox, 10 parallel runs have zero interference.


Method 3: Python SDK

bash

pip install freecustom-email httpx

python

import asyncio
import os
import httpx
from freecustom_email import FreeCustomEmail

fce = FreeCustomEmail(api_key=os.environ["FCE_API_KEY"])
APP = os.environ["APP_URL"]

async def automate_signup() -> dict:
    # Fresh inbox
    email = f"auto-{int(asyncio.get_event_loop().time())}@ditmail.info"
    await fce.inboxes.register(email)
    print(f"Inbox: {email}")

    async with httpx.AsyncClient() as client:
        # Submit signup
        r = await client.post(f"{APP}/api/signup",
                              json={"email": email, "password": "AutoTest123!"})
        r.raise_for_status()

        # Extract OTP — SDK handles waiting
        otp = await fce.otp.wait_for(email, timeout=30)
        print(f"OTP: {otp}")

        # Complete verification
        r2 = await client.post(f"{APP}/api/verify",
                               json={"email": email, "otp": otp})
        r2.raise_for_status()

    await fce.inboxes.unregister(email)
    return {"email": email, "otp": otp}

# Run parallel
async def main():
    results = await asyncio.gather(*[automate_signup() for _ in range(5)])
    print(results)

asyncio.run(main())

Method 4: Playwright Integration

Playwright is the best choice for browser-based signup automation because it interacts with the actual UI, not just the API. The FCE fixture pattern ensures each test worker gets an isolated inbox:

typescript

// playwright.config.ts — 4 parallel workers
export default defineConfig({ workers: 4, testDir: './tests' });

// tests/signup.spec.ts
import { test, expect } from '@playwright/test';
import { FreecustomEmailClient } from 'freecustom-email';

const fce = new FreecustomEmailClient({ apiKey: process.env.FCE_API_KEY! });

test.beforeEach(async ({}, testInfo) => {
  testInfo['inbox'] = `pw-${testInfo.workerIndex}-${Date.now()}@ditmail.info`;
  await fce.inboxes.register(testInfo['inbox']);
});

test.afterEach(async ({}, testInfo) => {
  await fce.inboxes.unregister(testInfo['inbox']).catch(() => {});
});

test('signup flow', async ({ page }, testInfo) => {
  const inbox = testInfo['inbox'];
  await page.goto('/signup');
  await page.fill('#email', inbox);
  await page.fill('#password', 'Test123!');
  await page.click('button[type="submit"]');
  await page.waitForSelector('#otp-input');

  const otp = await fce.otp.waitFor(inbox, { timeout: 30_000 });
  await page.fill('#otp-input', otp);
  await page.click('button[type="submit"]');
  await expect(page).toHaveURL(/dashboard/);
});

Full Playwright guide: OTP API for Playwright: Automate Email Verification Without Regex (2026)


Method 5: Selenium Integration

python

# Python Selenium
from selenium import webdriver
from selenium.webdriver.common.by import By
from freecustom_email import FreeCustomEmail
import asyncio, os

fce = FreeCustomEmail(api_key=os.environ["FCE_API_KEY"])
opts = webdriver.ChromeOptions()
opts.add_argument("--headless")
driver = webdriver.Chrome(options=opts)

inbox = f"sel-{int(asyncio.get_event_loop().time())}@ditmail.info"
asyncio.run(fce.inboxes.register(inbox))

driver.get("https://myapp.com/signup")
driver.find_element(By.ID, "email").send_keys(inbox)
driver.find_element(By.ID, "password").send_keys("Test123!")
driver.find_element(By.CSS_SELECTOR, "button[type='submit']").click()

otp = asyncio.run(fce.otp.wait_for(inbox, timeout=30))
driver.find_element(By.ID, "otp-input").send_keys(otp)
driver.find_element(By.CSS_SELECTOR, "button[type='submit']").click()
driver.quit()
asyncio.run(fce.inboxes.unregister(inbox))

Full Selenium guide: Temp Mail API for Selenium: Automate Email Verification (2026)


Method 6: OpenClaw AI Agent (Zero Code)

With OpenClaw or any AI agent that can run shell commands, describe the task in plain English:

"Automate a complete signup flow for https://myapp.com: create a temp inbox, fill the signup form, extract the OTP, and complete verification. Use the fce CLI (already logged in)."

The agent runs:

fce inbox add random → dev-fy8x@ditcloud.info
[curl signup endpoint]
fce otp dev-fy8x@ditcloud.info → 212342
[curl verify endpoint]
fce inbox remove dev-fy8x@ditcloud.info

Full automation guide: AI Agent Email Automation with OpenClaw and n8n (2026)


Method 7: n8n Visual Workflow

For teams that prefer a visual automation canvas without code:

[Trigger]
→ [Execute Command: fce inbox add random]
→ [HTTP Request: POST /api/signup { email: {{stdout}} }]
→ [Execute Command: fce otp {{inbox}}]
→ [Code: parse OTP from stdout]
→ [HTTP Request: POST /api/verify { otp: {{otp}} }]
→ [Slack: "Signup automation passed"]

See freecustom.email/api/automation/n8n for the full node configuration guide.


CI/CD: GitHub Actions (Complete)

yaml

# .github/workflows/signup-automation.yml
name: Signup Automation E2E

on:
  push:
    branches: [main]
  schedule:
    - cron: '0 */6 * * *'   # Every 6 hours

jobs:
  signup-test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        shard: [1, 2, 3, 4]   # 4 parallel jobs, each with isolated inboxes

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }

      - name: Install fce CLI
        run: curl -fsSL freecustom.email/install.sh | sh

      - name: Install deps
        run: npm ci

      - name: Run signup automation (shard ${{ matrix.shard }})
        env:
          FCE_API_KEY: ${{ secrets.FCE_API_KEY }}
          APP_URL: ${{ vars.STAGING_URL }}
          SHARD: ${{ matrix.shard }}
        run: node scripts/signup-automation.js

      - name: Cleanup on failure
        if: failure()
        env:
          FCE_API_KEY: ${{ secrets.FCE_API_KEY }}
        run: |
          # List and remove any inboxes created by this shard
          fce inbox list | grep "auto-" | while read addr; do
            fce inbox remove "$addr" || true
          done

Handling Different Email Types

Not all verification emails contain a numeric OTP. Here is how to handle each:

Numeric OTP (most common)

typescript

const otp = await fce.otp.waitFor(inbox);
// → "212342"
await page.fill('#otp-input', otp);

typescript

const messages = await fce.messages.list(inbox);
const msg = await fce.messages.get(inbox, messages[0].id);
const link = msg.verification_link;
await page.goto(link);

Alphanumeric token

typescript

const messages = await fce.messages.list(inbox);
const msg = await fce.messages.get(inbox, messages[0].id);
// msg.body contains the full email text/html
const token = msg.body.match(/token=([A-Za-z0-9]+)/)?.[1];

Avoiding Disposable Domain Blockers

Some apps validate the email domain at signup and reject known disposable domains. Solutions:

Option 1: Custom domain (Growth plan)
Register and verify your own domain in the FCE dashboard. Then create inboxes like test-001@automation.yourcompany.com.

Option 2: Use FCE's less-known domains
FCE provides multiple domains. Run fce domains to list all available on your plan — some are less likely to appear on blocklists.

Option 3: Use the Developer plan with custom domains for test teams
See Private or Custom Domains for the full setup guide.

Related: Getting Around 'Work Email' Requirements Ethically (2026)


Complete Approach Comparison

Approach

Setup

OTP extraction

Parallel safe

Browser needed

CI-ready

fce CLI

1 command

Auto

JS/TS SDK

npm install

Auto

Python SDK

pip install

Auto

Playwright + SDK

Fixture setup

Auto

Selenium + SDK

Fixture setup

Auto

OpenClaw AI agent

fce login

Auto

Partial

n8n workflow

n8n + fce

Auto

REST API direct

HTTP client

Manual parse

Mailinator API

API key

Manual regex

Partial

Gmail IMAP

OAuth setup

Manual parse


Frequently Asked Questions

How many inboxes can I create per minute? Depends on your plan. Free: 1/sec. Developer: 10/sec. Startup: 25/sec. Growth: 50/sec. Enterprise: 100/sec. For parallel test suites, Growth covers most needs.

What happens if my app is slow to send the email? The FCE SDK's waitFor() and the CLI's fce otp both poll with backoff until the email arrives, up to a configurable timeout. Set the timeout to match your app's SLA — usually 30–60 seconds is safe.

Can I use FCE in a Docker container without fce login? Yes — set FCE_API_KEY as an environment variable. The CLI and SDK both detect it automatically and skip interactive login. Get the key from the dashboard.

Does the same inbox work for multiple test steps (signup + profile update + password reset)? Yes. The inbox persists until you call fce inbox remove or fce.inboxes.unregister(). Each call to fce otp or fce.otp.waitFor() returns the most recent OTP, so multi-step flows work naturally.

Can I test with a custom subdomain like qa-bot@test.mycompany.com? Yes, on Growth plan and above with a custom domain registered in the dashboard.

Is there a webhook option instead of polling? Webhooks are on the API roadmap. Today, real-time delivery is via WebSocket (fce watch / client.realtime) on Startup plan+.

Is the free tier suitable for automation testing? For local development and small test suites, yes. For CI pipelines with many parallel jobs, Startup ($19/mo) or Growth ($49/mo) is more appropriate.


Written by

D

Dishant Singh

A full stack developer with good knowledge of email server, SEO, proxies, and networking, have more than 3 years of experience in building webapps for the netizens. Developing open source, fast, and free SaaS for all.

FAQ

Frequently Asked Questions

How many inboxes can I create per minute?+

Depends on your plan. Free: 1/sec. Developer: 10/sec. Startup: 25/sec. Growth: 50/sec. Enterprise: 100/sec. For parallel test suites, Growth covers most needs.

What happens if my app is slow to send the email?+

The FCE SDK's waitFor() and the CLI's fce otp both poll with backoff until the email arrives, up to a configurable timeout. Set the timeout to match your app's SLA — usually 30–60 seconds is safe.

Does the same inbox work for multiple test steps (signup + profile update + password reset)?+

Yes. The inbox persists until you call fce inbox remove or fce.inboxes.unregister(). Each call to fce otp or fce.otp.waitFor() returns the most recent OTP, so multi-step flows work naturally.

Is there a webhook option instead of polling?+

Webhooks are on the API roadmap. Today, real-time delivery is via WebSocket (fce watch / client.realtime) on Startup plan+.

Is the free tier suitable for automation testing?+

For local development and small test suites, yes. For CI pipelines with many parallel jobs, Startup ($19/mo) or Growth ($49/mo) is more appropriate.

Discussion0

No comments yet. Be the first to share your thoughts.