Guides / Vonage Verify

How to test Vonage Verify without sending real SMS

Updated October 8, 2026
Short answer

With Vonage Verify v2, Vonage generates the code, so your test cannot know it. In test environments, point the Vonage SDK's apiHost at otpmock. otpmock generates the code, stores it in the inbox for that number and returns completed when your app calls checkCode with it. Application code stays the same.

Why Verify is harder to test than plain SMS

When your app sends its own code with the SMS API, a mock only has to store the text. With Verify, the provider creates the code and later checks it. A test needs both halves: a way to read the code, and a check endpoint that accepts it.

Setup

Create the client as usual and pass otpmock as the host when OTPMOCK_URL is set. In the test environment, set VONAGE_APPLICATION_ID to your otpmock API key. The SDK still signs a JWT with your private key; otpmock reads the application_id claim to find your account.

src/vonage.js
import { Vonage } from "@vonage/server-sdk";

export const vonage = new Vonage(
  {
    apiKey: process.env.VONAGE_API_KEY,
    apiSecret: process.env.VONAGE_API_SECRET,
    applicationId: process.env.VONAGE_APPLICATION_ID, // otpmock API key in tests
    privateKey: process.env.VONAGE_PRIVATE_KEY,
  },
  process.env.OTPMOCK_URL ? { restHost: process.env.OTPMOCK_URL, apiHost: process.env.OTPMOCK_URL } : {},
);

Your application code (unchanged)

src/auth.js
export async function startVerification(phone) {
  const { requestId } = await vonage.verify2.newRequest({
    brand: "Acme",
    workflow: [{ channel: "sms", to: phone.replace(/^\+/, "") }],
  });
  return requestId;
}

export async function confirm(requestId, code) {
  return (await vonage.verify2.checkCode(requestId, code)) === "completed";
}

The test

tests/login.spec.ts
import { test, expect } from "@playwright/test";
import { OtpMock } from "./otpmock";

const otp = new OtpMock({ baseUrl: process.env.OTPMOCK_URL!, apiKey: process.env.OTPMOCK_API_KEY! });

test("login with Vonage Verify", async ({ page }) => {
  const phone = otp.randomPhone();
  await page.goto("/login");
  await page.fill("[name=phone]", phone);
  const since = Date.now() - 5000;
  await page.click("text=Send code");

  const { code } = await otp.waitForCode(phone, { since });
  await page.fill("[name=code]", code);
  await page.click("text=Verify");
  await expect(page).toHaveURL(/dashboard/);
});

Download the helper with curl -O https://otpmock.com/sdk/otpmock.ts.

Behaviour that matches Vonage

Full endpoint list on the Vonage provider page.

FAQ

Does otpmock check the JWT signature?

No. It only reads the application_id claim to find your account, so your normal private key works and no Vonage key is shared with otpmock.

Does this cover the SMS API and Messages API too?

Yes. vonage.sms.send and vonage.messages.send with channel sms are emulated; the text lands in the same inbox.

Try it on your own suite

The free plan includes 100 messages a month. No card required.

Get a free API key

Related guides