Free
Try itApp → otpmock → Test
- 100 messages a month
- 1 API key
- All 8 SMS provider APIs
- Live inbox
SMS Mock inbox for end-to-end tests
otpmock speaks the API of the SMS provider you already use: Twilio, Vonage, AWS SNS, Infobip, Sinch, Plivo, Netgsm or İleti Merkezi. Your app sends verification codes the same way it does in production. We catch every message before it gets anywhere near a carrier, and hand the code to Playwright, Cypress, or whoever is clicking through staging.
Free for 100 messages a month. No card required.
This board is a demo. Nothing on it ever reached a phone, and nothing your tests send will either.
Works with Playwright / Cypress / Appium / Jest / GitHub Actions / GitLab CI / any HTTP client
1 The problem
The flow works. The suite doesn't, because step four is waiting for a text message. Every common workaround costs you something.
You pay for every message on every CI run. Delivery takes as long as the carrier decides, so tests time out at random. And you still need a phone, or a paid inbox, to read the code back.
if (phone === TEST_PHONE) code = "000000" works fine until one config mistake ships it to production. Then it's a key to every account on that number.
Fine for plain SMS. Useless with Verify APIs (Twilio Verify, Vonage Verify), where the provider generates the code and your fake never sees it. Then you build it again for the next project.
2 How it works
otpmock answers in your SMS provider's own API format, so your application code can't tell the difference. Only the test environment's configuration changes.
Your app
Calls your provider's SDK: messages.create, sms.send, a Verify request. Same SDK, same code path as production.
otpmock
Answers in your provider's exact response format, keeps the message for 10 minutes and pulls the code out of the text.
Carrier network never contacted
Your test
Asks the inbox over HTTPS and types the code in. Verify checks pass with the right code, exactly like the real provider.
Example: the whole integration for the Twilio Node.js SDK. Every provider is a similar one-option change (Vonage's restHost, the AWS SDK's endpoint, Infobip's baseUrl…). Production keeps talking to your provider; anything with OTPMOCK_URL set talks to us.
import twilio from "twilio";
import { OtpMockHttpClient } from "./twilio-node-client.mjs";
export const sms = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN, {
httpClient: process.env.OTPMOCK_URL ? new OtpMockHttpClient(process.env.OTPMOCK_URL) : undefined,
});
3 Providers
Each emulation follows the provider's real paths, payloads, responses and errors, and is tested end to end with the provider's official Node.js SDK.
twilio@vonage/server-sdk@aws-sdk/client-sns@infobip-api/sdk@sinch/sdk-coreplivo@netgsm/sms@iletimerkezi/iletimerkezi-nodeUsing something else? Send to the generic HTTP API from a test-only branch. All providers
4 In your tests
Give every test its own number so parallel workers never read each other's codes. Then wait for the code and type it in.
test("sign up with a phone number", async ({ page }) => {
const phone = otp.randomPhone(); // unique per test
await page.getByLabel("Phone").fill(phone);
await page.getByRole("button", { name: "Send code" }).click();
const { code } = await otp.waitForCode(phone);
await page.getByLabel("Verification code").fill(code);
await expect(page.getByText("Welcome")).toBeVisible();
});
// cypress.config.js: on("task", { waitForCode: (phone) => otp.waitForCode(phone).then((r) => r.code) })
it("signs up with a phone number", () => {
const phone = "+1555" + Cypress._.random(1e7, 9e7);
cy.get("[name=phone]").type(phone);
cy.contains("Send code").click();
cy.task("waitForCode", phone).then((code) => cy.get("[name=code]").type(code));
cy.contains("Welcome").should("be.visible");
});
# Latest code sent to a number (404 until one arrives)
curl https://api.otpmock.com/v1/inbox/%2B15550142/code \
-H "Authorization: Bearer $OTPMOCK_API_KEY"
{ "code": "482913", "messageSid": "SM6926…", "receivedAt": 1791406301420 }
The otp helper is a single dependency-free file. Get it from the docs. Using an AI coding assistant? Point it at otpmock.com/llms-full.txt and it can do the whole integration.
5 Dashboard
Manual testers watch codes land in the browser and click to copy. Engineers manage keys per environment. Everyone sees how much of the month is left.
Your Acme verification code is: 482913
Acme: 7731 is your login code. Don't share it.
Your Acme code: 905126
Use 3388 to confirm your phone number.
6 Spec sheet
since timestamp ignores anything left over from the previous run.7 Pricing
A message is one SMS your app sends, or one Verify verification it starts. Reading codes is always free.
App → otpmock → Test
App → otpmock → Test
App → otpmock → Test
App → otpmock → Test
Prices in USD, billed monthly. Payments are processed by Creem, our merchant of record, which also handles sales tax and VAT. Cancel any time. See the refund policy.
8 Questions
No, and that's the point. Messages never leave our API: no carrier fees, no delivery delays, no phones involved. Use otpmock in test and staging environments only.
Barely. You point your provider's SDK at otpmock when an environment variable is set, usually one option such as Twilio's httpClient, Vonage's restHost or the AWS SDK's endpoint. Your production configuration stays exactly as it is.
Supported for Twilio Verify v2 and Vonage Verify v2. otpmock generates the code, your test reads it from the inbox, and the verification check approves it the same way the provider would, including wrong-code and too-many-attempts errors.
Twilio, Vonage, AWS SNS, Infobip, Sinch, Plivo, Netgsm and İleti Merkezi. For anything else, such as MessageBird or an in-house gateway, send to otpmock's generic HTTP API from a test-only branch, and email support@otpmock.com: requests decide what we build next.
You can, but please don't. Test environments should use made-up numbers like the ones randomPhone() generates. Either way, every message is deleted after 10 minutes.
Sending returns HTTP 429 until your allowance resets on the 1st of next month (UTC), or until you upgrade. Reading codes keeps working.
Any time, from your account. You keep access until the end of the period you've paid for. New subscriptions can be refunded within 7 days; see the refund policy.
Get a key in under a minute. The free plan covers a small suite for good.
Get a free API key