MailSlurp logo

mobile

SMS API for Real Phone Numbers, OTP Tests, and Inbound Messages

Use MailSlurp SMS APIs to provision phone numbers, receive and send texts, wait for OTP codes, and route inbound messages with webhooks.

An SMS test needs to follow the message beyond a successful send request. For a login code, that means receiving the current text and using its code to sign in to the intended account. For an inbound reply, it means checking that the application processes the right message once.

MailSlurp gives developers and QA teams real phone numbers with APIs for reading messages, waiting for SMS and receiving webhook events. Assign a number to the test or application, trigger the message through your sending service, then inspect the received SMS and complete the action it supports.

What you can do with the MailSlurp SMS API

  • Assign phone numbers: use a provisioned number in a supported region for your test account or inbound workflow.
  • Read received SMS: retrieve a message by its ID to inspect its sender, body and arrival details.
  • Wait for a message: use a bounded wait after the application action that sends an OTP or notification.
  • Receive events: send new-message notifications to your HTTP endpoint with SMS webhooks.
  • Send replies: use the sending API for approved, consent-based messages from a provisioned number.
  • Control inbound traffic: use rulesets to allow or block selected numbers or patterns.

Choose the right SMS workflow

Test an OTP or verification journey

Use a controlled phone number and a wait call. Trigger the product's real signup or login action, wait for the code, submit it, and assert the verified state. See SMS and OTP verification API for a verification workflow overview.

Process inbound messages in an application

Use a webhook for the primary event path and retain API access for inspection and reconciliation. See receive SMS API for polling, wait, and webhook tradeoffs.

Send a reply or transactional message

Use a provisioned MailSlurp number and follow consent rules. By default, outbound SMS is restricted to numbers that have initiated contact. See send SMS API for the exact setup.

Provision and identify phone numbers

Provision a phone number in the dashboard before running the example. Check current region, number availability and account access there. Each phone has a displayed number and a MailSlurp ID: enter the number in your application, and use the ID in MailSlurp API calls.

Set MAILSLURP_PHONE_NUMBER_ID to the ID assigned to this test and keep MAILSLURP_API_KEY in the test environment. The setup below retrieves that phone; retrieving it does not reserve it. Keep other workers and test suites off that number until this flow finishes. If two workers pick the first listed number, either one can read the other's message.

import { MailSlurp } from 'mailslurp-client';

const apiKey = process.env.MAILSLURP_API_KEY;
const phoneNumberId = process.env.MAILSLURP_PHONE_NUMBER_ID;
if (!apiKey || !phoneNumberId) {
  throw new Error('Set an API key and the ID of your assigned test number');
}
const mailslurp = new MailSlurp({ apiKey });
const phoneNumber = await mailslurp.phoneController.getPhoneNumber({ phoneNumberId });
// Give phoneNumber.phoneNumber to your app. Use phoneNumber.id in API calls.

For parallel runs, use phone-pool leases to reserve a different number for each active flow. Release the lease in teardown without deleting the provisioned number. If no lease is available, report the setup failure rather than sharing a busy number.

If your application uses a short-code sender, check the region and number type before building the test around it. A local code test cannot establish that a particular carrier route will deliver the SMS.

Use a phone number API in a QA fixture

Set up the receiving number before opening the signup or login page. Associate its ID with the test user and worker, and verify that the returned country matches the market the test is exercising. A phone-pool lease supplies exclusive access; fetching the number merely checks that the configured ID exists and is accessible to the API key.

Keep the sender on the application's normal path. For example, a signup test should enter the assigned number and click the application's verification action, which invokes its configured SMS provider. Calling a separate send API from the test would skip the application's template, challenge creation, and provider integration.

QA check How to use the number
Signup or SMS MFA Assign one phone to the test user and receive the challenge generated by the application.
Phone-number change Use two controlled numbers and verify that a challenge for the old one cannot verify the replacement.
Parallel browser tests Lease one number per active flow; fail setup when the pool is exhausted rather than borrowing a busy number.
Formatting and invalid-input checks Exercise the input rules without sending SMS when the application should reject the value first.

Release leases in the fixture's teardown even when an assertion fails. Keep the rental active when the number belongs to reusable test inventory, and reset the application's test user separately. A replacement test should start only after the previous flow's outstanding SMS requests have completed. The phone verification guide covers the account-state assertions around the receiving API.

Read an inbound SMS

Use the SMS message ID returned by a wait or list operation to fetch that specific message. The helper returns the message for inspection; reading it should not delete the number's other messages.

import { MailSlurp } from 'mailslurp-client';

export async function readSms(mailslurp: MailSlurp, smsId: string) {
  return mailslurp.smsController.getSmsMessage({ smsId });
}
// Inspect the returned message in your test; avoid logging its body or OTP.
// Reading a message does not release or cancel the phone number.

Check the received sender and body against the message your application was meant to send. For an OTP, finding digits is only extraction: submit the code to your application and assert the resulting account state. Keep the full body and code out of shared diagnostic output.

Wait for the expected message

For a signup or login test, capture the time immediately before the action that requests the SMS. Wait within the assigned phone using that boundary and a bounded timeout. The boundary excludes messages timestamped before the request. An older request can still produce a delayed SMS after that boundary, so do not reuse a number while a previous request is outstanding. Use an application-provided request identifier for correlation when the workflow supports one.

The helper below expects the text Your code: followed by exactly six digits. It keeps the code as a string, including any leading zero. Adapt that exact format to the SMS your application sends.

Supply three application actions: requestCode(number) must await the request and reject if it fails; submitCode(code) enters the delivered value; assertSignedIn() checks that the intended account is signed in and rejects if it is not. These functions belong to your test suite, not the MailSlurp SDK. Add the expected-sender and request-correlation checks your application requires before submitting the code; the sample's regular expression checks body format only. A successful SMS read cannot replace the final account assertion.

import { MailSlurp } from 'mailslurp-client';

export async function checkSmsLogin(
  mailslurp: MailSlurp,
  phoneNumber: { id: string; phoneNumber: string },
  actions: {
    requestCode: (number: string) => Promise<void>;
    submitCode: (code: string) => Promise<void>;
    assertSignedIn: () => Promise<void>;
  },
) {
  // Reserve this number exclusively for this attempt before calling the helper.
  const since = new Date();
  await actions.requestCode(phoneNumber.phoneNumber);
  const sms = await mailslurp.waitController.waitForLatestSms({
    waitForSingleSmsOptions: {
      phoneNumberId: phoneNumber.id,
      since,
      timeout: 60_000,
      unreadOnly: false,
    },
  });
  // Adapt this exact template to the SMS your application sends.
  const match = /^Your code: ([0-9]{6})$/.exec(sms.body ?? '');
  if (!match) throw new Error('SMS did not match the expected OTP template');
  await actions.submitCode(match[1]);
  await actions.assertSignedIn();
}

Keep the enclosing test timeout long enough for setup, the SMS wait, account verification and cleanup. Reuse the message selected inside the helper instead of repeating a broad latest-message wait. An unread-only filter can change the result when someone opens the SMS while debugging. Exclusive number use prevents concurrent workers from sharing the same message stream; the timestamp bounds which messages can match, but cannot prove which application request caused a late delivery.

On failure, retain the stage, phone ID, SMS ID when available, trigger time and elapsed wait. Exclude the OTP, full body and raw error payload. The wait troubleshooting guide separates missing delivery from a filter mismatch or an application-rejected code.

Route inbound SMS with webhooks

Create a webhook for the phone number when each new message should be sent to your server. In this standalone example, set MAILSLURP_SMS_WEBHOOK_URL to your receiving HTTPS endpoint, alongside the API key and assigned phone ID.

import { MailSlurp, CreateWebhookOptionsEventNameEnum } from 'mailslurp-client';

const apiKey = process.env.MAILSLURP_API_KEY;
const phoneNumberId = process.env.MAILSLURP_PHONE_NUMBER_ID;
const webhookUrl = process.env.MAILSLURP_SMS_WEBHOOK_URL;
if (!apiKey || !phoneNumberId || !webhookUrl) {
  throw new Error('Set the API key, phone number ID, and webhook URL');
}
const mailslurp = new MailSlurp({ apiKey });
const webhook = await mailslurp.webhookController.createWebhookForPhoneNumber({
  phoneNumberId,
  createWebhookOptions: {
    eventName: CreateWebhookOptionsEventNameEnum.NEW_SMS,
    url: webhookUrl,
    name: 'Process test SMS',
  },
});
// Retain webhook.id to manage this subscription; create it once per endpoint.

Keep webhook receipt and downstream processing separate. Associate the notification and SMS identifiers with your application's job so a failed route can be investigated. Store any message content needed for processing in the application's controlled data store; do not copy the whole webhook payload into CI logs. The SMS webhook guide explains receipt, deduplication and recovery.

Send an SMS

MailSlurp can send an SMS from a provisioned phone number. Only message recipients who have consented to be contacted. The default account policy permits sending to numbers that initiated contact with your number; contact MailSlurp support if an approved use case needs a different policy.

Set MAILSLURP_SMS_TO to the consented destination number in international format. The source is the assigned MailSlurp phone ID, as in the other examples.

import { MailSlurp } from 'mailslurp-client';

const apiKey = process.env.MAILSLURP_API_KEY;
const phoneNumberId = process.env.MAILSLURP_PHONE_NUMBER_ID;
const to = process.env.MAILSLURP_SMS_TO;
if (!apiKey || !phoneNumberId || !to) {
  throw new Error('Set the API key, sending phone ID, and consented destination');
}
const mailslurp = new MailSlurp({ apiKey });
const sent = await mailslurp.phoneController.sendSmsFromPhoneNumber({
  phoneNumberId,
  smsSendOptions: {
    to,
    body: 'Your test notification has arrived',
  },
});
// A successful request is followed by a receiving-side check in your test.

For OTP and notification products, test outbound behavior against controlled destinations before using it in a customer workflow. Check that the text arrives and that its code, link or reply completes the action your application expects.

A reliable end-to-end SMS test

  1. Assign an exclusively used number or acquire a phone-pool lease.
  2. Capture the request time, then trigger the same action a customer takes.
  3. Wait for the current SMS with a bounded timeout.
  4. Check the selected message and extract the expected code or reply.
  5. Submit it and assert the intended account or application result.
  6. Retain only the diagnostic fields needed to identify a failure.
  7. Release any lease and clean up the application's test account according to your suite's lifecycle.

The Playwright SMS OTP guide shows this flow in a browser test. Use programmable email and SMS APIs when the same journey includes an email code, link, or fallback.

FAQ

Does MailSlurp provide real phone numbers?

Yes. You can provision phone numbers in supported regions and control them from the dashboard or API.

Can MailSlurp receive SMS in automated tests?

Yes. Fetch an existing message or use a wait method to pause the test until the expected SMS arrives.

Can MailSlurp send SMS?

Yes, for consent-based workflows. Outbound access is restricted by default to destinations that initiated contact; contact support for other approved requirements.

Can I test email and SMS in one user journey?

Yes. MailSlurp provides programmable inboxes and phone numbers, so one test suite can cover SMS OTP, email OTP, magic links, password resets, and cross-channel recovery.