Use inbound TXT/SMS
Documentation navigation
Create MailSlurp phone numbers, send and receive SMS, wait for OTP codes, and test mobile messaging workflows from the dashboard or API.
MailSlurp supports creation and management of real mobile phone numbers in multiple regions. Phone numbers can receive inbound SMS text messages that can be read and received in code, tests, or online.
A common use for SMS is testing OTP codes. See the tutorials for examples:
Set up the SDK
The phone examples on this page use the MailSlurp SDK for JavaScript, Python, Java or C#. Choose a language once and every example on the page switches to it. Install the SDK for your language, set MAILSLURP_API_KEY in your server or test environment, then create a client:
JavaScript
import { MailSlurp } from 'mailslurp-client';
export function createClient() {
const apiKey = process.env.MAILSLURP_API_KEY;
if (!apiKey) throw new Error('Set MAILSLURP_API_KEY');
return new MailSlurp({ apiKey });
}
Python
import os
import mailslurp_client
def create_client():
api_key = os.environ.get("MAILSLURP_API_KEY")
if not api_key:
raise RuntimeError("Set MAILSLURP_API_KEY")
configuration = mailslurp_client.Configuration()
configuration.api_key["x-api-key"] = api_key
return mailslurp_client.ApiClient(configuration)
Java
import com.mailslurp.apis.*;
import com.mailslurp.clients.ApiClient;
import com.mailslurp.clients.ApiException;
import com.mailslurp.models.*;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
final class PhoneSetup {
static ApiClient createClient() {
String apiKey = System.getenv("MAILSLURP_API_KEY");
if (apiKey == null || apiKey.trim().isEmpty()) {
throw new IllegalStateException("Set MAILSLURP_API_KEY");
}
ApiClient client = new ApiClient();
client.setApiKey(apiKey);
client.setConnectTimeout(10_000);
client.setReadTimeout(75_000);
return client;
}
}
C#
using System;
using System.Collections.Generic;
using System.Linq;
using mailslurp.Api;
using mailslurp.Client;
using mailslurp.Model;
public static class PhoneSetup
{
public static Configuration CreateConfiguration()
{
var apiKey = Environment.GetEnvironmentVariable("MAILSLURP_API_KEY");
if (string.IsNullOrWhiteSpace(apiKey))
throw new InvalidOperationException("Set MAILSLURP_API_KEY");
var configuration = new Configuration { Timeout = TimeSpan.FromSeconds(75) };
configuration.ApiKey.Add("x-api-key", apiKey);
return configuration;
}
}
The complete phone examples include dependency files for each language.
Creating phone numbers
First add a phone plan to your account subscription. Then provision a phone number for the plan in the MailSlurp dashboard or using the API.
Variants
Some regions support phone number variants such as Mobile and Toll Free. These variants affect the type of number allocated on our networks. Choosing the right variant can improve receipt of SMS messages from short-code numbers.
Create in dashboard
To assign a new phone number, use the dashboard phone number page. You can select from a range of countries and pricing plans.

Create phone number programmatically
To enable API phone creation you must first have a paid plan and enable API creation on the phone number overview page in the dashboard.

Once enabled you can call the create phone number endpoints:
const phone = await mailslurp.phoneController.createPhoneNumber({
createPhoneNumberOptions: {
name: 'Sales inbound',
phoneCountry: CreatePhoneNumberOptionsPhoneCountryEnum.US,
}
})
expect(phone.phoneNumber).toContain('+1')
Info. Each create phone number call bills your account for a phone number rental of one month. This is because telecom providers only rent phone numbers for a minimum of one month.
The phone creation endpoint is the API surface for provisioning numbers after API creation is enabled for your account.
/phone
Add phone number to your account. Only works if you have already added a plan and an initial phone number in your account and acknowledged the pricing and terms of service by enabling API phone creation.
Create new phone number
Request, parameters, and responses
Request body (required)
| Field | Type | Required | Description |
|---|---|---|---|
phoneCountry | enum: US | GB | AU | CA | EE | HK | PL | CH | PT | NL | IL | FI | SE | Yes | Values: US, GB, AU, CA, EE, HK, PL, CH, PT, NL, IL, FI, SE |
name | string | No | |
description | string | No | |
tags | string[] | No | |
schedule | enum: MONTHLY | YEARLY | No | Values: MONTHLY, YEARLY |
phoneNumberEndpointOverride | string | No | |
phoneNumberVariant | enum: LOCAL | MOBILE | TOLL_FREE | No | Values: LOCAL, MOBILE, TOLL_FREE |
phoneProvider | enum: T1 | T2 | No | Values: T1, T2 |
phoneLineFilter | enum: ANY | OTP_FRIENDLY | NON_VOIP | No | Line-quality preference for simple phone number provisioningValues: ANY, OTP_FRIENDLY, NON_VOIP |
environmentAccountId | string:uuid | No |
{
"phoneCountry": "US",
"name": "Example name",
"description": "value",
"tags": [
"example"
],
"schedule": "MONTHLY",
"phoneNumberEndpointOverride": "value"
}
Responses
| Status | Schema | Description |
|---|---|---|
200 | PhoneNumberDto | OK |
HTTP examples
HTTP
POST /phone HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Content-Type: application/json
Accept: */*
{
"phoneCountry": "US"
}
cURL
Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.
(
set -eu
response_file=$(mktemp)
request_body=$(mktemp)
trap 'rm -f "$response_file" "$request_body"' EXIT
{
printf '%b' '{\0012 "phoneCountry": "US"\0012}'
} > "$request_body"
status=$(curl --silent --show-error \
--request POST \
--url 'https://api.mailslurp.com/phone' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--output "$response_file" \
--write-out '%{http_code}' \
--data-binary "@$request_body")
case "$status" in
2??) mv "$response_file" 'response.bin' ;;
*) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)
JavaScript (HTTP)
Requires Node.js 22+. Tested with Node.js 22.14.0.
import { writeFile } from "node:fs/promises";
const body = Buffer.concat([
Buffer.from("{\n \"phoneCountry\": \"US\"\n}", "utf8")
]);
const response = await fetch("https://api.mailslurp.com/phone", {
method: "POST",
headers: {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
"Accept": "*/*"
},
body,
redirect: "manual",
signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);
Python (HTTP)
Requires Python 3.9+. Tested with Python 3.9.21.
import http.client
from pathlib import Path
from urllib.parse import urlsplit
url = urlsplit("https://api.mailslurp.com/phone")
headers = {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
"Accept": "*/*"
}
body = b"".join([
"{\n \"phoneCountry\": \"US\"\n}".encode("utf-8")
])
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
path = (url.path or "/") + ("?" + url.query if url.query else "")
connection.request("POST", path, body=body, headers=headers)
response = connection.getresponse()
response_bytes = response.read()
if not 200 <= response.status < 300:
raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
Path("response.bin").write_bytes(response_bytes)
finally:
connection.close()
Listing phone numbers
Phone numbers have an ID and a number. When fetching the phone number use the ID. Use the PhoneControllerApi to manage phone numbers. List your numbers to find the ID of the one a test should use:
JavaScript
export async function listNumbers(mailslurp) {
// Phone numbers are created in the dashboard or API; list them to find the one for your test.
const page = await mailslurp.phoneController.getPhoneNumbers({ size: 20 });
return page.content.map(phone => ({ id: phone.id, phoneNumber: phone.phoneNumber, country: phone.phoneCountry }));
}
Python
def list_numbers(api_client):
# Phone numbers are created in the dashboard or API; list them to find the one for your test.
page = mailslurp_client.PhoneControllerApi(api_client).get_phone_numbers(size=20)
return [(phone.id, phone.phone_number, phone.phone_country) for phone in page.content]
Java
public static List<String> listNumbers(ApiClient client) throws ApiException {
// Phone numbers are created in the dashboard or API; list them to find the one for your test.
return new PhoneControllerApi(client).getPhoneNumbers().size(20).execute().getContent().stream()
.map(phone -> phone.getId() + " " + phone.getPhoneNumber())
.collect(Collectors.toList());
}
C#
public static List<string> ListNumbers(Configuration configuration)
{
// Phone numbers are created in the dashboard or API; list them to find the one for your test.
return new PhoneControllerApi(configuration).GetPhoneNumbers(size: 20).Content
.Select(phone => $"{phone.Id} {phone.PhoneNumber}")
.ToList();
}
You can fetch numbers directly using the getBy methods:
const phoneBy = await mailslurp.phoneController.getPhoneNumberByName({
name: 'Sales inbound'
})
View SMS messages
In the dashboard you can view TXT message threads in the phones section:

Use the SmsControllerApi to view and download SMS messages. This lists the latest messages received by one number:
JavaScript
export async function listSms(mailslurp, phoneNumberId) {
const page = await mailslurp.smsController.getAllSmsMessages({ phoneNumber: phoneNumberId, size: 20 });
return page.content.map(sms => ({ id: sms.id, from: sms.fromNumber, body: sms.body, createdAt: sms.createdAt }));
}
Python
def list_sms(api_client, phone_number_id):
page = mailslurp_client.SmsControllerApi(api_client).get_all_sms_messages(
phone_number=phone_number_id, size=20
)
return [(sms.id, sms.from_number, sms.body, sms.created_at) for sms in page.content]
Java
public static List<String> listSms(ApiClient client, UUID phoneNumberId) throws ApiException {
return new SmsControllerApi(client).getAllSmsMessages().phoneNumber(phoneNumberId).size(20).execute()
.getContent().stream()
.map(sms -> sms.getFromNumber() + ": " + sms.getBody())
.collect(Collectors.toList());
}
C#
public static List<string> ListSms(Configuration configuration, Guid phoneNumberId)
{
return new SmsControllerApi(configuration).GetAllSmsMessages(phoneNumber: phoneNumberId, size: 20).Content
.Select(sms => $"{sms.FromNumber}: {sms.Body}")
.ToList();
}
You can fetch individual messages using the message's ID.
// fetch a message
const txtMessage = await mailslurp.smsController.getSmsMessage({
smsId: sms.id // UUID of the SMS message
});
expect(txtMessage.read).toEqual(true);
expect(txtMessage.fromNumber).toEqual('+13252527014');
For tests that need to list, inspect, or parse SMS content after receipt, pair phone-number message listing with direct SMS reads and code extraction.
/phone/numbers/{phoneNumberId}/sms
List SMS messages for a phone number
Get SMS messages for a phone number
Request, parameters, and responses
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
phoneNumberId | string:uuid | Yes |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
page | integer:int32 | No | Optional page index in SMS list pagination |
size | integer:int32 | No | Optional page size in SMS list pagination. Maximum size is 100. Use page index and sort to page through larger results |
sort | enum: ASC | DESC | No | Optional createdAt sort direction ASC or DESCValues: ASC, DESC |
unreadOnly | boolean | No | Optional filter for unread SMS only. All SMS are considered unread until they are viewed in the dashboard or requested directly |
since | string:date-time | No | Optional filter SMSs received after given date time |
before | string:date-time | No | Optional filter SMSs received before given date time |
search | string | No | Optional search filter |
favourite | boolean | No | Optionally filter results for favourites only |
Responses
| Status | Schema | Description |
|---|---|---|
200 | PageSmsProjection | OK |
HTTP examples
HTTP
GET /phone/numbers/00000000-0000-4000-8000-000000000000/sms HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: */*
cURL
Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.
(
set -eu
response_file=$(mktemp)
trap 'rm -f "$response_file"' EXIT
status=$(curl --silent --show-error \
--request GET \
--url 'https://api.mailslurp.com/phone/numbers/00000000-0000-4000-8000-000000000000/sms' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Accept: */*' \
--output "$response_file" \
--write-out '%{http_code}')
case "$status" in
2??) mv "$response_file" 'response.bin' ;;
*) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)
JavaScript (HTTP)
Requires Node.js 22+. Tested with Node.js 22.14.0.
import { writeFile } from "node:fs/promises";
const response = await fetch("https://api.mailslurp.com/phone/numbers/00000000-0000-4000-8000-000000000000/sms", {
method: "GET",
headers: {
"x-api-key": "YOUR_API_KEY",
"Accept": "*/*"
},
redirect: "manual",
signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);
Python (HTTP)
Requires Python 3.9+. Tested with Python 3.9.21.
import http.client
from pathlib import Path
from urllib.parse import urlsplit
url = urlsplit("https://api.mailslurp.com/phone/numbers/00000000-0000-4000-8000-000000000000/sms")
headers = {
"x-api-key": "YOUR_API_KEY",
"Accept": "*/*"
}
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
path = (url.path or "/") + ("?" + url.query if url.query else "")
connection.request("GET", path, headers=headers)
response = connection.getresponse()
response_bytes = response.read()
if not 200 <= response.status < 300:
raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
Path("response.bin").write_bytes(response_bytes)
finally:
connection.close()
/sms/{smsId}
Get SMS content including body. Expects SMS to exist by ID. For SMS that may not have arrived yet use the WaitForController.
Returns a SMS summary object with content.
Request, parameters, and responses
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
smsId | string:uuid | Yes |
Responses
| Status | Schema | Description |
|---|---|---|
200 | SmsDto | OK |
HTTP examples
HTTP
GET /sms/00000000-0000-4000-8000-000000000000 HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: */*
cURL
Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.
(
set -eu
response_file=$(mktemp)
trap 'rm -f "$response_file"' EXIT
status=$(curl --silent --show-error \
--request GET \
--url 'https://api.mailslurp.com/sms/00000000-0000-4000-8000-000000000000' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Accept: */*' \
--output "$response_file" \
--write-out '%{http_code}')
case "$status" in
2??) mv "$response_file" 'response.bin' ;;
*) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)
JavaScript (HTTP)
Requires Node.js 22+. Tested with Node.js 22.14.0.
import { writeFile } from "node:fs/promises";
const response = await fetch("https://api.mailslurp.com/sms/00000000-0000-4000-8000-000000000000", {
method: "GET",
headers: {
"x-api-key": "YOUR_API_KEY",
"Accept": "*/*"
},
redirect: "manual",
signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);
Python (HTTP)
Requires Python 3.9+. Tested with Python 3.9.21.
import http.client
from pathlib import Path
from urllib.parse import urlsplit
url = urlsplit("https://api.mailslurp.com/sms/00000000-0000-4000-8000-000000000000")
headers = {
"x-api-key": "YOUR_API_KEY",
"Accept": "*/*"
}
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
path = (url.path or "/") + ("?" + url.query if url.query else "")
connection.request("GET", path, headers=headers)
response = connection.getresponse()
response_bytes = response.read()
if not 200 <= response.status < 300:
raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
Path("response.bin").write_bytes(response_bytes)
finally:
connection.close()
/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.Values: AUTO, PATTERN, LLM, OCR, OCR_THEN_LLM |
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 examples
HTTP
POST /sms/00000000-0000-4000-8000-000000000000/codes HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Content-Type: application/json
Accept: */*
{
"allowFallback": true,
"minLength": 4,
"maxLength": 10,
"maxCandidates": 5
}
cURL
Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.
(
set -eu
response_file=$(mktemp)
request_body=$(mktemp)
trap 'rm -f "$response_file" "$request_body"' EXIT
{
printf '%b' '{\0012 "allowFallback": true,\0012 "minLength": 4,\0012 "maxLength": 10,\0012 "maxCandidates": 5\0012}'
} > "$request_body"
status=$(curl --silent --show-error \
--request POST \
--url 'https://api.mailslurp.com/sms/00000000-0000-4000-8000-000000000000/codes' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--output "$response_file" \
--write-out '%{http_code}' \
--data-binary "@$request_body")
case "$status" in
2??) mv "$response_file" 'response.bin' ;;
*) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)
JavaScript (HTTP)
Requires Node.js 22+. Tested with Node.js 22.14.0.
import { writeFile } from "node:fs/promises";
const body = Buffer.concat([
Buffer.from("{\n \"allowFallback\": true,\n \"minLength\": 4,\n \"maxLength\": 10,\n \"maxCandidates\": 5\n}", "utf8")
]);
const response = await fetch("https://api.mailslurp.com/sms/00000000-0000-4000-8000-000000000000/codes", {
method: "POST",
headers: {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
"Accept": "*/*"
},
body,
redirect: "manual",
signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);
Python (HTTP)
Requires Python 3.9+. Tested with Python 3.9.21.
import http.client
from pathlib import Path
from urllib.parse import urlsplit
url = urlsplit("https://api.mailslurp.com/sms/00000000-0000-4000-8000-000000000000/codes")
headers = {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
"Accept": "*/*"
}
body = b"".join([
"{\n \"allowFallback\": true,\n \"minLength\": 4,\n \"maxLength\": 10,\n \"maxCandidates\": 5\n}".encode("utf-8")
])
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
path = (url.path or "/") + ("?" + url.query if url.query else "")
connection.request("POST", path, body=body, headers=headers)
response = connection.getresponse()
response_bytes = response.read()
if not 200 <= response.status < 300:
raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
Path("response.bin").write_bytes(response_bytes)
finally:
connection.close()
Send SMS
You can send SMS programmatically or using the dashboard. Send from one of your numbers to an E.164 destination:
JavaScript
export async function sendSms(mailslurp, fromPhoneId, to) {
// `to` is an E.164 number, for example +15555550100.
return mailslurp.smsController.sendSms({
fromPhoneId,
smsSendOptions: { to, body: 'Your appointment is confirmed for 3pm.' },
});
}
Python
def send_sms(api_client, from_phone_id, to):
# `to` is an E.164 number, for example +15555550100.
return mailslurp_client.SmsControllerApi(api_client).send_sms(
mailslurp_client.SmsSendOptions(to=to, body="Your appointment is confirmed for 3pm."),
from_phone_id=from_phone_id,
)
Java
public static SentSmsDto sendSms(ApiClient client, UUID fromPhoneId, String to) throws ApiException {
// `to` is an E.164 number, for example +15555550100.
return new SmsControllerApi(client).sendSms(new SmsSendOptions()
.to(to)
.body("Your appointment is confirmed for 3pm.")).fromPhoneId(fromPhoneId).execute();
}
C#
public static SentSmsDto SendSms(Configuration configuration, Guid fromPhoneId, string to)
{
// `to` is an E.164 number, for example +15555550100.
return new SmsControllerApi(configuration).SendSms(
new SmsSendOptions(to: to, body: "Your appointment is confirmed for 3pm."), fromPhoneId: fromPhoneId);
}
Info. Telecom providers impose some regulations on sending. Only send to numbers that have explicitly consented to being contacted. By default MailSlurp only permits sending to numbers that have initiated contact with you. To remove this restriction contact support.
Send a test message
Use the normal send endpoint with a controlled destination and content in test environments:
await mailslurp.phoneController.sendSmsFromPhoneNumber({
phoneNumberId: sendingNumber,
sendPhoneNumberOptions: {
to: '+14155551212',
message: 'Hello from Mailslurp'
}
});
Use phone-scoped sending when the sender number matters. For a received inbound message, replies can be sent from the number that received it.
/phone/numbers/{phoneNumberId}/sms
Send TXT message from a phone number
Send SMS from a phone number
Request, parameters, and responses
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
phoneNumberId | string:uuid | Yes |
Request body (required)
| Field | Type | Required | Description |
|---|---|---|---|
to | string | Yes | |
body | string | Yes |
{
"to": "+15551234567",
"body": "Hello from MailSlurp"
}
Responses
| Status | Schema | Description |
|---|---|---|
200 | SentSmsDto | OK |
HTTP examples
HTTP
POST /phone/numbers/00000000-0000-4000-8000-000000000000/sms HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Content-Type: application/json
Accept: */*
{
"to": "user@example.com",
"body": "value"
}
cURL
Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.
(
set -eu
response_file=$(mktemp)
request_body=$(mktemp)
trap 'rm -f "$response_file" "$request_body"' EXIT
{
printf '%b' '{\0012 "to": "user@example.com",\0012 "body": "value"\0012}'
} > "$request_body"
status=$(curl --silent --show-error \
--request POST \
--url 'https://api.mailslurp.com/phone/numbers/00000000-0000-4000-8000-000000000000/sms' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--output "$response_file" \
--write-out '%{http_code}' \
--data-binary "@$request_body")
case "$status" in
2??) mv "$response_file" 'response.bin' ;;
*) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)
JavaScript (HTTP)
Requires Node.js 22+. Tested with Node.js 22.14.0.
import { writeFile } from "node:fs/promises";
const body = Buffer.concat([
Buffer.from("{\n \"to\": \"user@example.com\",\n \"body\": \"value\"\n}", "utf8")
]);
const response = await fetch("https://api.mailslurp.com/phone/numbers/00000000-0000-4000-8000-000000000000/sms", {
method: "POST",
headers: {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
"Accept": "*/*"
},
body,
redirect: "manual",
signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);
Python (HTTP)
Requires Python 3.9+. Tested with Python 3.9.21.
import http.client
from pathlib import Path
from urllib.parse import urlsplit
url = urlsplit("https://api.mailslurp.com/phone/numbers/00000000-0000-4000-8000-000000000000/sms")
headers = {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
"Accept": "*/*"
}
body = b"".join([
"{\n \"to\": \"user@example.com\",\n \"body\": \"value\"\n}".encode("utf-8")
])
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
path = (url.path or "/") + ("?" + url.query if url.query else "")
connection.request("POST", path, body=body, headers=headers)
response = connection.getresponse()
response_bytes = response.read()
if not 200 <= response.status < 300:
raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
Path("response.bin").write_bytes(response_bytes)
finally:
connection.close()
/sms/{smsId}/reply
Send a reply to a received SMS message. Replies are sent from the receiving number.
Reply to an SMS message.
Request, parameters, and responses
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
smsId | string:uuid | Yes |
Request body (required)
| Field | Type | Required | Description |
|---|---|---|---|
body | string | Yes |
{
"body": "Hello from MailSlurp"
}
Responses
| Status | Schema | Description |
|---|---|---|
200 | SentSmsDto | OK |
HTTP examples
HTTP
POST /sms/00000000-0000-4000-8000-000000000000/reply HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Content-Type: application/json
Accept: */*
{
"body": "value"
}
cURL
Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.
(
set -eu
response_file=$(mktemp)
request_body=$(mktemp)
trap 'rm -f "$response_file" "$request_body"' EXIT
{
printf '%b' '{\0012 "body": "value"\0012}'
} > "$request_body"
status=$(curl --silent --show-error \
--request POST \
--url 'https://api.mailslurp.com/sms/00000000-0000-4000-8000-000000000000/reply' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--output "$response_file" \
--write-out '%{http_code}' \
--data-binary "@$request_body")
case "$status" in
2??) mv "$response_file" 'response.bin' ;;
*) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)
JavaScript (HTTP)
Requires Node.js 22+. Tested with Node.js 22.14.0.
import { writeFile } from "node:fs/promises";
const body = Buffer.concat([
Buffer.from("{\n \"body\": \"value\"\n}", "utf8")
]);
const response = await fetch("https://api.mailslurp.com/sms/00000000-0000-4000-8000-000000000000/reply", {
method: "POST",
headers: {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
"Accept": "*/*"
},
body,
redirect: "manual",
signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);
Python (HTTP)
Requires Python 3.9+. Tested with Python 3.9.21.
import http.client
from pathlib import Path
from urllib.parse import urlsplit
url = urlsplit("https://api.mailslurp.com/sms/00000000-0000-4000-8000-000000000000/reply")
headers = {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
"Accept": "*/*"
}
body = b"".join([
"{\n \"body\": \"value\"\n}".encode("utf-8")
])
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
path = (url.path or "/") + ("?" + url.query if url.query else "")
connection.request("POST", path, body=body, headers=headers)
response = connection.getresponse()
response_bytes = response.read()
if not 200 <= response.status < 300:
raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
Path("response.bin").write_bytes(response_bytes)
finally:
connection.close()
Wait for new SMS messages
Use the WaitForControllerApi waitForSms methods to hold a connection open until an expected count of SMS messages is present in a phone number
// wait for the latest unread sms
const [sms] = await mailslurp.waitController.waitForSms({
waitForSmsConditions: {
count: 1,
unreadOnly: true,
phoneNumberId: phoneNumber.id,
timeout: 30_000
}
});
// extract a code from body with regex
expect(sms.body).toContain('Your code: 123');
const [, code] = /.+:\s([0-9]{3})/.exec(sms.body);
expect(code).toEqual('123');
This is an important technique for SMS based testing. See the testing guide for more information.
Wait for the latest SMS and read its code
For a one-time passcode test, record the time before your application sends the code, wait for the next unread SMS after that time, then ask MailSlurp for the code in the message:
JavaScript
export async function waitForSms(mailslurp, phoneNumberId, since) {
// Waits until an unread SMS arrives after `since`, or throws after the timeout.
return mailslurp.waitController.waitForLatestSms({
waitForSingleSmsOptions: { phoneNumberId, timeout: 60_000, unreadOnly: true, since },
});
}
Python
def wait_for_sms(api_client, phone_number_id, since):
# Waits until an unread SMS arrives after `since`, or raises after the timeout.
return mailslurp_client.WaitForControllerApi(api_client).wait_for_latest_sms(
mailslurp_client.WaitForSingleSmsOptions(
phone_number_id=phone_number_id, timeout=60_000, unread_only=True, since=since
)
)
Java
public static SmsDto waitForSms(ApiClient client, UUID phoneNumberId, OffsetDateTime since) throws ApiException {
// Waits until an unread SMS arrives after `since`, or throws after the timeout.
return new WaitForControllerApi(client).waitForLatestSms(new WaitForSingleSmsOptions()
.phoneNumberId(phoneNumberId)
.timeout(60_000L)
.unreadOnly(true)
.since(since)).execute();
}
C#
public static SmsDto WaitForSms(Configuration configuration, Guid phoneNumberId, DateTime since)
{
// Waits until an unread SMS arrives after `since`, or throws after the timeout.
return new WaitForControllerApi(configuration).WaitForLatestSms(new WaitForSingleSmsOptions(
phoneNumberId: phoneNumberId, timeout: 60_000, unreadOnly: true, since: since));
}
JavaScript
export async function extractCode(mailslurp, smsId) {
// MailSlurp finds the one-time code in the message body.
const result = await mailslurp.smsController.getSmsCodes({ smsId });
if (!result.found) throw new Error('No code found in the SMS');
return result.code;
}
Python
def extract_code(api_client, sms_id):
# MailSlurp finds the one-time code in the message body.
result = mailslurp_client.SmsControllerApi(api_client).get_sms_codes(sms_id)
if not result.found:
raise ValueError("No code found in the SMS")
return result.code
Java
public static String extractCode(ApiClient client, UUID smsId) throws ApiException {
// MailSlurp finds the one-time code in the message body.
ExtractCodesResult result = new SmsControllerApi(client).getSmsCodes(smsId).execute();
if (!Boolean.TRUE.equals(result.getFound())) throw new IllegalStateException("No code found in the SMS");
return result.getCode();
}
C#
public static string ExtractCode(Configuration configuration, Guid smsId)
{
// MailSlurp finds the one-time code in the message body.
var result = new SmsControllerApi(configuration).GetSmsCodes(smsId);
if (!result.Found) throw new InvalidOperationException("No code found in the SMS");
return result.Code;
}
Define BLOCK and ALLOW rules
You can define rules for which phone numbers can send SMS to your phone numbers. This is done using rulesets. You can create rulesets that block or allow SMS from specific phone numbers or patterns to prevent any unwanted spam. An example looks like this:
// block SMS spam except for known numbers
const blockAll = await rulesetController.createNewRuleset({
phoneId: undefined, // account wide block
createRulesetOptions: {
action: 'BLOCK',
scope: 'RECEIVING_SMS',
target: '*'
}
})
const allowSpecific = await rulesetController.createNewRuleset({
phoneId: undefined, // account wide permit
createRulesetOptions: {
action: 'ALLOW',
scope: 'RECEIVING_SMS',
target: '+1234567890' // replace with a known number
}
})
In the app dashboard the interface can be found in the automations tab on the phone number overview:

When a test triggers an OTP or MFA flow, wait for the expected SMS instead of sleeping for a fixed delay.
/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?Values: EXACTLY, ATLEAST |
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 timeValues: ASC, DESC |
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 |
ai | AIMessageMatchOptions | No |
{
"phoneNumberId": "00000000-0000-4000-8000-000000000000",
"count": 1,
"timeout": 1,
"limit": 1,
"delayTimeout": 1,
"unreadOnly": true
}
Responses
| Status | Schema | Description |
|---|---|---|
200 | SmsPreview[] | OK |
HTTP examples
HTTP
POST /waitForSms HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Content-Type: application/json
Accept: */*
{
"phoneNumberId": "00000000-0000-4000-8000-000000000000",
"count": 1,
"timeout": 1
}
cURL
Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.
(
set -eu
response_file=$(mktemp)
request_body=$(mktemp)
trap 'rm -f "$response_file" "$request_body"' EXIT
{
printf '%b' '{\0012 "phoneNumberId": "00000000-0000-4000-8000-000000000000",\0012 "count": 1,\0012 "timeout": 1\0012}'
} > "$request_body"
status=$(curl --silent --show-error \
--request POST \
--url 'https://api.mailslurp.com/waitForSms' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--output "$response_file" \
--write-out '%{http_code}' \
--data-binary "@$request_body")
case "$status" in
2??) mv "$response_file" 'response.bin' ;;
*) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)
JavaScript (HTTP)
Requires Node.js 22+. Tested with Node.js 22.14.0.
import { writeFile } from "node:fs/promises";
const body = Buffer.concat([
Buffer.from("{\n \"phoneNumberId\": \"00000000-0000-4000-8000-000000000000\",\n \"count\": 1,\n \"timeout\": 1\n}", "utf8")
]);
const response = await fetch("https://api.mailslurp.com/waitForSms", {
method: "POST",
headers: {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
"Accept": "*/*"
},
body,
redirect: "manual",
signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);
Python (HTTP)
Requires Python 3.9+. Tested with Python 3.9.21.
import http.client
from pathlib import Path
from urllib.parse import urlsplit
url = urlsplit("https://api.mailslurp.com/waitForSms")
headers = {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
"Accept": "*/*"
}
body = b"".join([
"{\n \"phoneNumberId\": \"00000000-0000-4000-8000-000000000000\",\n \"count\": 1,\n \"timeout\": 1\n}".encode("utf-8")
])
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
path = (url.path or "/") + ("?" + url.query if url.query else "")
connection.request("POST", path, body=body, headers=headers)
response = connection.getresponse()
response_bytes = response.read()
if not 200 <= response.status < 300:
raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
Path("response.bin").write_bytes(response_bytes)
finally:
connection.close()
/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 | Values: ASC, DESC |
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 examples
HTTP
POST /waitForLatestSms HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Content-Type: application/json
Accept: */*
{
"phoneNumberId": "00000000-0000-4000-8000-000000000000",
"timeout": 1
}
cURL
Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.
(
set -eu
response_file=$(mktemp)
request_body=$(mktemp)
trap 'rm -f "$response_file" "$request_body"' EXIT
{
printf '%b' '{\0012 "phoneNumberId": "00000000-0000-4000-8000-000000000000",\0012 "timeout": 1\0012}'
} > "$request_body"
status=$(curl --silent --show-error \
--request POST \
--url 'https://api.mailslurp.com/waitForLatestSms' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--output "$response_file" \
--write-out '%{http_code}' \
--data-binary "@$request_body")
case "$status" in
2??) mv "$response_file" 'response.bin' ;;
*) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)
JavaScript (HTTP)
Requires Node.js 22+. Tested with Node.js 22.14.0.
import { writeFile } from "node:fs/promises";
const body = Buffer.concat([
Buffer.from("{\n \"phoneNumberId\": \"00000000-0000-4000-8000-000000000000\",\n \"timeout\": 1\n}", "utf8")
]);
const response = await fetch("https://api.mailslurp.com/waitForLatestSms", {
method: "POST",
headers: {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
"Accept": "*/*"
},
body,
redirect: "manual",
signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);
Python (HTTP)
Requires Python 3.9+. Tested with Python 3.9.21.
import http.client
from pathlib import Path
from urllib.parse import urlsplit
url = urlsplit("https://api.mailslurp.com/waitForLatestSms")
headers = {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
"Accept": "*/*"
}
body = b"".join([
"{\n \"phoneNumberId\": \"00000000-0000-4000-8000-000000000000\",\n \"timeout\": 1\n}".encode("utf-8")
])
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
path = (url.path or "/") + ("?" + url.query if url.query else "")
connection.request("POST", path, body=body, headers=headers)
response = connection.getresponse()
response_bytes = response.read()
if not 200 <= response.status < 300:
raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
Path("response.bin").write_bytes(response_bytes)
finally:
connection.close()
Use SMS webhooks
Have SMS messages sent via HTTP POST to your server with TXT Webhooks
// create a webhook for a phone number to have new SMS sent to your server
const webhook = await mailslurp.webhookController.createWebhookForPhoneNumber({
phoneNumberId: phoneNumber.id,
createWebhookOptions: {
eventName: CreateWebhookOptionsEventNameEnum.NEW_SMS,
url: 'https://myserver.com',
name: 'Process SMS'
}
});
Manage number ownership and reuse
Name and tag numbers by environment or purpose so tests can find their assigned inventory. Use get-by-name or get-by-number when selecting a known resource and paginate inventory reads rather than assuming the first page contains every number.
For parallel tests, use phone pools and leases. Reserve a number for the entire application flow, capture a time boundary before requesting the OTP and release the lease after verification. Deleting a rented phone number is a separate lifecycle action; it is not test-message cleanup or a lease release.
/phone/numbers
Get phone numbers
List phone numbers for account
Request, parameters, and responses
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
phoneCountry | enum: US | GB | AU | CA | EE | HK | PL | CH | PT | NL | IL | FI | SE | No | Optional phone countryValues: US, GB, AU, CA, EE, HK, PL, CH, PT, NL, IL, FI, SE |
lineType | string | No | Optional line type filter |
carrierName | string | No | Optional carrier name filter |
mobileCountryCode | string | No | Optional mobile country code filter |
mobileNetworkCode | string | No | Optional mobile network code filter |
providerLabel | string | No | Optional provider label filter such as T1 or T2 |
page | integer:int32 | No | Optional page index for list pagination |
size | integer:int32 | No | Optional page size for list pagination |
sort | enum: ASC | DESC | No | Optional createdAt sort direction ASC or DESCValues: ASC, DESC |
since | string:date-time | No | Filter by created at after the given timestamp |
before | string:date-time | No | Filter by created at before the given timestamp |
search | string | No | Optional search filter |
tag | string[] | No | Optional tags to filter by. A phone must include all provided tags |
include | string:uuid[] | No | Optional phoneIds to include in result |
favourite | boolean | No | Optionally filter results for favourites only |
Responses
| Status | Schema | Description |
|---|---|---|
200 | PagePhoneNumberProjection | OK |
HTTP examples
HTTP
GET /phone/numbers HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: */*
cURL
Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.
(
set -eu
response_file=$(mktemp)
trap 'rm -f "$response_file"' EXIT
status=$(curl --silent --show-error \
--request GET \
--url 'https://api.mailslurp.com/phone/numbers' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Accept: */*' \
--output "$response_file" \
--write-out '%{http_code}')
case "$status" in
2??) mv "$response_file" 'response.bin' ;;
*) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)
JavaScript (HTTP)
Requires Node.js 22+. Tested with Node.js 22.14.0.
import { writeFile } from "node:fs/promises";
const response = await fetch("https://api.mailslurp.com/phone/numbers", {
method: "GET",
headers: {
"x-api-key": "YOUR_API_KEY",
"Accept": "*/*"
},
redirect: "manual",
signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);
Python (HTTP)
Requires Python 3.9+. Tested with Python 3.9.21.
import http.client
from pathlib import Path
from urllib.parse import urlsplit
url = urlsplit("https://api.mailslurp.com/phone/numbers")
headers = {
"x-api-key": "YOUR_API_KEY",
"Accept": "*/*"
}
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
path = (url.path or "/") + ("?" + url.query if url.query else "")
connection.request("GET", path, headers=headers)
response = connection.getresponse()
response_bytes = response.read()
if not 200 <= response.status < 300:
raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
Path("response.bin").write_bytes(response_bytes)
finally:
connection.close()
/phone/provisioning/capabilities
Get phone provisioning capabilities
Get supported provider-country variant capabilities for advanced provisioning
Request, parameters, and responses
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
phoneCountry | enum: US | GB | AU | CA | EE | HK | PL | CH | PT | NL | IL | FI | SE | Yes | Values: US, GB, AU, CA, EE, HK, PL, CH, PT, NL, IL, FI, SE |
providerLabel | string | No |
Responses
| Status | Schema | Description |
|---|---|---|
200 | PhoneProviderCapabilitiesResult | OK |
HTTP examples
HTTP
GET /phone/provisioning/capabilities?phoneCountry=US HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: */*
cURL
Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.
(
set -eu
response_file=$(mktemp)
trap 'rm -f "$response_file"' EXIT
status=$(curl --silent --show-error \
--request GET \
--url 'https://api.mailslurp.com/phone/provisioning/capabilities?phoneCountry=US' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Accept: */*' \
--output "$response_file" \
--write-out '%{http_code}')
case "$status" in
2??) mv "$response_file" 'response.bin' ;;
*) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)
JavaScript (HTTP)
Requires Node.js 22+. Tested with Node.js 22.14.0.
import { writeFile } from "node:fs/promises";
const response = await fetch("https://api.mailslurp.com/phone/provisioning/capabilities?phoneCountry=US", {
method: "GET",
headers: {
"x-api-key": "YOUR_API_KEY",
"Accept": "*/*"
},
redirect: "manual",
signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);
Python (HTTP)
Requires Python 3.9+. Tested with Python 3.9.21.
import http.client
from pathlib import Path
from urllib.parse import urlsplit
url = urlsplit("https://api.mailslurp.com/phone/provisioning/capabilities?phoneCountry=US")
headers = {
"x-api-key": "YOUR_API_KEY",
"Accept": "*/*"
}
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
path = (url.path or "/") + ("?" + url.query if url.query else "")
connection.request("GET", path, headers=headers)
response = connection.getresponse()
response_bytes = response.read()
if not 200 <= response.status < 300:
raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
Path("response.bin").write_bytes(response_bytes)
finally:
connection.close()
/phone/provisioning/search
Search available phone numbers
Search available numbers for advanced provisioning
Request, parameters, and responses
Request body (required)
| Field | Type | Required | Description |
|---|---|---|---|
phoneCountry | enum: US | GB | AU | CA | EE | HK | PL | CH | PT | NL | IL | FI | SE | Yes | Values: US, GB, AU, CA, EE, HK, PL, CH, PT, NL, IL, FI, SE |
phoneVariant | enum: LOCAL | MOBILE | TOLL_FREE | No | Values: LOCAL, MOBILE, TOLL_FREE |
qualityFilter | enum: OTP | NON_VOIP | MOBILE_ONLY | ANY | No | Quality filter for advanced phone provisioning searchValues: OTP, NON_VOIP, MOBILE_ONLY, ANY |
lineType | string | No | |
carrierName | string | No | |
mobileCountryCode | string | No | |
mobileNetworkCode | string | No | |
providerLabels | string[] | No | |
limit | integer:int32 | No |
{
"phoneCountry": "US",
"phoneVariant": "LOCAL",
"qualityFilter": "OTP",
"lineType": "value",
"carrierName": "Example name",
"mobileCountryCode": "value"
}
Responses
| Status | Schema | Description |
|---|---|---|
200 | AvailablePhoneNumbersResult | OK |
HTTP examples
HTTP
POST /phone/provisioning/search HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Content-Type: application/json
Accept: */*
{
"phoneCountry": "US"
}
cURL
Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.
(
set -eu
response_file=$(mktemp)
request_body=$(mktemp)
trap 'rm -f "$response_file" "$request_body"' EXIT
{
printf '%b' '{\0012 "phoneCountry": "US"\0012}'
} > "$request_body"
status=$(curl --silent --show-error \
--request POST \
--url 'https://api.mailslurp.com/phone/provisioning/search' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--output "$response_file" \
--write-out '%{http_code}' \
--data-binary "@$request_body")
case "$status" in
2??) mv "$response_file" 'response.bin' ;;
*) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)
JavaScript (HTTP)
Requires Node.js 22+. Tested with Node.js 22.14.0.
import { writeFile } from "node:fs/promises";
const body = Buffer.concat([
Buffer.from("{\n \"phoneCountry\": \"US\"\n}", "utf8")
]);
const response = await fetch("https://api.mailslurp.com/phone/provisioning/search", {
method: "POST",
headers: {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
"Accept": "*/*"
},
body,
redirect: "manual",
signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);
Python (HTTP)
Requires Python 3.9+. Tested with Python 3.9.21.
import http.client
from pathlib import Path
from urllib.parse import urlsplit
url = urlsplit("https://api.mailslurp.com/phone/provisioning/search")
headers = {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
"Accept": "*/*"
}
body = b"".join([
"{\n \"phoneCountry\": \"US\"\n}".encode("utf-8")
])
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
path = (url.path or "/") + ("?" + url.query if url.query else "")
connection.request("POST", path, body=body, headers=headers)
response = connection.getresponse()
response_bytes = response.read()
if not 200 <= response.status < 300:
raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
Path("response.bin").write_bytes(response_bytes)
finally:
connection.close()
/phone/provisioning/jobs
Create a phone provisioning job
Create an advanced phone provisioning job from shortlisted numbers
Request, parameters, and responses
Request body (required)
| Field | Type | Required | Description |
|---|---|---|---|
phoneCountry | enum: US | GB | AU | CA | EE | HK | PL | CH | PT | NL | IL | FI | SE | Yes | Values: US, GB, AU, CA, EE, HK, PL, CH, PT, NL, IL, FI, SE |
phoneVariant | enum: LOCAL | MOBILE | TOLL_FREE | No | Values: LOCAL, MOBILE, TOLL_FREE |
items | CreatePhoneProvisioningJobItemOptions[] | Yes |
{
"phoneCountry": "US",
"items": [
{
"phoneNumber": "value",
"providerLabel": "value",
"lineType": "value",
"carrierName": "Example name",
"mobileCountryCode": "value",
"mobileNetworkCode": "value"
}
],
"phoneVariant": "LOCAL"
}
Responses
| Status | Schema | Description |
|---|---|---|
200 | PhoneProvisioningJobDto | OK |
HTTP examples
HTTP
POST /phone/provisioning/jobs HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Content-Type: application/json
Accept: */*
{
"phoneCountry": "US",
"items": [
{
"phoneNumber": "value"
}
]
}
cURL
Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.
(
set -eu
response_file=$(mktemp)
request_body=$(mktemp)
trap 'rm -f "$response_file" "$request_body"' EXIT
{
printf '%b' '{\0012 "phoneCountry": "US",\0012 "items": [\0012 {\0012 "phoneNumber": "value"\0012 }\0012 ]\0012}'
} > "$request_body"
status=$(curl --silent --show-error \
--request POST \
--url 'https://api.mailslurp.com/phone/provisioning/jobs' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--output "$response_file" \
--write-out '%{http_code}' \
--data-binary "@$request_body")
case "$status" in
2??) mv "$response_file" 'response.bin' ;;
*) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)
JavaScript (HTTP)
Requires Node.js 22+. Tested with Node.js 22.14.0.
import { writeFile } from "node:fs/promises";
const body = Buffer.concat([
Buffer.from("{\n \"phoneCountry\": \"US\",\n \"items\": [\n {\n \"phoneNumber\": \"value\"\n }\n ]\n}", "utf8")
]);
const response = await fetch("https://api.mailslurp.com/phone/provisioning/jobs", {
method: "POST",
headers: {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
"Accept": "*/*"
},
body,
redirect: "manual",
signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);
Python (HTTP)
Requires Python 3.9+. Tested with Python 3.9.21.
import http.client
from pathlib import Path
from urllib.parse import urlsplit
url = urlsplit("https://api.mailslurp.com/phone/provisioning/jobs")
headers = {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
"Accept": "*/*"
}
body = b"".join([
"{\n \"phoneCountry\": \"US\",\n \"items\": [\n {\n \"phoneNumber\": \"value\"\n }\n ]\n}".encode("utf-8")
])
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
path = (url.path or "/") + ("?" + url.query if url.query else "")
connection.request("POST", path, body=body, headers=headers)
response = connection.getresponse()
response_bytes = response.read()
if not 200 <= response.status < 300:
raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
Path("response.bin").write_bytes(response_bytes)
finally:
connection.close()
Advanced provisioning exposes available country/variant capabilities and a job workflow. Check job status before using the number. Confirm pricing and API-creation prerequisites in the dashboard before automating purchases.
Troubleshoot a missing verification text
Check the selected phone ID, application destination and country format first. Confirm the application actually requested a new code and that another test does not own the number. Keep the SMS wait bounded and filter by sender or content when the workflow has a known signature.
Use since captured before the application action to exclude old messages. unreadOnly is useful but shared dashboard reads and other workers can change read state. A resend may make a previous code invalid; assert that the application accepts the extracted code rather than only checking that an SMS exists.
Use SMS webhook recovery for asynchronous consumers, or scoped SMS access when an AI agent should see only assigned numbers.
Running this code needs an API key. Get one free