Temp Mail API for Playwright

A Playwright test should own a fresh inbox for every run. TempMailGrab creates that inbox over API and returns OTP codes as structured data, so the browser test can stay focused on the user flow.

Your Temporary Email Address

Generating...
Inbox
Live
SenderSubjectView
📬

Waiting for mail...

  • No signup
  • 24-hour inbox
  • OTP auto-detect
  • Zero tracking
  • Android · Chrome · Firefox
  • Developer API

Why Playwright tests need real inboxes

Mocking email is fast, but it misses the integration risk: the template can change, the queue can delay, the verification link can be malformed, or the OTP can be hidden in HTML instead of text. A real disposable inbox tests the actual signup path while still keeping every run isolated.

TempMailGrab gives each Playwright run a private temporary address, keeps it alive long enough for retries, and exposes extracted_otp and extracted_links so your test does not scrape MIME.

Recommended flow

  1. Create an inbox in test setup with POST /api/v1/inbox.
  2. Fill your app's signup form with the returned address.
  3. Poll GET /api/v1/inbox/:id/messages until the OTP or verification link appears.
  4. Complete the Playwright assertion with the parsed field.
  5. Delete the inbox in teardown, or let the TTL expire automatically.

Playwright fixture shape

import { test as base, expect } from "@playwright/test";
import { TempMailGrab } from "tempmailgrab";

export const test = base.extend({
  inbox: async ({}, use) => {
    const tmg = new TempMailGrab({ apiKey: process.env.TMG_API_KEY });
    const inbox = await tmg.createInbox({ ttlSeconds: 3600 });
    await use(inbox);
    await inbox.delete().catch(() => {});
  },
});

test("user can verify email", async ({ page, inbox }) => {
  await page.goto("https://your-app.example/signup");
  await page.getByLabel("Email").fill(inbox.address);
  await page.getByRole("button", { name: "Create account" }).click();

  const otp = await inbox.waitForOtp({ timeoutMs: 60000 });
  await page.getByLabel("Verification code").fill(otp);
  await page.getByRole("button", { name: "Verify" }).click();

  await expect(page.getByText("Account verified")).toBeVisible();
});

Parallel test safety

Do not share one disposable address across a Playwright project. Create the inbox inside the fixture so each worker receives a separate address. That prevents one test from reading another test's OTP and turns email delivery into an isolated dependency instead of global state.

Useful companion links

Last verified: . Endpoint examples and package versions may change; check the API docs before copying into production CI.

Automate inboxes with the API

Prefer to script it? Generate disposable inboxes and read auto-parsed OTPs & verification links programmatically — built for QA, test automation, and bots. The same private inboxes, over a clean REST API.

# 1. Create a private inbox
curl -X POST https://tempmailgrab.com/api/v1/inbox \
  -H "Authorization: Bearer $TMG_API_KEY"
# → { "id": "inb_…", "address": "a1b2c3d4e5f6@tempmailgrab.com" }

# 2. Poll for mail — the OTP + verification links are auto-extracted
curl https://tempmailgrab.com/api/v1/inbox/$ID/messages \
  -H "Authorization: Bearer $TMG_API_KEY"
# → { "messages": [{ "extracted_otp": "123456", "extracted_links": [ … ] }] }

Related guides

Frequently Asked Questions

Can Playwright test real email verification without a shared inbox?

Yes. Create a fresh TempMailGrab inbox in a Playwright fixture, use its address during signup, then poll the API for messages tied to that inbox ID. Each test run gets its own address.

Does TempMailGrab return OTP codes directly?

Yes. Messages include an extracted_otp field when a one-time code is detected, plus extracted_links for confirmation URLs. That keeps brittle parsing out of the Playwright test.

Should I delete the inbox after the test?

It is a good practice in teardown, but not required. Inboxes expire automatically based on their TTL, and deleting them early removes messages and attachments immediately.