← Back to Blog

Email OTP Testing Tools for CI: A Practical Roundup

Practical roundup of approaches for email OTP and magic-link testing in CI — local SMTP, email-test APIs, and OTP-first disposable inboxes — and when each fits.

testingci-cdotproundup
Email OTP Testing Tools for CI: A Practical Roundup

CI suites that exercise signup, password reset, or magic-link login all hit the same problem: the test needs a real inbox without sharing Gmail, racing parallel jobs, or inventing sleeps. The tools below solve that in different ways. This is a roundup of approaches, not a scored affiliate list — pick the fit for your stack, not a universal winner.

No invented market share or pricing. Features are described from public docs and from OTPBox's live API.

What every CI OTP setup needs

Regardless of vendor, reliable email verification tests share four requirements:

  1. Isolation — a unique address (or inbox) per test run so parallel jobs cannot steal each other's codes
  2. Async wait — poll or wait with a deadline, not a fixed sleep 10
  3. Extraction — get a 4–8 digit code or magic link without brittle HTML scraping
  4. Debuggability — log the address so a failed job can be correlated with send logs

For the failure modes when those are missing, see 7 Mistakes That Make Email OTP Tests Flake in CI and the CI Email OTP Checklist.

1. Local SMTP catchers (Mailpit; MailHog as legacy)

What it is: Your app (or a test SMTP relay) delivers mail to a local catcher instead of the public internet. You read messages from a web UI or REST API on the same machine or Docker network.

Mailpit is the maintained default for new work. It is a small SMTP testing tool with a modern UI and a REST API, originally inspired by MailHog. Defaults are SMTP on port 1025 and the UI on 8025. See the Mailpit project and its API docs.

MailHog still appears in older Compose files and tutorials. It is largely unmaintained; Mailpit's maintainers explicitly position Mailpit as the successor. SMTP ports often match, but the HTTP APIs are not drop-in compatible — plan a small adapter if you migrate.

Best when:

  • Local development and fast feedback loops
  • Integration tests where you control the SMTP host in config
  • CI jobs that already run Docker Compose and can start Mailpit as a service

Weak when:

  • You need to prove real-world deliverability (SPF/DKIM, provider throttling)
  • Parallel SaaS CI runners should not each own a long-lived SMTP sidecar
  • You want built-in OTP/link extraction without parsing message bodies yourself

Pattern: point SMTP_HOST=mailpit (or similar) in test env → trigger the flow → GET /api/v1/messages → parse the latest body for a code. Fine for controlled environments; not a substitute for an end-to-end path through your production mail provider.

2. Dedicated email testing APIs

What it is: A hosted mailbox (often a private subdomain) plus an API your Playwright/Cypress job polls. Messages are real SMTP deliveries into that test domain.

Public walkthroughs from Mailinator's Playwright docs and Mailosaur's Playwright email guide describe the same shape:

  1. Mint a unique address for the run
  2. Trigger signup / OTP / password reset in the browser
  3. Wait or poll until the message arrives
  4. Read OTP codes or links from structured fields when the vendor provides them

Mailosaur's client libraries, for example, expose helpers that wait for a message and surface codes under fields such as html.codes / text.codes (see their API overview). Mailinator documents private domains for automation and warns against public shared inboxes for auth flows.

Best when:

  • Mature QA suites that assert headers, HTML, links, and sometimes SMS
  • Teams already standardized on a commercial email-test vendor
  • You need rich wait helpers and SDK support across languages

Tradeoffs to plan for:

  • API tokens or server IDs become CI secrets
  • Paid plans are common for private domains and higher volume (check the vendor's current pricing yourself — we do not quote numbers here)
  • Overkill if you only need "give me the six-digit code" for a thin E2E path

3. OTP-first disposable email (OTPBox)

What it is: Ephemeral inboxes aimed at verification codes and magic links, with a tiny REST surface for CI. OTPBox creates addresses on *@box.otpbox.app (product OTP mail — not the apex domain), extracts codes and common verification URLs, and expires inboxes after one hour.

From the live docs:

Endpoint Purpose Rate notes
POST /api/inbox Create inbox → email, token, expiresAt 10 creates/hour per IP
GET /api/inbox/:token Inbox + messages (newest first) Poll freely
GET /api/inbox/:token/latest Latest otp / link (or 404 if empty) Poll freely

Create + poll example:

RESPONSE=$(curl -s -X POST https://otpbox.app/api/inbox)
TOKEN=$(echo "$RESPONSE" | jq -r '.token')
EMAIL=$(echo "$RESPONSE" | jq -r '.email')

# Trigger your app to send to $EMAIL, then:
for i in $(seq 1 10); do
  RESPONSE=$(curl -s "https://otpbox.app/api/inbox/${TOKEN}/latest")
  OTP=$(echo "$RESPONSE" | jq -r '.otp // empty')
  if [ -n "$OTP" ] && [ "$OTP" != "null" ]; then
    echo "OTP: $OTP"
    break
  fi
  sleep 3
done

Playwright-shaped poll:

async function waitForOtp(token, { attempts = 10, delayMs = 3000 } = {}) {
  for (let i = 0; i < attempts; i++) {
    const res = await fetch(`https://otpbox.app/api/inbox/${token}/latest`);
    if (res.ok) {
      const data = await res.json();
      if (data.otp || data.link) return data;
    }
    await new Promise((r) => setTimeout(r, delayMs));
  }
  throw new Error("Timeout waiting for OTP or magic link");
}

Create the inbox once per test, then poll. Creating inside the loop burns the create quota; polling does not. Details and GitHub Actions wiring live in How to Test Email OTP Verification in CI and Disposable Email for CI OTP Testing.

Best when:

  • CI E2E that only needs codes or magic links
  • Parallel jobs that each need an isolated address without managing SMTP
  • Free, rate-limited API access with no SDK required

Weak when:

  • You need deep MIME inspection, spam scoring, or SMS in the same tool
  • Local-only workflows where spinning Mailpit is simpler than hitting the network

Try a manual inbox on the home page, then automate with the API docs.

4. Public temp-mail and shared inboxes

Public temporary-mail sites and shared team Gmail accounts look convenient and fail in CI for predictable reasons: no per-test isolation, races on "latest message," and no safe place for authentication secrets.

Use them for casual privacy experiments if you must — not for signup or 2FA automation. Longer comparison: Disposable Email vs Temp Mail for Verification Codes. For magic links specifically, see Magic Link Testing Without Polluting Real Inboxes.

Decision matrix (qualitative)

Need Local Mailpit Email-test API OTPBox Public temp-mail
Per-test isolation Yes, if you name/filter well Yes (unique address) Yes (new inbox) No
Built-in OTP/link extract Usually DIY Often yes Yes (otp / link) No
Secrets in CI Usually none API key / server id None for free create N/A
Parallel CI jobs Needs a service per job or shared with care Strong Strong Poor
Real public SMTP path No (captured locally) Yes Yes Yes
Best when Local / Compose Rich QA assertions Thin CI OTP/E2E Almost never for auth

How to choose in one sentence

  • Developing on a laptop → Mailpit (or equivalent local SMTP).
  • Enterprise QA with deep email assertions → a dedicated email-testing API.
  • CI that just needs the code or magic link → OTP-first disposable mail such as OTPBox.
  • Shared public temp-mail → skip for authentication tests.

Related reading

Resources


Email OTP in CI is solved when isolation, polling, and extraction are deliberate. Match the tool to the job: local catcher for offline speed, a full email-test platform when you need depth, and an OTP-first disposable inbox when CI only needs the code.