Vitest and Node
Test the emails your backend sends, without a browser.
@mailwurf/e2e only needs fetch. Any Node test runner can use it to check that an endpoint sends the right email.
import { expect, it } from "vitest";
import { createMailwurf } from "@mailwurf/e2e";
const mailwurf = createMailwurf(process.env.MAILWURF_API_KEY!); // API key (mw_live_…) from Dashboard → API keys
const appUrl = process.env.APP_URL ?? "http://localhost:3000";
it("sends a password reset link", { timeout: 30_000 }, async () => {
const inbox = await mailwurf.createInbox();
await fetch(`${appUrl}/api/signup`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ email: inbox.address, password: "correct-horse-battery" }),
});
const welcome = await inbox.waitFor({ subject: "Welcome" });
const res = await fetch(`${appUrl}/api/password-reset`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ email: inbox.address }),
});
expect(res.status).toBe(202);
const mail = await inbox.waitFor({ subject: "Reset", after: welcome.id });
expect(mail.links.some((url) => url.startsWith(`${appUrl}/reset-password?token=`))).toBe(true);
});Set the test timeout above the Mailwurf timeout, so a missing email fails with MailwurfTimeoutError instead of a runner timeout.
Without the package
The API is plain HTTP. From any language:
INBOX=$(curl -s -X POST "https://api.mailwurf.io/v1/inboxes" \
-H "Authorization: Bearer $MAILWURF_API_KEY")
ID=$(echo "$INBOX" | jq -r .id)
ADDRESS=$(echo "$INBOX" | jq -r .address)
# trigger the email for $ADDRESS, then:
curl -s "https://api.mailwurf.io/v1/inboxes/$ID/messages/wait?subject=Reset&timeout=30000" \
-H "Authorization: Bearer $MAILWURF_API_KEY" | jq -r '.links[]'See HTTP API.
Mailwurf