InboxSpecDocumentation

Compare and switch

Migrate from Mailosaur

Map Mailosaur messages.get, search criteria, codes and links to the InboxSpec wait.

Mailosaur's client waits by searching repeatedly until a message appears. InboxSpec's wait is one request that the server holds open until a message matches or the timeout passes. Most tests change in three places: where the address comes from, the wait call, and how codes and links are read. Mailosaur behaviour described here was checked on 24 October 2026; see the Mailosaur comparison for sources.

Before and after

A Node.js signup test using Mailosaur's client:

Example
import MailosaurClient from "mailosaur";

const mailosaur = new MailosaurClient(process.env.MAILOSAUR_API_KEY);
const serverId = process.env.MAILOSAUR_SERVER_ID;
const emailAddress = `signup-${Date.now()}@${serverId}.mailosaur.net`;

await startSignup(emailAddress); // your application sends the email

const message = await mailosaur.messages.get(
  serverId,
  { sentTo: emailAddress, subject: "Verify" },
  { timeout: 30000 },
);
const code = message.html.codes[0].value;
const link = message.html.links[0].href;

The same test with InboxSpec's REST API and Node.js 18 or later. Set INBOXSPEC_API_KEY to a project key first.

Example
const origin = process.env.INBOXSPEC_API_URL ?? "https://api.inboxspec.com";

async function inboxspec(path, body) {
  const response = await fetch(origin + path, {
    method: "POST",
    headers: {
      Authorization: "Bearer " + process.env.INBOXSPEC_API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  // Authentication, quota and validation problems are HTTP errors, never timeouts.
  if (!response.ok) throw new Error(`InboxSpec ${response.status}: ${await response.text()}`);
  return response.json();
}

const inbox = await inboxspec("/v1/inboxes/for-run", { run_id: `signup-${Date.now()}` });

await startSignup(inbox.address); // your application sends the email

const result = await inboxspec("/v1/messages/wait", {
  inbox: inbox.address,
  match: { subject_contains: "Verify", has_code: true },
  timeout_ms: 30000,
});
if (result.status === "timeout") throw new Error(result.hint);
const code = result.message.codes[0];
const link = result.message.links.find((candidate) => candidate.kind === "verify")?.href;

Register the inbox before your application sends to it. Mail to an address that was never registered is not stored.

Call mapping

MailosaurInboxSpec
API key as the Basic-auth usernameAuthorization: Bearer with a project key
A server, with any address under its subdomainA project, with one registered inbox per run from POST /v1/inboxes/for-run
messages.get(serverId, criteria, options)POST /v1/messages/wait with inbox, match and timeout_ms
sentToThe inbox itself, or match.to_contains
sentFrommatch.from_contains
subjectmatch.subject_contains
bodymatch.body_contains, which searches the subject and plain-text part only
match: "ANY"No equivalent. Match fields are always combined with AND, so issue one wait per alternative
receivedAfter, which defaults to one hour agomatch.since, which has no default; every stored message in the inbox is eligible
timeout, 10 seconds by default, counted in the clienttimeout_ms, 120,000 by default and at most 285,000, counted on the server
errorOnTimeout: falseAlways on: a timeout returns status: "timeout" with a hint
message.html.codes[0].valueresult.message.codes[0], best candidate first
message.html.links[0].hrefresult.message.links, each with href, text and kind
message.html.bodyGET /v1/messages/:id/body?format=html
messages.search and messages.listGET /v1/messages?inbox=…
messages.del and messages.deleteAllDELETE /v1/messages/:id, DELETE /v1/inboxes/:id or POST /v1/inboxes/cleanup
SMTP settings from the inbox's Connect tabCapture SMTP credentials, port 587 with STARTTLS

Behaviour to check after switching

  • A wait does not consume messages, and it returns the oldest match. Use a fresh run ID for each journey rather than relying on a time window. See Inboxes.
  • Code values are ranked candidates. Assert the one your application issued; see OTP and link assertions.
  • Keep your HTTP client's timeout longer than timeout_ms, so the server's answer arrives before your client gives up.
  • InboxSpec does not include Mailosaur's compatibility, accessibility, SMS or preview testing. Keep those tests where they are.

Next, follow the CI guide for fixtures, assertions and cleanup in a pipeline.