Mailosaur is an email and SMS testing service aimed at QA teams, with client libraries for the common languages and guides for Cypress, Playwright and Selenium. Lumbox gives AI agents their own inboxes. The two overlap on the step that every signup test and every signup agent share (wait for an email, then pull out a code or a link) and differ on most of what surrounds it. The Mailosaur details below come from its documentation and pricing page as of September 2026.
How addresses work
In Mailosaur, an inbox (called a server in the API) has its own domain name, such as abc123.mailosaur.net, and any address at that domain delivers into it. The docs describe this as a wildcard pattern, which is why every plan includes unlimited test addresses: your test invents signup-4821@abc123.mailosaur.net and then searches for mail sent to it.
In Lumbox, each address is its own inbox, created with POST /v1/inboxes and returned with an ID, for example signup-4821@trylumbox.com. Creating one costs an API call and counts against the plan's inbox limit (3 on free, 10 on Starter). In exchange, everything about that address (its emails, threads and parsed codes) hangs off one inbox ID you can hand to an agent, and a DELETE removes all of it when the task is over.
Waiting and extracting
Mailosaur's messages.get waits for the first message that matches your criteria (sent to, sent from, subject, body text). The default timeout is 10 seconds and can be raised, and by default it only considers messages received in the last hour. The message it returns has html.codes and text.codes arrays filled automatically, the matching links arrays, images, attachments, and a SpamAssassin analysis call you can run against it.
// Mailosaur (Node), from its docs
const email = await mailosaur.messages.get(
"SERVER_ID",
{ sentTo: "signup-4821@SERVER_ID.mailosaur.net" },
{ timeout: 20000 }
);
const code = email.html.codes[0].value;
// Lumbox (Node SDK)
import { Lumbox } from "lumbox";
const lumbox = new Lumbox({ apiKey: process.env.LUMBOX_API_KEY, maxRetries: 0 });
const inbox = await lumbox.createInbox({ name: "signup-4821" });
// ...submit inbox.address in the signup form...
const { code: otp } = await inbox.waitForOtp({ from: "acme.dev", timeout: 60 });
await inbox.delete();
The maxRetries: 0 is deliberate. The Lumbox SDK retries HTTP 408 by default, and a wait that times out returns 408, so with the default of two retries a 60 second wait can block for roughly three minutes before it throws.
On the Lumbox side, /otp looks back five minutes and returns only the code, while /wait returns the whole email and by default only matches mail that arrives after the call starts. Parsed values live under parsed: otp_codes, verification_links, magic_links and code_expiry. On received mail the parser reads the subject and plain-text body for 4 to 8 digit codes, so a code that only exists in the HTML part is a case where Mailosaur's separate html.codes array will find something Lumbox misses.
Sending, and who you can send to
Mailosaur can send, reply and forward, with a restriction that fits its purpose: outbound mail only goes to verified external addresses, meaning addresses you have added to your account. That lets a test simulate a customer replying to your product without Mailosaur becoming a general sending service. Forwarding individual messages is on the Personal plan; replying and sending to external addresses are listed under Core.
Lumbox sends to arbitrary recipients because an agent's correspondents are not known in advance: a vendor's support desk, a person who wrote in, a service that wants a reply to confirm. Replies set In-Reply-To and References so they thread in the recipient's client. The guardrails are different in kind: per-minute and monthly send limits by plan, a suppression list for bounces and complaints, and checks that stop shared-domain inboxes from impersonating other brands.
Side by side
| Mailosaur | Lumbox | |
|---|---|---|
| Built for | QA and test automation | AI agents that need their own address |
| Addresses | Wildcard: any address at the inbox's domain | One address per inbox, created by API |
| Waiting | messages.get, 10 second default timeout | /wait and /otp, 30 second default, up to 120 |
| Codes and links | html.codes, text.codes, links arrays | parsed.otp_codes, verification_links, magic_links |
| Outbound mail | Verified external addresses only | Any recipient, within plan limits |
| Mail client access | POP3 and IMAP on Core | IMAP per inbox |
| SMS testing | Add-on, from $37.50/month | None |
| Rendering previews | Add-on, from $25/month | None |
| Entry price | Personal from $20/month billed annually, 14-day trial | Free plan, then $9/month |
| Hosting | Hosted service | Hosted service; no self-hosted edition |
When Mailosaur wins
If your team writes end-to-end tests for a product that sends email, Mailosaur is built for exactly that. The wildcard domain removes inbox setup from every test, the message object is shaped for assertions (sender, recipients, links, images, attachments), the SpamAssassin check and rendering previews catch problems a functional test never sees, and SMS lives in the same place as email. The Core plan adds five team seats, POP3 and IMAP access, and mail rules for auto-forwarding.
When Lumbox wins
If the thing reading the email is an agent acting on its own behalf, the requirements shift. It needs an address it can keep, a way to wait for a code without writing a polling loop, and the ability to answer mail from people and services it has never contacted before. Lumbox also ships an MCP server, so an assistant like Claude or Cursor can create an inbox and wait for a code as tool calls rather than through code you write. The free plan (3 inboxes, 500 received and 100 sent emails a month) is enough to build and try the whole loop.
The Playwright OTP guide shows the Lumbox half of a browser signup test end to end, the sending docs cover reply and forward, and the MailSlurp comparison covers the other large testing platform.