Signup and password-reset flows depend on email, which makes them awkward to test end-to-end. Most teams either mock the email step entirely (so a broken email template or a misconfigured SMTP relay never shows up in CI) or wire up a real mailbox and fight with IMAP polling and leftover test messages.

A temp mail API gives every test run a brand-new, disposable inbox. The test waits for the real confirmation email to arrive, reads it like any other HTTP response, and throws the inbox away when it's done — no mocking, no shared mailbox, no cleanup script.

What you need

  • An AxioAPI key, stored as the AXIOAPI_KEY environment variable. Never commit it to source control — read it from the environment or your CI secret store.
  • Playwright Test (the same request fixture used for UI assertions also makes the API calls — no extra HTTP client needed).
  • A staging environment that actually sends email.

The test

The flow is three calls: create an inbox, submit it in the signup form, poll for the confirmation email.

// signup.spec.js import { test, expect } from '@playwright/test'; const API = 'https://axioapi.com/api/v1'; const headers = { Authorization: `Bearer ${process.env.AXIOAPI_KEY}`, Accept: 'application/json' }; async function waitForMail(request, inbox, timeoutMs = 60000) { const deadline = Date.now() + timeoutMs; while (Date.now() < deadline) { const res = await request.get(`${API}/temp-mail/inboxes/${inbox}/messages`, { headers }); const { items } = (await res.json()).data; if (items.length > 0) return items[0]; await new Promise((resolve) => setTimeout(resolve, 3000)); } throw new Error('No email arrived in time'); } test('signup sends a confirmation email', async ({ page, request }) => { const created = await request.post(`${API}/temp-mail/inboxes`, { headers }); const { id, address } = (await created.json()).data; try { await page.goto('https://staging.example.com/signup'); await page.fill('[name=email]', address); await page.click('button[type=submit]'); const message = await waitForMail(request, id); expect(message.subject).toContain('Confirm'); } finally { await request.delete(`${API}/temp-mail/inboxes/${id}`, { headers }); } }); 

How it works

  • The create call returns data.id and data.address — use the address as the test's throwaway email, keep the id to read and delete the inbox later.
  • The list call returns data.items, a summary of each message. To read a full body (for an HTML confirmation link, for example), call GET /api/v1/temp-mail/inboxes/{inbox}/messages/{message}.
  • DELETE removes the inbox and is free, so it belongs in a finally block — the test cleans up even when the assertion fails.

Keep it reliable and cheap

Every poll is billed, so wait a few seconds between polls and set a timeout instead of looping forever — check the current prices on the Temp Mail API page before you wire this into a large suite. Use a fresh inbox per test so tests never read each other's mail, and rotate the address if your app rejects one it has already seen. Disposable inboxes are for testing only — never use them for an account you need to keep.

If you want to try it without writing any code first, there's a free email validator tool backed by the same API.