Any address, private domain.
Your workspace gets its own domain on devhelmmail.com. Every test run can use a fresh address on it, with no create-inbox call.
EMAIL TESTING · BETA
Send to anything@your-private-domain; the address exists the moment you use it. Your signup test blocks on one API call until the message lands, then reads the one-time code or the link from the response.
WHAT IT DOES
Everything an auth test needs from email, as API calls.
Your workspace gets its own domain on devhelmmail.com. Every test run can use a fresh address on it, with no create-inbox call.
Every message comes back with its one-time code and links already pulled out. Your test reads otp[0].value, not an HTML body.
When a test fails, open the message: headers, body, attachments. Need a message without sending one? Inject it through the API.
THE WAIT API
Use a fresh address per run, trigger the email, then make one call. It returns as soon as the message lands, so there is no sleep to tune.
signup-${Date.now()}@your-domain keeps parallel CI jobs apart.import { test, expect } from '@playwright/test';
const API = 'https://api.devhelm.io/api/v1';
const DOMAIN = process.env.DEVHELM_MAIL_DOMAIN!; // your domain on devhelmmail.com
test('signup with emailed one-time code', async ({ page, request }) => {
test.setTimeout(60_000);
const to = `signup-${Date.now()}@${DOMAIN}`;
await page.goto('https://staging.acme.dev/signup');
await page.getByLabel('Email').fill(to);
await page.getByRole('button', { name: 'Create account' }).click();
const res = await request.post(`${API}/email/wait`, {
headers: {
Authorization: `Bearer ${process.env.DEVHELM_API_TOKEN}`,
'x-phelm-workspace-id': process.env.DEVHELM_WORKSPACE_ID!,
},
data: { to, timeoutMs: 30_000 },
timeout: 35_000,
});
expect(res.status(), 'verification email never arrived').toBe(200);
const { message } = await res.json();
await page.getByLabel('Verification code').fill(message.otp[0].value);
await page.getByRole('button', { name: 'Verify' }).click();
await expect(page).toHaveURL(/\/welcome/);
});
// same wait call as signup.spec.ts, then:
const { message } = await res.json();
await page.goto(message.links[0].href); // the sign-in link from the email
await expect(page).toHaveURL(/\/dashboard/);
curl -sS -X POST "https://api.devhelm.io/api/v1/email/wait" \
-H "Authorization: Bearer $DEVHELM_API_TOKEN" \
-H "x-phelm-workspace-id: $DEVHELM_WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{"to":"signup+7f3a@acme-qa.devhelmmail.com","timeoutMs":30000}' \
| jq -r '.message.otp[0].value'
# 200 → 418302
# 408 → WAIT_TIMEOUT, nothing arrived within timeoutMs
PRIVATE DOMAIN
Most teams end up with a QA Gmail account or a disposable-mail domain anyone can read. A private domain avoids both.
mail.acme-test.com at DevHelm and use addresses like reset@mail.acme-test.com.Test run
signup.spec.ts
Unique address
signup+7f3a@…
Verified code
418302
INSPECT
When a wait fails, see the mail that arrived, or see that nothing did.
SETUP
Once the DevHelm team turns on your workspace, three steps take the Playwright signup test you already have to green.
Create an API key, copy your workspace ID, and add your private domain on devhelmmail.com.
Sign up with signup-${Date.now()}@your-domain in staging.
Paste the snippet above. The first matched wait returns the email and the code.
$ export DEVHELM_API_TOKEN=dh_live_xxxxxxxx
$ export DEVHELM_WORKSPACE_ID=1234
$ export DEVHELM_MAIL_DOMAIN=acme-qa.devhelmmail.com
$ npx playwright test signup.spec.ts
✓ signup with emailed one-time code
1 passed
FIT CHECK
It is built for tests that need to read real email in CI.
PRICING
Testing is its own plan, on the same subscription as monitoring. Add it to any monitoring plan, Free included.
$0
$19 / mo
$79 / mo
$149 / mo
Enterprise: unlimited domains, inboxes and events, 365-day history, custom pricing. Stored events count email and webhook together. Annual billing: two months free.
FAQ
Addresses, codes, privacy and pricing.
Use a fresh address per run, such as signup-${Date.now()}@your-domain, trigger the email, and call the wait endpoint for that address. The response includes the extracted code; fill it into your form.
Same flow: wait for the message, then page.goto the extracted link. No HTML parsing in your test.
No. Every address at your private domain already exists. Use any string before the @.
The wait call returns HTTP 408 WAIT_TIMEOUT after your timeout (default 30 s, maximum 120 s). It never returns an empty success. Open the domain in the dashboard to see whether anything arrived for that address.
Private. Each workspace gets its own domain, and only that workspace can read its mail.
On Team and above, yes: add your domain, point its MX record at DevHelm, and verify it.
There is a free plan: 1 domain, 300 stored events a month and 3 days of history. Paid plans are $19, $79 and $149 per month, and annual billing gets two months free.
The beta is open by invitation. Book a 30-minute demo and we'll set up your workspace.
$ curl -sS -X POST https://api.devhelm.io/api/v1/email/wait \
-H "Authorization: Bearer $DEVHELM_API_TOKEN" -H "x-phelm-workspace-id: $DEVHELM_WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{"to":"signup+7f3a@acme-qa.devhelmmail.com","timeoutMs":30000}' | jq -r '.message.otp[0].value'
418302