Skip to main content

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

Project description

mxlabs-number-verify

Thin Python 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 methods; 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. Standard library only. Python 3.8+.
  • 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 your own backend running against your own Bridge Alliance credentials — same wire contract.

Install

pip install mxlabs-number-verify

Quick start

import os
from mxlabs_number_verify import MXNumberVerify

nv = MXNumberVerify(api_key=os.environ["MXLABS_API_KEY"])

# 1. Start a session for the number the user claims to own.
started = nv.start("+60123456789")

# 2. Have the USER'S DEVICE open started["auth_url"] over its CELLULAR data
#    connection. That mobile-network path is what silently proves the number.

# 3. Poll for the result.
result = nv.poll_verify(started["session_id"])

if result["status"] == "verified":
    grant_access(result["phone"])          # silently verified — no OTP, no friction
else:
    # 4. Fallback: send an OTP by SMS.
    sent = nv.sms_send(started["session_id"])
    code = input(f"Enter the code sent to {sent['masked']}: ")
    if nv.sms_verify(started["session_id"], code)["status"] == "verified":
        grant_access(result.get("phone"))

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

MXNumberVerify(api_key, backend_url=..., timeout=30.0, transport=None)

arg notes
api_key required — issued by MX Labs
backend_url hosted backend (default https://api.mxlab.sg/sdk.php), or your own
timeout per-request network timeout in seconds
transport optional callable(url, headers, body, timeout) -> (status, dict) for tests / a custom HTTP stack

Methods

  • ping(){"status": "ok", "partner": ...}.
  • start(phone){"session_id", "auth_url", "callback_url", ...}. phone may be E.164 (+60...) or local digits.
  • verify(session_id, code=None) → one check; status is pending, verified, not_verified, or nv_unavailable.
  • poll_verify(session_id, interval=2.0, timeout=30.0) → polls verify until a terminal status or timeout. On timeout returns {"status": "nv_unavailable", "reason": "timeout", "fallback": "sms_otp"}.
  • sms_send(session_id){"status": "sms_sent", "masked", "expires_in"}.
  • sms_verify(session_id, code){"status": "verified" | "invalid" | "expired"}.

Errors

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

from mxlabs_number_verify import MXNumberVerifyError
try:
    nv.start("nope")
except MXNumberVerifyError as e:
    print(e.code)  # "invalid_phone"

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 only accept these magic test numbers:

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 is identical to production — same methods, same responses (with an extra "sandbox": true). Swap in a live key for real numbers; no code changes.

nv = MXNumberVerify(api_key="sbx_...")
started = nv.start("+10000000001")
nv.verify(started["session_id"])   # -> {"status": "verified", "sandbox": True}

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mxlabs_number_verify-0.1.0.tar.gz (7.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mxlabs_number_verify-0.1.0-py3-none-any.whl (8.1 kB view details)

Uploaded Python 3

File details

Details for the file mxlabs_number_verify-0.1.0.tar.gz.

File metadata

  • Download URL: mxlabs_number_verify-0.1.0.tar.gz
  • Upload date:
  • Size: 7.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for mxlabs_number_verify-0.1.0.tar.gz
Algorithm Hash digest
SHA256 61b98c9e2baf646dfa4b4dbd9423292bbec27dfd7d243d8c67a87184a9da6e60
MD5 ad6f1f243a989b9a2041be61f5b3b079
BLAKE2b-256 6277950479cf468b331bede3e223c2d219a9d225ba7fd67ec1c595ecd4ef4677

See more details on using hashes here.

File details

Details for the file mxlabs_number_verify-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for mxlabs_number_verify-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f9913f2bc37bcf334c4e0f8c35cfd4add309dabefc971dac93a0319542630e55
MD5 53f260f57ed2dcf953a95f5e27a10a5a
BLAKE2b-256 5d4ae99059712646766d0a2788d18c20bf767cd50d34dd912ad00fded5c5d2fe

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page