Playwright

How to automate email OTP verification in Playwright

Use a PostMX temporary inbox to receive a real OTP in the same test that triggered it, without shared mailboxes, brittle waits, or regex scraping.

Make the test wait for the email, not for a mailbox UI.

Why it works

OTP checks are reliable when the inbox is per-test

Shared inboxes create collisions, static codes hide regressions, and HTML scraping makes tests brittle. A temporary inbox keeps the verification path isolated, while PostMX gives you the code directly once the next message lands.

Keep the assertion focused

The browser test should prove that the app delivered the email and that the OTP arrived. Everything else belongs to the app under test, not to the mailbox plumbing.

Minimal Playwright test

import { test, expect } from "@playwright/test";
import { PostMX } from "postmx";

test("verifies OTP email", async ({ page }) => {
  const postmx = new PostMX(process.env.POSTMX_API_KEY!);
  const inbox = await postmx.createTemporaryInbox({ label: "playwright-otp" });

  await page.goto("http://localhost:3000/login");
  await page.getByLabel("Email").fill(inbox.email_address);
  await page.getByRole("button", { name: "Send code" }).click();

  const message = await postmx.waitForMessage(inbox.id);
  expect(message.otp).toBeTruthy();
});

Related links

Keep moving through the email testing stack

Use these pages when you need the shorter product overview or the adjacent workflow.

Product page OTP

OTP email testing

See the tighter product pitch for building OTP assertions with programmable inboxes.

Open page
Workflow Playwright

Playwright email testing

Jump into the broader Playwright-focused page if you want the full workflow context.

Open page
API Inbox

Temporary inbox API

Review the inbox primitive that powers the test flow and the rest of the product surface.

Open page

Start building today

Verify OTP emails with a real inbox

Send the message, wait for it to land, and assert the code your user actually receives.