Waiting for matching entities
MailSlurp documentation.
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
Examples
Here are examples of how to fetch emails in code.
Creating a client
First install a MailSlurp SDK for your language. Then create a client instance using your API key. You can find your API key on the MailSlurp dashboard.
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.
// 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 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.
// 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.
/waitFor
Wait for an email to match the provided filter conditions such as subject contains keyword.
Generic waitFor method that will wait until an inbox meets given conditions or return immediately if already met
Request, parameters, and responses
Request body (required)
| Field | Type | Required | Description |
|---|---|---|---|
inboxId | string:uuid | Yes | ID of inbox to search within and apply conditions to. Essentially filtering the emails found to give a count. |
count | integer:int32 | No | Number of results that should match conditions. Either exactly or at least this amount based on the `countType`. If count condition is not met and the timeout has not been reached the `waitFor` method will retry the operation. |
delayTimeout | integer:int64 | No | Max time in milliseconds to wait between retries if a `timeout` is specified. |
timeout | integer:int64 | Yes | Max time in milliseconds to retry the `waitFor` operation until conditions are met. |
unreadOnly | boolean | No | Apply conditions only to **unread** emails. All emails begin with `read=false`. An email is marked `read=true` when an `EmailDto` representation of it has been returned to the user at least once. For example you have called `getEmail` or `waitForLatestEmail` etc., or you have viewed the email in the dashboard. |
countType | enum: EXACTLY | ATLEAST | No | How result size should be compared with the expected size. Exactly or at-least matching result? |
matches | MatchOption[] | No | Conditions that should be matched for an email to qualify for results. Each condition will be applied in order to each email within an inbox to filter a result list of matching emails you are waiting for. |
sortDirection | enum: ASC | DESC | No | Direction to sort matching emails by created time |
since | string:date-time | No | ISO Date Time earliest time of email to consider. Filter for matching emails that were received after this date |
before | string:date-time | No | ISO Date Time latest time of email to consider. Filter for matching emails that were received before this date |
{
"inboxId": "00000000-0000-4000-8000-000000000000",
"timeout": 1,
"count": 1,
"delayTimeout": 1,
"unreadOnly": true,
"countType": "EXACTLY"
}
Responses
| Status | Schema | Description |
|---|---|---|
200 | EmailPreview[] | OK |
HTTP and SDK snippets
HTTP
POST /waitFor HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: application/json
Content-Type: application/json
{
"inboxId": "00000000-0000-4000-8000-000000000000",
"timeout": 1,
"count": 1,
"delayTimeout": 1,
"unreadOnly": true,
"countType": "EXACTLY"
}
cURL
curl -X POST "https://api.mailslurp.com/waitFor" \
-H "x-api-key: YOUR_API_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--data '{"inboxId":"00000000-0000-4000-8000-000000000000","timeout":1,"count":1,"delayTimeout":1,"unreadOnly":true,"countType":"EXACTLY"}'
JavaScript SDK
import { Configuration, WaitForControllerApi } from "mailslurp-client";
const config = new Configuration({ apiKey: "YOUR_API_KEY" });
const waitForController = new WaitForControllerApi(config);
const request = {
"waitForConditions": {
"inboxId": "00000000-0000-4000-8000-000000000000",
"timeout": 1,
"count": 1,
"delayTimeout": 1,
"unreadOnly": true,
"countType": "EXACTLY"
}
};
const result = await waitForController.waitFor(request);
Python SDK
import mailslurp_client
from mailslurp_client.api.wait_for_controller_api import WaitForControllerApi
configuration = mailslurp_client.Configuration()
configuration.api_key["x-api-key"] = "YOUR_API_KEY"
with mailslurp_client.ApiClient(configuration) as api_client:
waitForController = WaitForControllerApi(api_client)
wait_for_conditions = {
"inboxId": "00000000-0000-4000-8000-000000000000",
"timeout": 1,
"count": 1,
"delayTimeout": 1,
"unreadOnly": True,
"countType": "EXACTLY"
}
result = waitForController.wait_for(wait_for_conditions)
/waitForMatchingEmails
Wait or return list of emails that match simple matching patterns
Perform a search of emails in an inbox with the given patterns. If results match expected count then return or else retry the search until results are found or timeout is reached. Match options allow simple CONTAINS or EQUALS filtering on SUBJECT, TO, BCC, CC, and FROM. See the `MatchOptions` object for options. An example payload is `{ matches: [{field: 'SUBJECT',should:'CONTAIN',value:'needle'}] }`. You can use an array of matches and they will be applied sequentially to filter out emails. If you want to perform matches and extractions of content using Regex patterns see the EmailController `getEmailContentMatch` method.
Request, parameters, and responses
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
inboxId | string:uuid | Yes | Id of the inbox we are fetching emails from |
count | integer:int32 | Yes | Number of emails to wait for. Must be greater or equal to 1 |
before | string:date-time | No | Filter for emails that were received before the given timestamp |
since | string:date-time | No | Filter for emails that were received after the given timestamp |
sort | enum: ASC | DESC | No | Sort directionValues: ASC, DESC |
delay | integer:int64 | No | Max milliseconds delay between calls |
timeout | integer:int64 | No | Max milliseconds to wait |
unreadOnly | boolean | No | Optional filter for unread only |
Request body (required)
| Field | Type | Required | Description |
|---|---|---|---|
matches | MatchOption[] | No | Zero or more match options such as `{ field: 'SUBJECT', should: 'CONTAIN', value: 'Welcome' }`. Options are additive so if one does not match the email is excluded from results |
conditions | ConditionOption[] | No | Zero or more conditions such as `{ condition: 'HAS_ATTACHMENTS', value: 'TRUE' }`. Note the values are the strings `TRUE|FALSE` not booleans. |
{
"matches": [
{
"field": "SUBJECT",
"should": "MATCH",
"value": "value"
}
],
"conditions": [
{
"condition": "HAS_ATTACHMENTS",
"value": "TRUE"
}
]
}
Responses
| Status | Schema | Description |
|---|---|---|
200 | EmailPreview[] | OK |
HTTP and SDK snippets
HTTP
POST /waitForMatchingEmails?inboxId=00000000-0000-4000-8000-000000000000&count=value&before=value HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: application/json
Content-Type: application/json
{
"matches": [
{
"field": "SUBJECT",
"should": "MATCH",
"value": "value"
}
],
"conditions": [
{
"condition": "HAS_ATTACHMENTS",
"value": "TRUE"
}
]
}
cURL
curl -X POST "https://api.mailslurp.com/waitForMatchingEmails?inboxId=00000000-0000-4000-8000-000000000000&count=value&before=value" \
-H "x-api-key: YOUR_API_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--data '{"matches":[{"field":"SUBJECT","should":"MATCH","value":"value"}],"conditions":[{"condition":"HAS_ATTACHMENTS","value":"TRUE"}]}'
JavaScript SDK
import { Configuration, WaitForControllerApi } from "mailslurp-client";
const config = new Configuration({ apiKey: "YOUR_API_KEY" });
const waitForController = new WaitForControllerApi(config);
const request = {
"inboxId": "00000000-0000-4000-8000-000000000000",
"count": null,
"before": "value",
"matchOptions": {
"matches": [
{
"field": "SUBJECT",
"should": "MATCH",
"value": "value"
}
],
"conditions": [
{
"condition": "HAS_ATTACHMENTS",
"value": "TRUE"
}
]
}
};
const result = await waitForController.waitForMatchingEmails(request);
Python SDK
import mailslurp_client
from mailslurp_client.api.wait_for_controller_api import WaitForControllerApi
configuration = mailslurp_client.Configuration()
configuration.api_key["x-api-key"] = "YOUR_API_KEY"
with mailslurp_client.ApiClient(configuration) as api_client:
waitForController = WaitForControllerApi(api_client)
match_options = {
"matches": [
{
"field": "SUBJECT",
"should": "MATCH",
"value": "value"
}
],
"conditions": [
{
"condition": "HAS_ATTACHMENTS",
"value": "TRUE"
}
]
}
result = waitForController.wait_for_matching_emails(inbox_id="00000000-0000-4000-8000-000000000000", count=NaN, before="value", match_options)
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.
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.
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.
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.
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.
/waitForSms
Wait for an SMS message to match the provided filter conditions such as body contains keyword.
Generic waitFor method that will wait until a phone number meets given conditions or return immediately if already met
Request, parameters, and responses
Request body (required)
| Field | Type | Required | Description |
|---|---|---|---|
phoneNumberId | string:uuid | Yes | ID of phone number to search within and apply conditions to. Essentially filtering the SMS found to give a count. |
limit | integer:int32 | No | Limit results |
count | integer:int64 | Yes | Number of results that should match conditions. Either exactly or at least this amount based on the `countType`. If count condition is not met and the timeout has not been reached the `waitFor` method will retry the operation. |
delayTimeout | integer:int64 | No | Max time in milliseconds to wait between retries if a `timeout` is specified. |
timeout | integer:int64 | Yes | Max time in milliseconds to retry the `waitFor` operation until conditions are met. |
unreadOnly | boolean | No | Apply conditions only to **unread** SMS. All SMS messages begin with `read=false`. An SMS is marked `read=true` when an `SMS` has been returned to the user at least once. For example you have called `getSms`, or you have viewed the SMS in the dashboard. |
countType | enum: EXACTLY | ATLEAST | No | How result size should be compared with the expected size. Exactly or at-least matching result? |
matches | SmsMatchOption[] | No | Conditions that should be matched for an SMS to qualify for results. Each condition will be applied in order to each SMS within a phone number to filter a result list of matching SMSs you are waiting for. |
sortDirection | enum: ASC | DESC | No | Direction to sort matching SMSs by created time |
since | string:date-time | No | ISO Date Time earliest time of SMS to consider. Filter for matching SMSs that were received after this date |
before | string:date-time | No | ISO Date Time latest time of SMS to consider. Filter for matching SMSs that were received before this date |
{
"phoneNumberId": "00000000-0000-4000-8000-000000000000",
"count": 1,
"timeout": 1,
"limit": 1,
"delayTimeout": 1,
"unreadOnly": true
}
Responses
| Status | Schema | Description |
|---|---|---|
200 | SmsPreview[] | OK |
HTTP and SDK snippets
HTTP
POST /waitForSms HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: application/json
Content-Type: application/json
{
"phoneNumberId": "00000000-0000-4000-8000-000000000000",
"count": 1,
"timeout": 1,
"limit": 1,
"delayTimeout": 1,
"unreadOnly": true
}
cURL
curl -X POST "https://api.mailslurp.com/waitForSms" \
-H "x-api-key: YOUR_API_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--data '{"phoneNumberId":"00000000-0000-4000-8000-000000000000","count":1,"timeout":1,"limit":1,"delayTimeout":1,"unreadOnly":true}'
JavaScript SDK
import { Configuration, WaitForControllerApi } from "mailslurp-client";
const config = new Configuration({ apiKey: "YOUR_API_KEY" });
const waitForController = new WaitForControllerApi(config);
const request = {
"waitForSmsConditions": {
"phoneNumberId": "00000000-0000-4000-8000-000000000000",
"count": 1,
"timeout": 1,
"limit": 1,
"delayTimeout": 1,
"unreadOnly": true
}
};
const result = await waitForController.waitForSms(request);
Python SDK
import mailslurp_client
from mailslurp_client.api.wait_for_controller_api import WaitForControllerApi
configuration = mailslurp_client.Configuration()
configuration.api_key["x-api-key"] = "YOUR_API_KEY"
with mailslurp_client.ApiClient(configuration) as api_client:
waitForController = WaitForControllerApi(api_client)
wait_for_sms_conditions = {
"phoneNumberId": "00000000-0000-4000-8000-000000000000",
"count": 1,
"timeout": 1,
"limit": 1,
"delayTimeout": 1,
"unreadOnly": True
}
result = waitForController.wait_for_sms(wait_for_sms_conditions)
/waitForLatestSms
Wait for the latest SMS message to match the provided filter conditions such as body contains keyword.
Wait until a phone number meets given conditions or return immediately if already met
Request, parameters, and responses
Request body (required)
| Field | Type | Required | Description |
|---|---|---|---|
phoneNumberId | string:uuid | Yes | |
timeout | integer:int64 | Yes | |
unreadOnly | boolean | No | |
before | string:date-time | No | |
since | string:date-time | No | |
sortDirection | enum: ASC | DESC | No | |
delay | integer:int64 | No |
{
"phoneNumberId": "00000000-0000-4000-8000-000000000000",
"timeout": 1,
"unreadOnly": true,
"before": "2026-06-21T00:00:00.000Z",
"since": "2026-06-21T00:00:00.000Z",
"sortDirection": "ASC"
}
Responses
| Status | Schema | Description |
|---|---|---|
200 | SmsDto | OK |
HTTP and SDK snippets
HTTP
POST /waitForLatestSms HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: application/json
Content-Type: application/json
{
"phoneNumberId": "00000000-0000-4000-8000-000000000000",
"timeout": 1,
"unreadOnly": true,
"before": "2026-06-21T00:00:00.000Z",
"since": "2026-06-21T00:00:00.000Z",
"sortDirection": "ASC"
}
cURL
curl -X POST "https://api.mailslurp.com/waitForLatestSms" \
-H "x-api-key: YOUR_API_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--data '{"phoneNumberId":"00000000-0000-4000-8000-000000000000","timeout":1,"unreadOnly":true,"before":"2026-06-21T00:00:00.000Z","since":"2026-06-21T00:00:00.000Z","sortDirection":"ASC"}'
JavaScript SDK
import { Configuration, WaitForControllerApi } from "mailslurp-client";
const config = new Configuration({ apiKey: "YOUR_API_KEY" });
const waitForController = new WaitForControllerApi(config);
const request = {
"waitForSingleSmsOptions": {
"phoneNumberId": "00000000-0000-4000-8000-000000000000",
"timeout": 1,
"unreadOnly": true,
"before": "2026-06-21T00:00:00.000Z",
"since": "2026-06-21T00:00:00.000Z",
"sortDirection": "ASC"
}
};
const result = await waitForController.waitForLatestSms(request);
Python SDK
import mailslurp_client
from mailslurp_client.api.wait_for_controller_api import WaitForControllerApi
configuration = mailslurp_client.Configuration()
configuration.api_key["x-api-key"] = "YOUR_API_KEY"
with mailslurp_client.ApiClient(configuration) as api_client:
waitForController = WaitForControllerApi(api_client)
wait_for_single_sms_options = {
"phoneNumberId": "00000000-0000-4000-8000-000000000000",
"timeout": 1,
"unreadOnly": True,
"before": "2026-06-21T00:00:00.000Z",
"since": "2026-06-21T00:00:00.000Z",
"sortDirection": "ASC"
}
result = waitForController.wait_for_latest_sms(wait_for_single_sms_options)
/sms/{smsId}/codes
Extract verification codes from an SMS
Extract one-time passcodes and verification tokens from SMS body content. Deterministic `PATTERN` extraction is available now. Use method flags to control fallback behavior for QA.
Request, parameters, and responses
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
smsId | string:uuid | Yes | ID of SMS to extract codes from |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
method | enum: AUTO | PATTERN | LLM | OCR | OCR_THEN_LLM | No | Extraction strategy for verification codes. Unsupported strategies may fall back when allowFallback is true. |
allowFallback | boolean | No | Allow fallback to deterministic pattern extraction when the selected method is unavailable. |
minLength | integer:int32 | No | Minimum code length to consider. Typical OTP values are between 4 and 8 characters. |
maxLength | integer:int32 | No | Maximum code length to consider. |
maxCandidates | integer:int32 | No | Maximum number of code candidates to return. Best candidate is also returned separately. |
customPatterns | string[] | No | Optional custom regex patterns for code extraction. Each pattern should have either one capture group for the code or match the full code directly. |
{
"method": "AUTO",
"allowFallback": true,
"minLength": 4,
"maxLength": 10,
"maxCandidates": 5,
"customPatterns": [
"value"
]
}
Responses
| Status | Schema | Description |
|---|---|---|
200 | ExtractCodesResult | OK |
HTTP and SDK snippets
HTTP
POST /sms/00000000-0000-4000-8000-000000000000/codes HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: application/json
Content-Type: application/json
{
"method": "AUTO",
"allowFallback": true,
"minLength": 4,
"maxLength": 10,
"maxCandidates": 5,
"customPatterns": [
"value"
]
}
cURL
curl -X POST "https://api.mailslurp.com/sms/00000000-0000-4000-8000-000000000000/codes" \
-H "x-api-key: YOUR_API_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--data '{"method":"AUTO","allowFallback":true,"minLength":4,"maxLength":10,"maxCandidates":5,"customPatterns":["value"]}'
JavaScript SDK
import { Configuration, SmsControllerApi } from "mailslurp-client";
const config = new Configuration({ apiKey: "YOUR_API_KEY" });
const smsController = new SmsControllerApi(config);
const request = {
"smsId": "00000000-0000-4000-8000-000000000000",
"extractCodesOptions": {
"method": "AUTO",
"allowFallback": true,
"minLength": 4,
"maxLength": 10,
"maxCandidates": 5,
"customPatterns": [
"value"
]
}
};
const result = await smsController.getSmsCodes(request);
Python SDK
import mailslurp_client
from mailslurp_client.api.sms_controller_api import SmsControllerApi
configuration = mailslurp_client.Configuration()
configuration.api_key["x-api-key"] = "YOUR_API_KEY"
with mailslurp_client.ApiClient(configuration) as api_client:
smsController = SmsControllerApi(api_client)
extract_codes_options = {
"method": "AUTO",
"allowFallback": True,
"minLength": 4,
"maxLength": 10,
"maxCandidates": 5,
"customPatterns": [
"value"
]
}
result = smsController.get_sms_codes("00000000-0000-4000-8000-000000000000", extract_codes_options)
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.
Javascript
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
);