Scoped SMS and TOTP for agents
Documentation navigation
Assign phone numbers and authenticator devices to agent workflows with separate read, send and management permissions.
An agent connection can use the phone numbers and TOTP devices allowed by its permissions and resource scope. The operator provisions and assigns resources; the running agent discovers what it can access with agentGetCapabilities and the channel-specific listing operations.
Assign phone numbers before the agent runs
Provision numbers through the dashboard or account API and assign the intended numbers to the agent connection. A scoped agent's phone listing returns only active assigned numbers. Phone provisioning is an operator action, separate from an agent reading or replying to SMS.
/agent/phone-numbers
List assigned phone numbers
Returns only active phone numbers explicitly assigned to this agent connection.
Request, parameters, and responses
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
page | integer:int32 | No | |
size | integer:int32 | No | |
sort | enum: ASC | DESC | No | Values: ASC, DESC |
search | string | No |
Responses
| Status | Schema | Description |
|---|---|---|
200 | PageAgentPhoneNumber | OK |
HTTP examples
HTTP
GET /agent/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/agent/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/agent/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/agent/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()
/agent/phone-numbers/{phoneNumberId}/sms
List received SMS for an assigned 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 | |
size | integer:int32 | No | |
sort | enum: ASC | DESC | No | Values: ASC, DESC |
since | string:date-time | No | |
before | string:date-time | No | |
search | string | No | |
favourite | boolean | No |
Responses
| Status | Schema | Description |
|---|---|---|
200 | PageSmsProjection | OK |
HTTP examples
HTTP
GET /agent/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/agent/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/agent/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/agent/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()
Keep SMS read/history permissions separate from SMS send permission. Replying requires both because the agent reads an inbound message before sending its response. An email role or inbox assignment alone should not be assumed to grant phone access; check capabilities and the assigned resources.
Read and reply within scope
- List the assigned phones and select the intended phone ID.
- List its received SMS or message threads, using a time boundary when following a new action.
- Retrieve the selected message and verify that it belongs to the intended workflow.
- Reply only when the connection has the required permission and the application authorizes the response.
- Inspect sent history and activity to confirm the outcome.
/agent/phone-numbers/{phoneNumberId}/message-threads
List SMS threads
Request, parameters, and responses
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
phoneNumberId | string:uuid | Yes |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
page | integer:int32 | No | |
size | integer:int32 | No |
Responses
| Status | Schema | Description |
|---|---|---|
200 | PagePhoneMessageThreadProjection | OK |
HTTP examples
HTTP
GET /agent/phone-numbers/00000000-0000-4000-8000-000000000000/message-threads 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/agent/phone-numbers/00000000-0000-4000-8000-000000000000/message-threads' \
--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/agent/phone-numbers/00000000-0000-4000-8000-000000000000/message-threads", {
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/agent/phone-numbers/00000000-0000-4000-8000-000000000000/message-threads")
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()
/agent/phone-numbers/{phoneNumberId}/sms/{smsId}/reply
Reply to received SMS
Request, parameters, and responses
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
phoneNumberId | string:uuid | Yes | |
smsId | string:uuid | Yes |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
expectedConversationVersion | integer:int64 | No | |
expectedLatestMessageId | string:uuid | No |
Header parameters
| Name | Type | Required | Description |
|---|---|---|---|
Idempotency-Key | string | No |
Request body (required)
| Field | Type | Required | Description |
|---|---|---|---|
body | string | Yes |
{
"body": "Hello from MailSlurp"
}
Responses
| Status | Schema | Description |
|---|---|---|
201 | SentSmsDto | Created |
HTTP examples
HTTP
POST /agent/phone-numbers/00000000-0000-4000-8000-000000000000/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/agent/phone-numbers/00000000-0000-4000-8000-000000000000/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/agent/phone-numbers/00000000-0000-4000-8000-000000000000/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/agent/phone-numbers/00000000-0000-4000-8000-000000000000/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()
/agent/phone-numbers/{phoneNumberId}/sms
Send SMS from an assigned phone number
Request, parameters, and responses
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
phoneNumberId | string:uuid | Yes |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
expectedConversationVersion | integer:int64 | No | |
expectedLatestMessageId | string:uuid | No |
Header parameters
| Name | Type | Required | Description |
|---|---|---|---|
Idempotency-Key | string | No |
Request body (required)
| Field | Type | Required | Description |
|---|---|---|---|
to | string | Yes | |
body | string | Yes |
{
"to": "+15551234567",
"body": "Hello from MailSlurp"
}
Responses
| Status | Schema | Description |
|---|---|---|
201 | SentSmsDto | Created |
HTTP examples
HTTP
POST /agent/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/agent/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/agent/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/agent/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()
For supervised SMS conversations, use the SMS claim, renewal and handoff operations. Do not reuse email conversation IDs or assume an email claim also reserves a phone.
/agent/sms-conversations/{conversationId}/claims
Acquire an SMS conversation claim
Request, parameters, and responses
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
conversationId | string:uuid | Yes |
Request body (required)
| Field | Type | Required | Description |
|---|---|---|---|
workerId | string | Yes | |
leaseSeconds | integer:int32 | Yes | |
expectedVersion | integer:int64 | No |
{
"workerId": "00000000-0000-4000-8000-000000000000",
"leaseSeconds": 1,
"expectedVersion": 1
}
Responses
| Status | Schema | Description |
|---|---|---|
201 | AgentConversationClaimDto | Created |
HTTP examples
HTTP
POST /agent/sms-conversations/00000000-0000-4000-8000-000000000000/claims HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Content-Type: application/json
Accept: */*
{
"workerId": "value",
"leaseSeconds": 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 "workerId": "value",\0012 "leaseSeconds": 5\0012}'
} > "$request_body"
status=$(curl --silent --show-error \
--request POST \
--url 'https://api.mailslurp.com/agent/sms-conversations/00000000-0000-4000-8000-000000000000/claims' \
--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 \"workerId\": \"value\",\n \"leaseSeconds\": 5\n}", "utf8")
]);
const response = await fetch("https://api.mailslurp.com/agent/sms-conversations/00000000-0000-4000-8000-000000000000/claims", {
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/agent/sms-conversations/00000000-0000-4000-8000-000000000000/claims")
headers = {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
"Accept": "*/*"
}
body = b"".join([
"{\n \"workerId\": \"value\",\n \"leaseSeconds\": 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()
/agent/sms-conversations/{conversationId}/claims/{claimId}
Renew an SMS conversation claim
Request, parameters, and responses
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
conversationId | string:uuid | Yes | |
claimId | string:uuid | Yes |
Request body (required)
| Field | Type | Required | Description |
|---|---|---|---|
leaseSeconds | integer:int32 | Yes |
{
"leaseSeconds": 1
}
Responses
| Status | Schema | Description |
|---|---|---|
200 | AgentConversationClaimDto | OK |
HTTP examples
HTTP
PUT /agent/sms-conversations/00000000-0000-4000-8000-000000000000/claims/00000000-0000-4000-8000-000000000000 HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Content-Type: application/json
Accept: */*
{
"leaseSeconds": 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 "leaseSeconds": 5\0012}'
} > "$request_body"
status=$(curl --silent --show-error \
--request PUT \
--url 'https://api.mailslurp.com/agent/sms-conversations/00000000-0000-4000-8000-000000000000/claims/00000000-0000-4000-8000-000000000000' \
--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 \"leaseSeconds\": 5\n}", "utf8")
]);
const response = await fetch("https://api.mailslurp.com/agent/sms-conversations/00000000-0000-4000-8000-000000000000/claims/00000000-0000-4000-8000-000000000000", {
method: "PUT",
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/agent/sms-conversations/00000000-0000-4000-8000-000000000000/claims/00000000-0000-4000-8000-000000000000")
headers = {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
"Accept": "*/*"
}
body = b"".join([
"{\n \"leaseSeconds\": 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("PUT", 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()
/agent/sms-conversations/{conversationId}/handoffs
Request human review for an SMS conversation
Request, parameters, and responses
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
conversationId | string:uuid | Yes |
Request body (required)
| Field | Type | Required | Description |
|---|---|---|---|
reasonCode | string | Yes | |
reason | string | No | |
priority | enum: LOW | NORMAL | HIGH | URGENT | Yes | Values: LOW, NORMAL, HIGH, URGENT |
sourceSmsId | string:uuid | No | |
expectedVersion | integer:int64 | No |
{
"reasonCode": "value",
"priority": "LOW",
"reason": "value",
"sourceSmsId": "00000000-0000-4000-8000-000000000000",
"expectedVersion": 1
}
Responses
| Status | Schema | Description |
|---|---|---|
201 | AgentConversationHandoffDto | Created |
HTTP examples
HTTP
POST /agent/sms-conversations/00000000-0000-4000-8000-000000000000/handoffs HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Content-Type: application/json
Accept: */*
{
"reasonCode": "value",
"priority": "LOW"
}
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 "reasonCode": "value",\0012 "priority": "LOW"\0012}'
} > "$request_body"
status=$(curl --silent --show-error \
--request POST \
--url 'https://api.mailslurp.com/agent/sms-conversations/00000000-0000-4000-8000-000000000000/handoffs' \
--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 \"reasonCode\": \"value\",\n \"priority\": \"LOW\"\n}", "utf8")
]);
const response = await fetch("https://api.mailslurp.com/agent/sms-conversations/00000000-0000-4000-8000-000000000000/handoffs", {
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/agent/sms-conversations/00000000-0000-4000-8000-000000000000/handoffs")
headers = {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
"Accept": "*/*"
}
body = b"".join([
"{\n \"reasonCode\": \"value\",\n \"priority\": \"LOW\"\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 an authenticator device
For applications using TOTP, list or find an agent-accessible device, then generate a code for the authorized authentication step. With the appropriate permissions, agent-managed devices can be created from a base32 secret or an otpauth URL.
/agent/totp/devices
List agent-accessible TOTP devices
Request, parameters, and responses
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
page | integer:int32 | No | |
size | integer:int32 | No | |
sort | enum: ASC | DESC | No | Values: ASC, DESC |
issuer | string | No | |
username | string | No |
Responses
| Status | Schema | Description |
|---|---|---|
200 | PageTotpDeviceProjection | OK |
HTTP examples
HTTP
GET /agent/totp/devices 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/agent/totp/devices' \
--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/agent/totp/devices", {
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/agent/totp/devices")
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()
/agent/totp/devices/otp-auth-url
Create an agent-managed TOTP device from an OTP Auth URL
Request, parameters, and responses
Request body (required)
| Field | Type | Required | Description |
|---|---|---|---|
otpAuthUrl | string | Yes | OTP Auth URI for connecting a TOTP device. |
name | string | No | Name for labeling the TOTP device |
username | string | No | Optional username for the TOTP device |
issuer | string | No | Optional issuer override for the TOTP device |
digits | integer:int32 | No | Optional number of digits in TOTP code |
period | integer:int32 | No | Optional period in seconds for TOTP code expiration |
algorithm | string | No | Optional algorithm override |
{
"otpAuthUrl": "otpauth://totp/your-app:contact%40mailslurp.dev?secret=LJCTOYKGGBAWCMSHJRZGITCVLIXCG4JY&issuer=your-app&algorithm=SHA1&digits=6&period=30",
"name": "Example name",
"username": "Example name",
"issuer": "value",
"digits": 6,
"period": 30
}
Responses
| Status | Schema | Description |
|---|---|---|
201 | TotpDeviceDto | Created |
HTTP examples
HTTP
POST /agent/totp/devices/otp-auth-url HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Content-Type: application/json
Accept: */*
{
"otpAuthUrl": "otpauth://totp/your-app:contact%40mailslurp.dev?secret=LJCTOYKGGBAWCMSHJRZGITCVLIXCG4JY&issuer=your-app&algorithm=SHA1&digits=6&period=30",
"digits": 6,
"period": 30
}
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 "otpAuthUrl": "otpauth://totp/your-app:contact%40mailslurp.dev?secret=LJCTOYKGGBAWCMSHJRZGITCVLIXCG4JY&issuer=your-app&algorithm=SHA1&digits=6&period=30",\0012 "digits": 6,\0012 "period": 30\0012}'
} > "$request_body"
status=$(curl --silent --show-error \
--request POST \
--url 'https://api.mailslurp.com/agent/totp/devices/otp-auth-url' \
--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 \"otpAuthUrl\": \"otpauth://totp/your-app:contact%40mailslurp.dev?secret=LJCTOYKGGBAWCMSHJRZGITCVLIXCG4JY&issuer=your-app&algorithm=SHA1&digits=6&period=30\",\n \"digits\": 6,\n \"period\": 30\n}", "utf8")
]);
const response = await fetch("https://api.mailslurp.com/agent/totp/devices/otp-auth-url", {
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/agent/totp/devices/otp-auth-url")
headers = {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json",
"Accept": "*/*"
}
body = b"".join([
"{\n \"otpAuthUrl\": \"otpauth://totp/your-app:contact%40mailslurp.dev?secret=LJCTOYKGGBAWCMSHJRZGITCVLIXCG4JY&issuer=your-app&algorithm=SHA1&digits=6&period=30\",\n \"digits\": 6,\n \"period\": 30\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()
/agent/totp/devices/{id}/code
Generate a code for an agent-managed TOTP device
Request, parameters, and responses
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string:uuid | Yes |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
at | string:date-time | No | |
minSecondsUntilExpire | integer:int32 | No | Minimum remaining code lifetime. The request may wait for the next period when needed. |
Responses
| Status | Schema | Description |
|---|---|---|
200 | TotpDeviceCodeDto | OK |
HTTP examples
HTTP
GET /agent/totp/devices/00000000-0000-4000-8000-000000000000/code 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/agent/totp/devices/00000000-0000-4000-8000-000000000000/code' \
--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/agent/totp/devices/00000000-0000-4000-8000-000000000000/code", {
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/agent/totp/devices/00000000-0000-4000-8000-000000000000/code")
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()
Treat the enrollment secret and generated code as credentials. Keep them out of shared logs, generate a fresh code near the submission step and verify the application's authenticated state. See TOTP testing for the enrollment and verification flow.
Choose the correct credential
Use a scoped agent key for the operations documented here. An account key belongs in a separate provisioning service when account-level setup is required. For browser-test suites using ordinary account APIs, see SMS waits and phone pool leases. Check the connected MCP client's available tool list before assuming every REST operation is exposed as an MCP tool.
Running this code needs an API key. Get one free