MailSlurp logo

Receive email with the MailSlurp API and SDK

Create an inbox, wait for a new message, read its body, and download attachments with the MailSlurp email API or SDK.

Use with your tools
Separate envelopes representing a private inbox for each test

Receiving email in code should feel like waiting for a normal application event: create an address, trigger the message, wait for the right email, and inspect the result.

Opening a shared mailbox by hand makes those checks difficult to repeat, and parallel tests can pick up each other's messages. MailSlurp provides private inboxes and an API that waits for the expected email, so your test can read it and continue the same account journey.

Use this workflow for signup tests, OTP and password-reset checks, or a monitor that expects one message. For every retrieval and content option, see the complete receiving email guide.

Quick answer

With MailSlurp you can receive an email in four steps:

  1. Create an inbox and keep its id and emailAddress.
  2. Send or trigger an email to that address.
  3. Wait for the expected message with a timeout.
  4. Read the body, headers, links, and attachments from the returned email.

Use a wait method when one application action should produce one known message. Use email webhooks when your service processes inbound mail continuously.

Install and configure the JavaScript SDK

Install the MailSlurp client:

npm install --save mailslurp-client

Set MAILSLURP_API_KEY to an API key from the MailSlurp dashboard in your local environment or CI secret store. The helper below initializes the client from that variable. Run it in Node.js, not in browser-delivered code, and keep the key out of your repository.

Create a receiving address

An inbox has two values you will use throughout the workflow:

  • emailAddress is the destination you give to the system sending the message.
  • id identifies the inbox in MailSlurp API and SDK calls.

Create a fresh inbox for each test scenario with createInboxWithOptions. Give its emailAddress to the application that will send the email; use its id in the receive request. Separate inboxes prevent parallel scenarios from consuming each other's messages.

The helper below creates the inbox and deletes it after the message check, including if the application action or an assertion fails. Its ten-minute expiry also limits the lifetime of an inbox left behind if the test process stops unexpectedly.

Wait for the received email

For a signup test, the application should send its confirmation email to the new address. The test then needs that message's verification link or code, not merely a nonempty inbox. waitForMatchingFirstEmail waits for one message with the sender and subject you expect and returns its complete content.

Save this TypeScript helper in your test project. Pass triggerEmail the application action that sends to the supplied recipient, such as submitting your signup form. Pass checkEmail the assertions and follow-up action that use the returned email, such as opening its confirmation link and checking the account. Supply your application's actual sender and subject; neither is inferred from the receiving address.

import assert from "node:assert/strict";
import {
  MailSlurp,
  MatchOptionFieldEnum,
  MatchOptionShouldEnum,
  type Email,
} from "mailslurp-client";

export async function testApplicationEmail({
  expectedSender,
  expectedSubject,
  triggerEmail,
  checkEmail,
}: {
  expectedSender: string;
  expectedSubject: string;
  triggerEmail: (recipient: string) => Promise<void>;
  checkEmail: (email: Email) => Promise<void>;
}) {
  const apiKey = process.env.MAILSLURP_API_KEY;
  if (!apiKey) throw new Error("Set MAILSLURP_API_KEY");
  const mailslurp = new MailSlurp({ apiKey });
  const inbox = await mailslurp.createInboxWithOptions({
    expiresIn: 600_000,
  });

  try {
    const since = new Date();
    await triggerEmail(inbox.emailAddress);
    const email = await mailslurp.waitController.waitForMatchingFirstEmail({
      inboxId: inbox.id,
      since,
      timeout: 60_000,
      unreadOnly: false,
      matchOptions: {
        matches: [
          {
            field: MatchOptionFieldEnum.FROM,
            should: MatchOptionShouldEnum.EQUAL,
            value: expectedSender,
          },
          {
            field: MatchOptionFieldEnum.SUBJECT,
            should: MatchOptionShouldEnum.EQUAL,
            value: expectedSubject,
          },
        ],
      },
    });
    assert.ok(email.to.includes(inbox.emailAddress));
    assert.equal(email.from, expectedSender);
    assert.equal(email.subject, expectedSubject);
    await checkEmail(email);
  } finally {
    await mailslurp.inboxController.deleteInbox({ inboxId: inbox.id });
  }
}

The application-specific callbacks are the parts you supply; the helper handles inbox creation, receipt checks, and cleanup. Keep checkEmail inside this lifecycle because deleting the inbox also removes its stored messages. Save any permitted failure diagnostics before cleanup, without writing private links or codes to shared logs.

The since value is recorded immediately before the application action, and unreadOnly: false allows the current message to match even if someone has opened it in the dashboard. Read state does not prove freshness: an old message can be unread, and a new one can already be read.

The receive wait is bounded to 60 seconds. Allow additional time in your test runner for the application action, message assertions, and cleanup. Let a failed send action or receive request fail the test; do not substitute an older message when delivery times out.

Match the message you actually expect

The sender and subject conditions above must both match. For example, a welcome email and a confirmation email can arrive at the same new address; the confirmation subject tells the test which one to use. Set expectedSender to the application's sending address, not the inbox receiving it.

For a subject that contains a changing account name or request identifier, use a deliberate CONTAIN or MATCH condition instead of an exact subject. The matching email guide explains these options. For an OTP test, validate the expected message before extracting the code.

A new inbox isolates the scenario, while sender, subject, and time filters select the message within it. If the same scenario requests multiple resets or resends, use an application-specific request marker where available and verify the application's policy for earlier links or codes.

Read the complete message

The helper's checkEmail callback receives a full Email, including its body and attachment references. List endpoints return compact previews instead. When working from a list or webhook, call emailController.getEmail with that message's emailId to fetch the complete email before inspecting its content.

The full email gives you the content needed to:

  • assert the subject and sender
  • inspect the plain-text or HTML body
  • extract a verification link or one-time code
  • review SMTP and authentication headers
  • find attachment IDs for download

For a confirmation or reset message, select the intended link by its destination and purpose before opening it. Emails can also contain help, preference, and unsubscribe links. Check that the selected URL belongs to your application, then perform the action and assert the expected account. For an OTP, keep the extracted code as a string so leading zeros survive.

If you need CSS selectors, regular expressions, or link extraction, continue with extracting email content. The Playwright email testing guide and Cypress email testing guide show how to connect message retrieval to browser steps.

An envelope between code brackets for reading an email in code

Download attachments

Attachments are separate resources associated with the email. Fetch the complete email first, then download the attachment you need by ID. This keeps the initial inbox and webhook payloads small even when a message carries a large file.

Check the expected filename, content type, and size before downloading. For an invoice test, assert the relevant content of the downloaded file as well as its presence; an empty or incorrect attachment can still have the right filename.

Use the email attachment API guide for download examples, metadata, base64 responses, and file checks.

Choose wait methods, listing, or webhooks

Your job Best starting point
Block a test until one known message arrives Wait for the latest or first matching email
Find mail that is already in an inbox List inbox emails, then fetch by email ID
Process every inbound message in a running service Subscribe to NEW_EMAIL webhooks
Read received mail through an existing mail client Use MailSlurp IMAP access

Wait methods and webhooks solve different timing problems. A wait call lets one test or request pause until its expected message exists. A webhook tells a long-running application that new work is ready. Many teams use both: waits for CI and webhooks for production intake.

A reliable receive-email test

A useful test proves the customer journey, not merely that an inbox contains mail:

  1. Create a new inbox for the test.
  2. Give its address to the real signup, login, or reset flow.
  3. Record the time just before triggering the email.
  4. Wait for a message with the expected sender or subject.
  5. Assert the recipient and message content.
  6. Extract the link or code and complete the action.
  7. Keep the email ID with failure output for investigation.
  8. Delete the test inbox after its checks and any permitted diagnostic capture.

A delivered password-reset email with a broken link is still a failed customer journey. In the helper, put the link or code action and the final account assertion in checkEmail, so they complete before teardown.

Troubleshooting

The wait returned an old email

Use a new inbox per test scenario and record since immediately before the action that sends the email. Match the intended sender and subject. An unread-only filter cannot distinguish old mail from the current message.

The request timed out

Confirm the application submitted its send successfully and used the inbox emailAddress. Inspect the inbox to distinguish a missing message from one excluded by the sender, subject, or time filter. Only increase the MailSlurp and calling-client timeouts when the expected delivery window warrants it. See inbox receiving diagnostics for transport checks.

The body is missing from a list result

Inbox lists contain previews. Fetch the complete message with its email ID before reading the body or attachment IDs.

Several test workers read the same message

Give each active test scenario its own inbox, even when multiple scenarios share a worker. Delete only that scenario's inbox during teardown. If a person is inspecting the current message in the dashboard, retain the timestamp and matching conditions and use unreadOnly: false.

Next step

Start with the receive email API overview if you are comparing capabilities, or open the complete receiving email guide for search, HTML queries, attachments, webhooks, and forwarding.