Route your app's SMS to a mock inbox in the test environment. Register a cy.task in setupNodeEvents that polls the inbox for a code sent to a given number after a given time. In the spec, use a unique number, trigger the SMS, then cy.task("waitForCode", { phone, since }) and type the result.
Why a task, not cy.request
cy.request can call the inbox API, but polling until a code arrives means writing retry logic with recursion. A Node task can simply await a promise that resolves when the code is there, and Cypress waits for it with its normal task timeout.
Step 1: route SMS to the mock in the test environment
Only the environment that runs your app under test changes. Point your SMS provider's SDK at otpmock when OTPMOCK_URL is set: Twilio, Vonage, Telnyx, AWS SNS, AWS End User Messaging, Infobip, Sinch, Bird, Plivo, Telesign, Bandwidth, Netgsm, İleti Merkezi, Verimor and Mutlucell each need one option (setup per provider). For anything else, send to POST /v1/messages/send from a test-only branch.
Step 2: register the task
Download the JavaScript helper with curl -O https://otpmock.com/sdk/otpmock.mjs.
import { defineConfig } from "cypress";
import { OtpMock } from "./otpmock.mjs";
const otp = new OtpMock({ baseUrl: process.env.OTPMOCK_URL, apiKey: process.env.OTPMOCK_API_KEY });
export default defineConfig({
e2e: {
taskTimeout: 30000,
setupNodeEvents(on) {
on("task", {
waitForCode: ({ phone, since }) => otp.waitForCode(phone, { since }).then((r) => r.code),
});
},
},
});Step 3: the spec
it("signs up with a phone number", () => {
const phone = "+1555" + Cypress._.random(1e7, 9e7); // unique per test
cy.visit("/signup");
cy.get("[name=phone]").type(phone);
const since = Date.now() - 5000;
cy.contains("Send code").click();
cy.task("waitForCode", { phone, since }).then((code) => {
cy.get("[name=code]").type(code);
});
cy.contains("Welcome").should("be.visible");
});Tips
- Unique numbers keep specs independent when you run them in parallel with Cypress Cloud or split CI jobs.
- The
sincetimestamp stops a retried test from reading the code its previous attempt received. - Environment variables: the task runs in Node, so
process.envworks there. Set the same variables on the app server that sends the SMS. - Manual QA: testers can watch the same codes land in the live inbox while the suite runs.
FAQ
Can I call the inbox from the browser instead?
You can, with cy.request and the API key, but then the key is exposed to the page under test and you have to write the polling yourself. A Node task keeps the key out of the browser.
What if my app uses a Verify API (Twilio Verify, Vonage Verify)?
It works the same way. otpmock generates the code for Verify requests and puts it in the inbox, so the task reads it like any other SMS.
The free plan includes 100 messages a month. Using an AI coding assistant? Point it at otpmock.com/llms-full.txt.
Get a free API key