docs
v1 Pricing Dashboard
Getting started / quickstart-sms

Quickstart: SMS

2FA-by-text is usually the flow nobody automates: it works against somebody's personal phone, or a shared temp-number site that recycles numbers into someone else's test. Paid plans get dedicated US phone numbers instead — provisioned by API, receive-only, with the same wait long-poll and OTP extraction as email.

1 · Install

SDK v0.2.0 or later — phone numbers arrived in 0.2.0.

terminal
$ npm install -D mailfixture

2 · Authenticate

Same key as email: create one in Dashboard → API keys and export it. The account must be on a paid plan — Free includes no phone numbers (see the fine print).

.env / CI secret
MAILFIXTURE_API_KEY=mfx_••••••••••••••••••••

3 · Get a number

One call provisions a dedicated US local number. It's yours alone — no shared pool, no recycled numbers — until you release it. A number can come back pending for up to about a minute before it turns active; the wait helpers work either way.

provision
import { MailFixture } from 'mailfixture';

const mail = new MailFixture(); // reads MAILFIXTURE_API_KEY

const number = await mail.createPhoneNumber();
console.log(number.phoneNumber); // "+12015550137"

4 · Write the test

Point your app's phone-verification field at the number and wait for the code. waitForOtp() is the same server-side long-poll as email — it returns the moment the text lands, or times out. No sleeps, no polling your own phone.

signup.spec.ts
import { test, expect } from '@playwright/test';
import { MailFixture } from 'mailfixture';

const mail = new MailFixture();

test('signup verifies a phone number', async ({ page }) => {
  const number = await mail.createPhoneNumber();

  await page.goto('/signup');
  await page.fill('#phone', number.phoneNumber);
  await page.click('text=Send code');

  const otp = await number.waitForOtp({ timeout: 30_000 });
  await page.fill('#otp', otp);

  await expect(page.locator('h1')).toHaveText('Welcome');
  await number.release();
});
tip Several texts racing in? Filter the wait: wait_for_otp(match="from:+1415"). SMS match takes from:, to:, and body: prefixes — a bare term searches the body, since texts have no subject. Links get extracted too: GET /v1/sms/:id/links.

5 · Tear down

Unlike inboxes, numbers don't expire on a TTL — they're a plan-capped resource (1 on Solo, 3 on Team, 10 on Scale), so a number per test doesn't scale the way an inbox per test does. Provision once per suite, release when it ends; on Solo the one number is shared state, so serialize the tests that use it.

suite-scoped number
let number;

test.beforeAll(async () => { number = await mail.createPhoneNumber(); });
test.afterAll(async () => { await number.release(); });

The fine print

Stated plainly, so you don't find out in CI:

Paid plans only. Free includes no phone numbers. Solo includes 1 number + 100 SMS/mo, Team 3 + 500, Scale 10 + 2,000.

The SMS quota is a hard stop on every plan. There's no SMS overage billing — texts beyond the monthly quota are dropped. This is different from email, where paid plans bill soft overage.

US local numbers only at launch. If your app sends codes from a service that only texts non-US numbers, this won't work yet.

Receive-only, like everything here. We never send SMS. No short codes, no MMS — plain inbound texts to a regular US local number.

Retention matches email: 14 days on Solo, 30 on Team and Scale. Releasing a number deletes its messages immediately.

Downgrades don't keep numbers forever. If a plan change leaves you with more numbers than the new plan includes, the extras are scheduled for automatic release after a 7-day grace period — you get an email and a dashboard warning first, and each number's release_after shows the date on the API. Upgrading (or releasing numbers you don't need) before then cancels it. On Free the SMS quota is 0, so inbound texts are already being dropped in the meantime.

Agents

The MCP server carries the same loop for agents: create_phone_number, then wait_for_sms_otp — "trigger the 2FA flow, read the code" is one tool call each, no glue code.

Next steps

Testing SMS OTP flows
Why SMS 2FA tests flake, and the fix — the guide behind this quickstart.
Testing OTP flows
The flake-free patterns — email edition.
Quickstart: MCP agents
The same SMS loop, driven by an agent.
Quickstart: Playwright
Email fixtures, if you start there.
Was this page useful? yes no Quickstart: MCP agents →
ON THIS PAGE
edit this page ↗
report an issue ↗