npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mxlabs-number-verify

v0.1.1

Published

Silent phone-number verification (CAMARA Number Verification) with SMS OTP fallback — thin client for the MX Labs Open Gateway.

Readme

mxlabs-number-verify

Thin Node client for silent phone-number verificationCAMARA Number Verification delivered through the MX Labs Open Gateway — with an automatic SMS OTP fallback.

Your app never touches telco keys, JWT signing, or the OIDC/CIBA dance. You call two functions; the hosted backend brokers every operator's auth and gives you back a yes/no.

┌──────────────┐  start / verify / sms_*   ┌────────────────────────┐   CAMARA   ┌────────────┐
│  your server │ ────────────────────────► │  MX Labs Open Gateway  │ ─────────► │  operator  │
│  (this SDK)  │ ◄──────────────────────── │  (keys stay here)      │            │  network   │
└──────────────┘           JSON            └────────────────────────┘            └────────────┘
  • Zero dependencies. Uses the built-in fetch (Node 18+).
  • Silent when it can be, SMS when it can't. One flow, graceful fallback.
  • Open-core. This client is MIT. Point it at the MX Labs hosted backend (recommended) or at your own backend running against your own Bridge Alliance credentials — same wire contract.

Install

npm install mxlabs-number-verify

Quick start

One call. The SDK handles the session and polling; you just open the auth URL on the user's device (that cellular fetch is what silently proves the number).

const { MXNumberVerify } = require('mxlabs-number-verify');

const nv = new MXNumberVerify({ apiKey: process.env.MXLABS_API_KEY });

const result = await nv.verifyNumber('+60123456789', {
  onAuthUrl: (url) => redirectUserToUrl(url),   // device opens it over cellular
});

if (result.status === 'verified') grantAccess(result.phone);
else await nv.smsSend(result.sessionId);        // fallback: OTP by SMS

Advanced — full control

verifyNumber is sugar over four primitives; use them directly when you want to drive the session, redirect, and fallback yourself:

const { sessionId, authUrl } = await nv.start('+60123456789');
// have the device open authUrl over cellular, then:
const result = await nv.pollVerify(sessionId);
if (result.status !== 'verified') {
  const { masked } = await nv.smsSend(sessionId);
  const code = await askUser(`Enter the code sent to ${masked}`);
  await nv.smsVerify(sessionId, code);
}

Why "open the URL over cellular"?

Silent Number Verification works by having the device's mobile network confirm the SIM's number — no SMS, no code, no user action. That only fires when the authorize URL is fetched over the cellular data path (not Wi-Fi). If the device is on Wi-Fi or the operator can't verify, the call returns nv_unavailable / not_verified with fallback: "sms_otp" — switch to the SMS flow. The SDK surfaces this for you; you never guess.

API

new MXNumberVerify(options)

| option | type | default | notes | | ------------ | -------- | -------------------------------- | -------------------------------------- | | apiKey | string | — | required — issued by MX Labs | | backendUrl | string | https://api.mxlab.sg/sdk.php| hosted backend, or your own | | timeoutMs | number | 30000 | per-request network timeout | | fetch | function | global fetch | override for tests / older runtimes |

Methods

All methods return Promises.

  • ping(){ status: 'ok', partner }. Health/key check.
  • start(phone){ sessionId, authUrl, callbackUrl }. phone may be E.164 (+60...) or local digits.
  • verify(sessionId, { code? }) → one check. status is pending, verified, not_verified, or nv_unavailable. Pass code only if your client captured the auth code from the redirect itself.
  • pollVerify(sessionId, { intervalMs = 2000, timeoutMs = 30000 }) → polls verify until a terminal status or timeout. On timeout resolves to { status: 'nv_unavailable', reason: 'timeout', fallback: 'sms_otp' }.
  • smsSend(sessionId){ status: 'sms_sent', masked, expires_in }. The OTP never leaves the backend.
  • smsVerify(sessionId, code){ status: 'verified' | 'invalid' | 'expired' }.

Errors

Transport failures, non-2xx HTTP, and {status:"error"} bodies throw MXNumberVerifyError with a machine-readable .code (e.g. invalid_api_key, invalid_phone, session_not_found_or_expired), plus .httpStatus and .response. Non-error verification outcomes (not_verified, nv_unavailable, invalid, expired) are returned, not thrown — they're normal results.

const { MXNumberVerifyError } = require('mxlabs-number-verify');
try {
  await nv.start('nope');
} catch (e) {
  if (e instanceof MXNumberVerifyError) console.error(e.code); // "invalid_phone"
}

Example

See examples/basic.js:

MXLABS_API_KEY=xxx node examples/basic.js +60123456789

TypeScript

Types ship with the package (src/index.d.ts) — no @types install needed.

Try it in your browser

Live interactive demo (sandbox, no signup): https://api.mxlab.sg/

Sandbox — try it in 10 minutes, no real phone

Ask MX Labs for a sandbox key (sbx_…). Sandbox keys are fully simulated: they never touch a real operator or send a real SMS, and they only accept these magic test numbers — so you can walk the whole flow from your laptop:

| Number | Simulates | | --------------- | -------------------------------------------- | | +10000000001 | silent verification succeeds | | +10000000002 | silent NV unavailable → SMS OTP fallback | | +10000000003 | NV reachable but number doesn't match |

In sandbox the SMS OTP is always 000000. Everything else in the API is identical to production — same functions, same responses (with an extra "sandbox": true). When you're ready for real numbers, swap in a live key; no code changes.

const nv = new MXNumberVerify({ apiKey: 'sbx_...' });
const { sessionId } = await nv.start('+10000000001');
const r = await nv.verify(sessionId);   // → { status: 'verified', sandbox: true }

Getting an API key

Request one from MX Labs. The hosted backend brokers Bridge Alliance's Open Gateway across its operator networks, so a single key works across every supported country.

API contract

The raw HTTP contract is published as an OpenAPI 3.1 spec: mxlabs/number-verify-openapi. Use it to generate a client in any other language.

License

MIT © MX Labs