MailSlurp logo

Phone pools for parallel tests

Documentation navigation
Search documentation

Reserve a phone number for each test worker, wait for its SMS, and release the lease without deleting the number.

View MarkdownAgent setup

A phone pool lets parallel workers borrow different numbers from an existing inventory. Each worker acquires a lease, uses the returned number for its application flow, then releases the lease. The number remains in your account for the next test.

Prepare the pool

Add phone numbers in the dashboard first. Provision enough numbers for the concurrency you intend to run. Creating a lease does not buy a number, and releasing it does not cancel the number's rental.

Create a named pool using createPhonePool or getOrCreatePhonePool. Add only the numbers intended for these tests with addPhoneNumbersToPhonePool. Keep production conversations and unrelated tests outside this pool.

POST /phone/pools/get-or-create

Get or create phone pool

Get a phone pool by name or create it if it does not exist

Request, parameters, and responses

Request body (required)

GetOrCreatePhonePoolOptions application/json
FieldTypeRequiredDescription
namestringYes
descriptionstringNo
Request example
{
  "name": "Example name",
  "description": "value"
}

Responses

StatusSchemaDescription
200PhonePoolDetailDtoOK
HTTP and SDK snippets

HTTP

HTTP
POST /phone/pools/get-or-create HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: application/json
Content-Type: application/json

{
  "name": "Example name",
  "description": "value"
}

cURL

cURL
curl -X POST "https://api.mailslurp.com/phone/pools/get-or-create" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  --data '{"name":"Example name","description":"value"}'

JavaScript SDK

JavaScript SDK
import { Configuration, PhoneControllerApi } from "mailslurp-client";

const config = new Configuration({ apiKey: "YOUR_API_KEY" });
const phoneController = new PhoneControllerApi(config);
const request = {
  "getOrCreatePhonePoolOptions": {
    "name": "Example name",
    "description": "value"
  }
};

const result = await phoneController.getOrCreatePhonePool(request);

Python SDK

Python SDK
import mailslurp_client
from mailslurp_client.api.phone_controller_api import PhoneControllerApi

configuration = mailslurp_client.Configuration()
configuration.api_key["x-api-key"] = "YOUR_API_KEY"

with mailslurp_client.ApiClient(configuration) as api_client:
    phoneController = PhoneControllerApi(api_client)
    get_or_create_phone_pool_options = {
      "name": "Example name",
      "description": "value"
    }
    result = phoneController.get_or_create_phone_pool(get_or_create_phone_pool_options)
POST /phone/pools/{poolId}/numbers

Add phone numbers to phone pool

Add one or more owned phone numbers to a pool

Request, parameters, and responses

Path parameters

NameTypeRequiredDescription
poolIdstring:uuidYes

Request body (required)

AddPhonePoolNumbersOptions application/json
FieldTypeRequiredDescription
phoneNumberIdsstring:uuid[]Yes
Request example
{
  "phoneNumberIds": [
    "00000000-0000-4000-8000-000000000000"
  ]
}

Responses

StatusSchemaDescription
200PhonePoolDetailDtoOK
HTTP and SDK snippets

HTTP

HTTP
POST /phone/pools/00000000-0000-4000-8000-000000000000/numbers HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: application/json
Content-Type: application/json

{
  "phoneNumberIds": [
    "00000000-0000-4000-8000-000000000000"
  ]
}

cURL

cURL
curl -X POST "https://api.mailslurp.com/phone/pools/00000000-0000-4000-8000-000000000000/numbers" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  --data '{"phoneNumberIds":["00000000-0000-4000-8000-000000000000"]}'

JavaScript SDK

JavaScript SDK
import { Configuration, PhoneControllerApi } from "mailslurp-client";

const config = new Configuration({ apiKey: "YOUR_API_KEY" });
const phoneController = new PhoneControllerApi(config);
const request = {
  "poolId": "00000000-0000-4000-8000-000000000000",
  "addPhonePoolNumbersOptions": {
    "phoneNumberIds": [
      "00000000-0000-4000-8000-000000000000"
    ]
  }
};

const result = await phoneController.addPhoneNumbersToPhonePool(request);

Python SDK

Python SDK
import mailslurp_client
from mailslurp_client.api.phone_controller_api import PhoneControllerApi

configuration = mailslurp_client.Configuration()
configuration.api_key["x-api-key"] = "YOUR_API_KEY"

with mailslurp_client.ApiClient(configuration) as api_client:
    phoneController = PhoneControllerApi(api_client)
    add_phone_pool_numbers_options = {
      "phoneNumberIds": [
        "00000000-0000-4000-8000-000000000000"
      ]
    }
    result = phoneController.add_phone_numbers_to_phone_pool("00000000-0000-4000-8000-000000000000", add_phone_pool_numbers_options)

Reserve, test, and release

Use Node.js 22 or later for this REST example. Set MAILSLURP_API_KEY on the test runner and provide the pool ID. exercisePhone is your application-specific browser flow: it receives the number and the timestamp from before the flow started.

const API_BASE_URL = 'https://api.mailslurp.com';

function createApi(apiKey) {
  if (!apiKey) throw new Error('Set MAILSLURP_API_KEY');

  return async function api(path, { method = 'GET', body } = {}) {
    const response = await fetch(`${API_BASE_URL}${path}`, {
      method,
      headers: {
        'x-api-key': apiKey,
        ...(body ? { 'content-type': 'application/json' } : {}),
      },
      body: body ? JSON.stringify(body) : undefined,
    });

    if (!response.ok) {
      const detail = await response.text();
      throw new Error(`MailSlurp ${method} ${path} failed (${response.status}): ${detail}`);
    }

    return response.status === 204 ? null : response.json();
  };
}
async function withPhoneLease(api, poolId, workerId, exercisePhone) {
  const lease = await api(`/phone/pools/${encodeURIComponent(poolId)}/leases`, {
    method: 'POST',
    body: {
      leaseName: `SMS test for ${workerId}`,
      leaseOwner: workerId,
      leaseDurationMillis: 10 * 60 * 1000,
      acquireTimeoutMillis: 30 * 1000,
    },
  });
  const since = new Date();

  try {
    return await exercisePhone(lease, since);
  } finally {
    await api(
      `/phone/pools/${encodeURIComponent(poolId)}/leases/${encodeURIComponent(lease.id)}`,
      { method: 'DELETE' },
    );
  }
}

Inside exercisePhone, enter lease.phoneNumber in the application, trigger the SMS, and call waitForSms or waitForLatestSms with lease.phoneNumberId. Use the supplied since, a bounded timeout, and sender/body conditions where appropriate. Extract the OTP, enter it in the application and assert the final authenticated state.

The lease duration must cover the whole browser flow, message wait and cleanup. An expired lease can make the number available to another worker, so abort work before its expiry rather than continuing with a number that may have been reassigned.

POST /phone/pools/{poolId}/leases

Acquire phone pool lease

Acquire an available phone number from the pool and mark it leased

Request, parameters, and responses

Path parameters

NameTypeRequiredDescription
poolIdstring:uuidYes

Request body (required)

AcquirePhonePoolLeaseOptions application/json
FieldTypeRequiredDescription
leaseNamestringNo
leaseOwnerstringNo
leaseDurationMillisinteger:int64No
acquireTimeoutMillisinteger:int64No
Request example
{
  "leaseName": "Example name",
  "leaseOwner": "value",
  "leaseDurationMillis": 1,
  "acquireTimeoutMillis": 1
}

Responses

StatusSchemaDescription
200PhonePoolLeaseDtoOK
HTTP and SDK snippets

HTTP

HTTP
POST /phone/pools/00000000-0000-4000-8000-000000000000/leases HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: application/json
Content-Type: application/json

{
  "leaseName": "Example name",
  "leaseOwner": "value",
  "leaseDurationMillis": 1,
  "acquireTimeoutMillis": 1
}

cURL

cURL
curl -X POST "https://api.mailslurp.com/phone/pools/00000000-0000-4000-8000-000000000000/leases" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  --data '{"leaseName":"Example name","leaseOwner":"value","leaseDurationMillis":1,"acquireTimeoutMillis":1}'

JavaScript SDK

JavaScript SDK
import { Configuration, PhoneControllerApi } from "mailslurp-client";

const config = new Configuration({ apiKey: "YOUR_API_KEY" });
const phoneController = new PhoneControllerApi(config);
const request = {
  "poolId": "00000000-0000-4000-8000-000000000000",
  "acquirePhonePoolLeaseOptions": {
    "leaseName": "Example name",
    "leaseOwner": "value",
    "leaseDurationMillis": 1,
    "acquireTimeoutMillis": 1
  }
};

const result = await phoneController.acquirePhonePoolLease(request);

Python SDK

Python SDK
import mailslurp_client
from mailslurp_client.api.phone_controller_api import PhoneControllerApi

configuration = mailslurp_client.Configuration()
configuration.api_key["x-api-key"] = "YOUR_API_KEY"

with mailslurp_client.ApiClient(configuration) as api_client:
    phoneController = PhoneControllerApi(api_client)
    acquire_phone_pool_lease_options = {
      "leaseName": "Example name",
      "leaseOwner": "value",
      "leaseDurationMillis": 1,
      "acquireTimeoutMillis": 1
    }
    result = phoneController.acquire_phone_pool_lease("00000000-0000-4000-8000-000000000000", acquire_phone_pool_lease_options)
DELETE /phone/pools/{poolId}/leases/{leaseId}

Release phone pool lease

Release an active phone pool lease

Request, parameters, and responses

Path parameters

NameTypeRequiredDescription
poolIdstring:uuidYes
leaseIdstring:uuidYes

Responses

StatusSchemaDescription
204ResponseNo Content
HTTP and SDK snippets

HTTP

HTTP
DELETE /phone/pools/00000000-0000-4000-8000-000000000000/leases/00000000-0000-4000-8000-000000000000 HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: application/json

cURL

cURL
curl -X DELETE "https://api.mailslurp.com/phone/pools/00000000-0000-4000-8000-000000000000/leases/00000000-0000-4000-8000-000000000000" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/json"

JavaScript SDK

JavaScript SDK
import { Configuration, PhoneControllerApi } from "mailslurp-client";

const config = new Configuration({ apiKey: "YOUR_API_KEY" });
const phoneController = new PhoneControllerApi(config);
const request = {
  "poolId": "00000000-0000-4000-8000-000000000000",
  "leaseId": "00000000-0000-4000-8000-000000000000"
};

const result = await phoneController.releasePhonePoolLease(request);

Python SDK

Python SDK
import mailslurp_client
from mailslurp_client.api.phone_controller_api import PhoneControllerApi

configuration = mailslurp_client.Configuration()
configuration.api_key["x-api-key"] = "YOUR_API_KEY"

with mailslurp_client.ApiClient(configuration) as api_client:
    phoneController = PhoneControllerApi(api_client)
    result = phoneController.release_phone_pool_lease("00000000-0000-4000-8000-000000000000", "00000000-0000-4000-8000-000000000000")

Pool exhaustion and test retries

acquireTimeoutMillis bounds how long acquisition can wait for a free number; it is separate from the SMS wait timeout. If acquisition fails, report a resource/setup failure and do not pick an arbitrary shared number. Reduce parallelism or add numbers to the pool.

Give leaseOwner a worker/run identifier so a failed job is traceable. A retried test needs a new timestamp and a newly acquired lease. Do not reuse an earlier OTP or assume unread state alone isolates the test. If the process crashes before cleanup, the lease expires at expiresAt.

Use the pool from a browser framework

Put lease acquisition in a fixture or setup method and release in teardown or finally. Keep the lease in the message-service object rather than the page object, which should only operate the application UI.