BUILD. TEST. VERIFY.

Test signup emails with Playwright without an email account

This example checks a signup verification flow in an application you own. Adapt the labels, expected sender and allowed link host to your test environment. InboxIt receives the application's email; Playwright drives your application.

1. Prepare your test application

Your test environment must send real emails and accept temporary addresses. Configure a baseURL in Playwright and run the example with Node.js 20+. Do not run account creation against unrelated websites.

2. Create and wait for an active inbox

const base = 'https://inboxit.wtf/api/v2/public/inboxes';
const response = await fetch(base, {
  method: 'POST',
  headers: {'Idempotency-Key': crypto.randomUUID()}
});
if (!response.ok) throw new Error('Create failed: ' + response.status);
const {token, inbox} = await response.json();
// Keep token private. Use inbox.address in your test application's signup.
const headers = {Authorization: 'Bearer ' + token};
for (let attempt = 0; attempt < 30; attempt++) {
  const r = await fetch(base, {headers});
  if (!r.ok) throw new Error('Inbox status failed: ' + r.status);
  const state = await r.json();
  if (state.inbox.status === 'active') break;
  if (attempt === 29) throw new Error('Inbox did not become active');
  await new Promise(resolve => setTimeout(resolve, 2000));
}

3. Submit signup and verify the email

// Inside your Playwright test, after the setup above:
try {
  await page.goto('/signup');
  await page.getByLabel('Email', {exact: true}).fill(inbox.address);
  await page.getByLabel('Password', {exact: true}).fill(process.env.TEST_PASSWORD);
  await page.getByRole('button', {name: 'Create account', exact: true}).click();
  // Insert the bounded wait loop below here.
  // Validate the extracted URL host before visiting it.
} finally {
  await fetch(base, {method: 'DELETE', headers});
}
const deadline = Date.now() + 120_000;
let email;
while (Date.now() < deadline) {
  const r = await fetch(base + '/wait?timeout=20&subject=Verify', {headers});
  if (r.status === 429) {
    await new Promise(resolve => setTimeout(resolve, 5000));
    continue;
  }
  if (!r.ok) throw new Error('Read failed: ' + r.status);
  const result = await r.json();
  if (result.status === 'matched') { email = result; break; }
  await new Promise(resolve => setTimeout(resolve, 5000));
}
if (!email) throw new Error('Verification email did not arrive in time');
// email.message contains text and HTML; email.extraction contains codes and links.
const expectedOrigin = new URL(process.env.TEST_APP_URL).origin;
const links = email.extraction.links.filter(value => new URL(value).origin === expectedOrigin);
if (links.length !== 1) throw new Error('Expected exactly one application link');
await page.goto(links[0]);
await expect(page.getByText('Email verified', {exact: true})).toBeVisible();

Place the wait loop and assertion inside try, before finally. Set your Playwright test timeout above the combined inbox-provisioning and email deadlines (for example 210 seconds). Keep TEST_PASSWORD and TEST_APP_URL in test configuration. Disable traces or redact sensitive fields for this flow.

4. Extend the test to password reset

Record the latest message ID before requesting a reset. Add after=LAST_ID and subject=Reset to the wait URL. Validate the reset link origin, complete the reset, and assert that the new test password works. This prevents the signup email from being mistaken for the reset email.

When a test fails

Check provisioning status, your application's outgoing-mail logs and the exact filter text. Use a bounded retry for 429 and transient 503 errors. A 410 requires a new inbox. Never replace a failed email assertion with an unconditional sleep or a passing test.

Each inbox lasts 24 hours from creation, with up to 100 incoming emails and 10 MB. Create up to three per network per UTC day, with no more than three active. Browser and API creations share these limits. There is also a service-wide daily capacity limit; a 429 response means you must wait or use an authenticated workspace. The service is receive-only.

The address is not a public mailbox: reading requires its secret bearer token, or its browser-only cookie. Anyone holding that secret can read the inbox. Never put it in a URL, repository, screenshot or agent trace. This is not end-to-end encryption. At expiry, access and receiving stop; the cleanup worker removes mailbox contents. Encrypted backups follow the backup retention policy. Do not use temporary addresses for sensitive or lasting account access.