Test-only backend endpoints that skip email — you're not testing email anymore
Gmail API with plus-addressing — shared inbox, race conditions in parallel
MailDev/MailHog for local — doesn't work in CI without extra infrastructure
Mailosaur/Mailtrap — expensive, enterprise-focused, still uses polling
You shouldn't have to build this yourself.
Each test creates its own inbox — no shared state, no race conditions, safe for parallel CI.
Long-poll that returns the moment email arrives. No client-side polling, no sleep(), no retries.
Magic links, OTPs (4-8 digits), and verification codes extracted from email body automatically. No regex.
Uses your actual auth provider (WorkOS, Auth0, Clerk, Supabase). Tests the full path, not a mock.
mailhooks.inboxes.create() — unique address, auto-expires with TTL
Your app sends a real email (magic link, OTP, password reset)
inboxes.waitForMessage() — server-side long-poll, returns when email arrives
msg.extracted.magicLink / msg.extracted.otp — no parsing needed
Create an inbox, trigger signup, wait for the email, use the magic link. Four lines that replace 50.
// POST /v1/inboxes — Create a virtual inbox
{
"id": "3e86a45b-f29d-4f33-883f-a89fed41cbd3",
"address": "[email protected]",
"active": true,
"messageCount": 0,
"expiresAt": "2025-01-15T11:30:00Z"
}
// GET /v1/inboxes/:id/messages/wait — Server-side long-poll
{
"id": "msg_xyz789",
"from": "[email protected]",
"subject": "Sign in to YourApp",
"extracted": {
"magicLink": "https://yourapp.com/auth?token=abc123",
"otp": null,
"verificationCode": null,
"links": ["https://yourapp.com/auth?token=abc123"],
"codes": []
}
}import { test, expect } from '@playwright/test';
import Mailhooks from '@mailhooks/sdk';
const mh = new Mailhooks({ apiKey: process.env.MAILHOOKS_API_KEY });
test('magic link login', async ({ page }) => {
// Inbox per test — no shared state
const inbox = await mh.inboxes.create({ ttlSeconds: 3600 });
// Trigger the real signup flow
await page.goto('/login');
await page.fill('[name="email"]', inbox.address);
await page.click('button[type="submit"]');
// Server-side wait — no polling, no sleep()
const msg = await mh.inboxes.waitForMessage(inbox.id);
// Magic link auto-extracted — no regex
expect(msg.extracted.magicLink).toBeTruthy();
await page.goto(msg.extracted.magicLink);
await expect(page.locator('.dashboard')).toBeVisible();
});
test('OTP verification', async ({ page }) => {
const inbox = await mh.inboxes.create({ ttlSeconds: 3600 });
await page.goto('/verify');
await page.fill('[name="email"]', inbox.address);
await page.click('button[type="submit"]');
const msg = await mh.inboxes.waitForMessage(inbox.id);
// OTP auto-extracted (4-8 digits)
expect(msg.extracted.otp).toMatch(/^\d{6}$/);
await page.fill('[name="otp"]', msg.extracted.otp);
await page.click('button[type="submit"]');
await expect(page.locator('.dashboard')).toBeVisible();
});Every inbox can be configured with sender filtering rules. You can whitelist specific domains or email addresses, or use our webhook to implement your own spam filtering logic. Emails that don't match your rules are automatically rejected.
Webhooks are typically delivered within 100-500ms of email receipt. We process emails in real-time with no polling delays. For high-availability applications, we also offer webhook retries with exponential backoff.
Mailhooks is built specifically for inbound email. We offer simpler setup (no DNS changes required for testing), better attachment handling with direct download URLs, and a developer-first API for fetching emails programmatically—perfect for E2E testing.
Yes! You can connect your own domain with simple DNS configuration. We also provide free subdomains on inbox.mailhooks.dev for testing and development.
We automatically retry failed webhooks with exponential backoff for up to 24 hours. You can also use our API to fetch any missed emails. All emails are stored and accessible via the dashboard.
Takes ~2 minutes — no email infrastructure required.
Sign up for free in seconds.
Get a unique email address for your use case.
Point to your endpoint and start receiving emails.