End-to-end tests often get stuck at the same point: the app sends a one-time password, but the test runner cannot safely read a real inbox. TempMailGrab gives each test run a disposable inbox and exposes the parsed OTP through a developer API, so Playwright can complete the full signup or login flow without manual email checking.
Useful links:
- API docs: https://tempmailgrab.com/api-docs
- Playwright guide: https://tempmailgrab.com/temp-mail-api-playwright
- OpenAPI spec: https://tempmailgrab.com/api/openapi.json
- npm package: https://www.npmjs.com/package/tempmailgrab
Why Use a Disposable Inbox in Tests
Testing OTP email with a shared inbox creates three problems: old messages can be picked up by mistake, parallel test runs collide, and private test credentials end up living in a mailbox. A temporary inbox avoids those issues because each run can create a fresh address, wait for the exact message, extract the OTP, and delete the inbox afterward.
Playwright Example
import { expect, test } from '@playwright/test';
const API_BASE = 'https://tempmailgrab.com/api/v1';
const API_KEY = process.env.TEMPMAILGRAB_API_KEY;
async function createInbox() {
const response = await fetch(`${API_BASE}/inbox`, {
method: 'POST',
headers: {
Authorization: `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ ttl_seconds: 3600 }),
});
if (!response.ok) throw new Error(`Inbox creation failed: ${response.status}`);
return response.json() as Promise<{ id: string; address: string }>;
}
async function waitForOtp(inboxId: string) {
const deadline = Date.now() + 60_000;
while (Date.now() < deadline) {
const response = await fetch(`${API_BASE}/inbox/${inboxId}/messages`, {
headers: { Authorization: `Bearer ${API_KEY}` },
});
if (!response.ok) throw new Error(`Message lookup failed: ${response.status}`);
const data = await response.json() as {
messages: Array<{ id: string; subject: string | null; extracted_otp: string | null }>;
};
const match = data.messages.find((message) => message.extracted_otp);
if (match?.extracted_otp) return match.extracted_otp;
await new Promise((resolve) => setTimeout(resolve, 2000));
}
throw new Error('OTP email did not arrive in time');
}
test('user can sign up with email OTP', async ({ page }) => {
const inbox = await createInbox();
await page.goto('https://your-app.example/signup');
await page.getByLabel('Email').fill(inbox.address);
await page.getByRole('button', { name: /continue/i }).click();
const otp = await waitForOtp(inbox.id);
await page.getByLabel('Verification code').fill(otp);
await page.getByRole('button', { name: /verify/i }).click();
await expect(page.getByText(/welcome/i)).toBeVisible();
});
CI Notes
Keep the API key in your CI secret store and read it from process.env.TEMPMAILGRAB_API_KEY. Use one inbox per test or per worker when tests run in parallel. For stricter cleanup, call DELETE /inbox/{id} after the flow.
TempMailGrab also publishes a machine-readable OpenAPI contract at https://tempmailgrab.com/api/openapi.json, which can be imported into Postman, generated clients, or internal API catalogs.
Top comments (0)