Skip to content

mxlabs-sg/number-verify-node

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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

About

Silent phone verification (CAMARA) with SMS OTP fallback — Node SDK

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors