TypeScript client
@mailwurf/e2e is a fetch-only client for the mailbox API. Playwright is a consumer, not a dependency.
@mailwurf/e2e wraps the HTTP API. It has no Playwright dependency. Anything that can fetch can use it: Playwright, Vitest, Cypress, a Node script.
Install
Public on npm.
npm install @mailwurf/e2eConfigure
MAILWURF_API_KEY=mw_live_…The client talks to https://api.mailwurf.io and always calls /api/v1. Only the key is required.
createClient({ baseUrl, apiKey, fetch }) overrides the env vars. To use a different Mailwurf origin, pass baseUrl or set MAILWURF_URL; the option wins. A host without scheme, such as api.dev.mailwurf.io, is treated as https://. Trailing slashes are ignored.
import { createClient } from "@mailwurf/e2e";
const mailwurf = createClient(); // MAILWURF_API_KEY, API at https://api.mailwurf.ioThe key is sent as Authorization: Bearer. Create a key in the Mailwurf dashboard; the full value is shown once.
Usage
One inbox per test. The app keeps its real mail sender.
const inbox = await mailwurf.createInbox();
await page.getByLabel("Email").fill(inbox.address);
await page.getByRole("button", { name: "Sign up" }).click();
const mail = await inbox.waitFor({ subject: "Confirm", timeout: 15_000 });
await page.goto(mail.links[0]!);
// mail.otp → "482193"subject is a case-insensitive substring, matched on the server. timeout is milliseconds, default 15000. 0 is a single lookup with no wait.
Client surface
| Call | HTTP |
|---|---|
health() | GET /api/v1/health |
createInbox() | POST /api/v1/inboxes |
getInbox(id) | GET /api/v1/inboxes/{id} |
inboxByAddress(address) | GET /api/v1/inboxes?address= |
getMessage(id) | GET /api/v1/messages/{id} |
listMessages(id, { subject, after }) | GET /api/v1/inboxes/{id}/messages |
send(id, { to, subject, text, html, inReplyTo }) | POST …/messages |
waitFor(id, { subject, timeout, after, signal }) | GET …/messages/wait |
deleteInbox(id) | DELETE /api/v1/inboxes/{id} |
createInbox() returns a MailwurfInbox with id, address, createdAt, expiresAt, plus waitFor(), send(), messages(), and delete(). from is always the inbox address.
Temporary inboxes expire after one hour. Custom temporary addresses are not supported. Permanent addresses are managed in the dashboard and cannot be deleted through this client.
Message shape
Wire JSON is snake_case. The client maps to camelCase Dates.
type Message = {
id: string;
inboxId: string;
from: string;
to: string;
subject: string;
text: string;
html: string;
links: string[];
otp: string;
receivedAt: Date;
};links is always an array. otp is a labeled 4–8 digit code, else the first 6-digit word, else "". Wait does not filter on those fields. Raw MIME stays on the server.
Errors
HTTP 408 throws MailwurfTimeoutError. Every other failure throws MailwurfError with a numeric status.