Mailwurf

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/e2e

Configure

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.io

The 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

CallHTTP
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.

On this page