# Waiting for matching entities

![](/assets/search-email.svg)

How to wait and search for matching emails, TXT messages, and events.

> **Info.**
> Email and SMS delivery is asynchronous. In tests and production workflows use sufficient wait timeouts and, when needed, retry wrappers so short delivery delays do not cause flaky failures.

## Waiting for messages
MailSlurp provides convenient methods for waiting for emails, SMS, and events to arrive. You can use these methods to wait for messages to arrive in tests or to wait for messages to arrive in production applications. You can also match based on the message content or metadata.

### Quick links
- [Email match search](https://www.mailslurp.com/guides/matching-email-search/)

## Examples
Here are examples of how to fetch emails in code.

### Creating a client
First [install a MailSlurp SDK](/docs/sdks/) for your language. Then create a client instance using your API key. You can find your API key on the [MailSlurp dashboard](https://app.mailslurp.com).

```typescript
const {MailSlurp} = await import("mailslurp-client");
const mailslurp = new MailSlurp({
    apiKey: YOUR_API_KEY
});
const inbox = await mailslurp.createInboxWithOptions({
    // expires in 5 minutes
    expiresIn: 300_000
});
expect(inbox.emailAddress).toContain("@mailslurp");
```

### Wait for latest email
The default wait method is to wait for the latest email. This means the first email matching the conditions. The client will hold a connection until the conditions are met.

```typescript
// send an email
await mailslurp.inboxController.sendEmailAndConfirm({
    inboxId: inbox.id,
    sendEmailOptions: {
        to: [inbox.emailAddress],
        subject: "First email",
    }
})
// wait for the first unread email to arrive
const email = await mailslurp.waitController.waitForLatestEmail({
    timeout: 120_000,
    inboxId: inbox.id,
    unreadOnly: true
})
expect(email.subject).toContain('First email')
```

In this example we create an [inbox](/docs/inboxes/) and then send an email from it to itself. Next we wait for the email to arrive and make assertions about its subject.

### Wait for matching emails
A more complex example is to wait for messages that match particular criteria, such as subject line.

```typescript
// send two emails
for (const i of [1, 2]) {
    await mailslurp.inboxController.sendEmailAndConfirm({
        inboxId: inbox.id,
        sendEmailOptions: {
            to: [inbox.emailAddress],
            // send a different message each time
            subject: `Match subject test-${i}`,
        }
    })
}
// wait for 2 emails matching the subject with a pattern
const emails = await mailslurp.waitController.waitForMatchingEmails({
    inboxId: inbox.id,
    timeout: 120_000,
    unreadOnly: true,
    count: 2,
    matchOptions: {
        matches: [
            {
                // expect subject to contain "Match subject"
                value: "Match subject",
                field: MatchOptionFieldEnum.SUBJECT,
                should: MatchOptionShouldEnum.CONTAIN
            }
        ]
    }
})
// assert we received two emails matching the subject
expect(emails.length).toEqual(2)
// the subjects contain the test number from the loop
expect(emails.filter(it => it.subject.includes("Match subject test-1")).length).toEqual(1)
```

For stricter matching, use the generic wait endpoint when you need one request body that combines count, timeout, unread state, sort order, and field-level match rules.

[API endpoint: `waitFor`](/docs/api/#waitFor)

[API endpoint: `waitForMatchingEmails`](/docs/api/#waitForMatchingEmails)

### Wait for plus-addressed emails
If you route multiple workflows through one inbox using plus aliases, match on the full recipient address in the `TO` field. This lets you wait for one alias deterministically without scanning large inbox result sets.

```bash
BASE_URL="https://api.mailslurp.com"
API_KEY="REPLACE_WITH_API_KEY"
INBOX_ID="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
FULL_PLUS_ADDRESS="app-user+signup-9f3a@example.test"

curl -sS -X POST "$BASE_URL/waitFor" \
  -H "x-api-key: $API_KEY" \
  -H "content-type: application/json" \
  -H "accept: application/json" \
  --data "{
    \"inboxId\": \"$INBOX_ID\",
    \"count\": 1,
    \"countType\": \"EXACTLY\",
    \"timeout\": 180000,
    \"delayTimeout\": 500,
    \"unreadOnly\": true,
    \"sortDirection\": \"DESC\",
    \"matches\": [
      {
        \"field\": \"TO\",
        \"should\": \"CONTAIN\",
        \"value\": \"$FULL_PLUS_ADDRESS\"
      }
    ]
  }"
```

For a full plus-address lifecycle (get/create alias, wait, then list by alias ID) see [plus addressing](/docs/plus-addresses/).

### Wait for email number
We can also wait for the "nth" email, an email at a particular index. This is useful for testing pagination or other scenarios where you want to wait for a particular email.

```typescript
const emailCount = await mailslurp.inboxController.getInboxEmailCount({inboxId: inbox.id})
const indexOfEmail0Based = emailCount.totalElements
await mailslurp.sendEmail(inbox.id, {
    to: [inbox.emailAddress],
    subject: "Next email"
})
const nthEmail = await mailslurp.waitController.waitForNthEmail({
    inboxId: inbox.id,
    index: indexOfEmail0Based
})
expect(nthEmail.subject).toContain('Next email');
```

### Wait for SMS
You can also wait for inbound SMS messages on phone numbers using wait-for methods.

```typescript
const sms = await mailslurp.waitController.waitForLatestSms({
  waitForSingleSmsOptions: {
    phoneNumberId: phone.id,
    timeout: 30_000,
    unreadOnly: true,
  },
});
expect(sms.body).toContain('Here is your code');
expect(sms.fromNumber).toEqual('+13252527014');
```

SMS waits use the same deterministic pattern as email waits. After a message arrives you can also extract verification codes with the SMS controller.

[API endpoint: `waitForSms`](/docs/api/#waitForSms)

[API endpoint: `waitForLatestSms`](/docs/api/#waitForLatestSms)

[API endpoint: `getSmsCodes`](/docs/api/#getSmsCodes)

<section data-component="DocsExamplesList" class="docs-related-examples" data-example-count="3">
<p class="docs-related-examples-title">Wait and OTP examples</p>
<p class="docs-related-examples-description">Start with these runnable projects for wait-for methods, email OTP, and SMS OTP checks.</p>
<div class="docs-card-grid docs-examples-list">
<a class="docs-card docs-example-card" href="https://www.github.com/mailslurp/examples/tree/master/wait-for-methods-vitest" target="_blank" rel="noopener noreferrer"><span class="docs-card-title">Wait For Methods Vitest</span><span class="docs-card-description">wait example repository: <code>wait-for-methods-vitest</code></span></a>
<a class="docs-card docs-example-card" href="https://www.github.com/mailslurp/examples/tree/master/playwright-email-testing" target="_blank" rel="noopener noreferrer"><span class="docs-card-title">Playwright Email Testing</span><span class="docs-card-description">playwright example repository: <code>playwright-email-testing</code></span></a>
<a class="docs-card docs-example-card" href="https://www.github.com/mailslurp/examples/tree/master/playwright-sms-testing" target="_blank" rel="noopener noreferrer"><span class="docs-card-title">Playwright Sms Testing</span><span class="docs-card-description">playwright example repository: <code>playwright-sms-testing</code></span></a>
</div>
</section>

## Waiting longer
If network latency or provider delivery varies, wrap wait calls in a small retry utility that retries up to three times until no exception is thrown.

```typescript
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

async function retryWait<T>(fn: () => Promise<T>, attempts = 3, delayMs = 1500): Promise<T> {
  let lastError: unknown;
  for (let attempt = 1; attempt <= attempts; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;
      if (attempt < attempts) await sleep(delayMs);
    }
  }
  throw lastError;
}

const email = await retryWait(() =>
  mailslurp.waitController.waitForLatestEmail({
    inboxId,
    timeout: 120000, // 2 minutes
    unreadOnly: true,
  })
);
```

```java
import java.util.concurrent.Callable;

public static <T> T retryWait(Callable<T> fn, int attempts, long delayMs) throws Exception {
  Exception lastError = null;
  for (int attempt = 1; attempt <= attempts; attempt++) {
    try {
      return fn.call();
    } catch (Exception ex) {
      lastError = ex;
      if (attempt < attempts) {
        Thread.sleep(delayMs);
      }
    }
  }
  throw lastError;
}

Email email = retryWait(
    () -> waitForControllerApi.waitForLatestEmail(
        inboxId, null, 120000L, true, null, null, null, null, null, null, null, null),
    3,
    1500L
);
```
