Node.js SDK
Server-side SMS OTP verification for Express, NestJS, Next.js API routes, and any Node.js backend
Installation
npm install @baxcloud/baxverify
# or
pnpm add @baxcloud/baxverifyRequires Node.js 18+. The client connects to BaxCloud automatically — only projectId and apiKey are required.
Quick Start
1import { BaxCloudVerifyClient } from '@baxcloud/baxverify';
2
3const verify = new BaxCloudVerifyClient({
4 projectId: process.env.BAXCLOUD_PROJECT_ID!,
5 apiKey: process.env.BAXCLOUD_API_KEY!,
6});
7
8// Send OTP from your API route
9await verify.sendOtp({ phone: '+14155552671', purpose: 'LOGIN' });
10
11// Verify code — returns accessToken (JWT proof)
12const result = await verify.verifyOtp({
13 phone: '+14155552671',
14 code: '123456',
15});
16
17// Validate token when client exchanges it for your session
18const proof = await verify.validateVerificationToken({
19 token: result.accessToken,
20});
21console.log('Verified:', proof.phone);Phone login pattern
Keep your BaxCloud API key on the server. Mobile or web clients call your API; your API calls BaxVerify. After OTP success, return the accessToken to the client or exchange it immediately for your own session JWT.
1// POST /auth/phone-login — client sends BaxVerify proof
2app.post('/auth/phone-login', async (req, res) => {
3 const proof = await verify.validateVerificationToken({
4 token: req.body.accessToken,
5 });
6
7 // Create your session (JWT, cookie, database session, etc.)
8 const session = await createUserSession(proof.phone);
9 res.json({ sessionToken: session.token });
10});Works with any auth stack — custom JWT, session cookies, Parse Server custom auth, Firebase custom tokens pattern, etc.
Parse Server
For Parse phone login, use the dedicated adapter @baxcloud/parse-server-baxverify — not a copy-paste auth file. Mobile clients call ParseUser.logInWith('baxverify', externalAuth) after BaxVerify OTP.
npm install @baxcloud/parse-server-baxverifyFull setup: Parse Server SDK guide · Flutter client
Configuration
| Option | Required | Description |
|---|---|---|
| projectId | Yes | BaxCloud project ID |
| apiKey | Yes | API key with BaxVerify enabled |
| timeout | No | Request timeout in ms (default 30000) |
| debug | No | Log requests to console |
1// externalAuth for Parse or other auth adapters
2const auth = BaxCloudVerifyClient.externalAuthFromVerifyResult(result);
3// { id: '+14155552671', token: 'eyJhbG...' }API Methods
| Method | Description |
|---|---|
| sendOtp | Send SMS verification code |
| verifyOtp | Verify code; returns accessToken |
| validateVerificationToken | Validate proof JWT (single-use by default) |
| getStats | Sent / verified / failed counts |
| listLogs | Paginated delivery logs |
Error codes
Failed BaxVerify API calls return a structured JSON envelope. Use error.code for programmatic handling and error.details.helpUrl to link users to the right dashboard page.
{
"success": false,
"error": {
"code": "BAXVERIFY_FEATURE_DISABLED",
"message": "BaxVerify is not enabled for this project. Enable it under Project → Features.",
"details": {
"projectId": "your-project-id",
"helpUrl": "https://baxcloud.tech/dashboard/projects/your-project-id?tab=features",
"docsUrl": "https://baxcloud.tech/docs/baxverify"
}
},
"timestamp": "2026-06-06T12:00:00.000Z",
"path": "/v1/auth/sms/send"
}| Code | HTTP | Message | What to do |
|---|---|---|---|
| BAXVERIFY_FEATURE_DISABLED | 403 | BaxVerify is not enabled for this project. | Enable BaxVerify under Project → Features (details.helpUrl). |
| BAXVERIFY_PLAN_NOT_INCLUDED | 403 | Your plan does not include BaxVerify. | Upgrade your subscription (details.helpUrl → Billing). |
| BAXVERIFY_API_KEY_SCOPE | 403 | API key lacks BaxVerify scope. | Create or edit an API key with BaxVerify enabled. |
| BAXVERIFY_SENDER_NOT_CONFIGURED | 400 | No SMS sender configured. | Rent a phone number and/or register a Sender ID in BaxVerify Setup (details.helpUrl). |
| BAXVERIFY_INSUFFICIENT_CREDITS | 400 | Insufficient credits for SMS delivery. | Add credits in Billing before sending (details.helpUrl). |
| BAXVERIFY_VERIFY_FEE_CREDITS | 400 | Insufficient credits for the verification fee. | Add credits in Billing (details.helpUrl). |
| BAXVERIFY_NO_SUBSCRIPTION | 400 | No active subscription for this project. | Subscribe or restore billing for the project. |
| BAXVERIFY_RATE_LIMIT | 429 | Rate limit exceeded. | Wait before sending or verifying another code. |
| BAXVERIFY_OTP_EXPIRED | 400 | Code expired or not found. | Call send again to request a new code. |
| BAXVERIFY_OTP_INVALID | 400 | Invalid verification code. | Ask the user to re-enter the code from SMS. |
| BAXVERIFY_OTP_MAX_ATTEMPTS | 429 | Too many failed attempts. | Request a new code via send. |
| BAXVERIFY_INVALID_PHONE | 400 | Phone number is not valid. | Use full international format with country code (e.g. +14155552671). |
| BAXVERIFY_SMS_SEND_FAILED | 400 | Failed to send SMS. | Check sender setup, destination number, and provider status. |
| BAXVERIFY_TOKEN_INVALID | 401 | Verification token is invalid or expired. | User must verify OTP again to obtain a new accessToken. |
| BAXVERIFY_TOKEN_CONSUMED | 401 | Verification token already used. | Tokens are single-use when consume: true — verify OTP again. |
| BAXVERIFY_PROJECT_INACTIVE | 403 | Project is not active. | Reactivate the project in the dashboard. |
| BAXVERIFY_PROJECT_HEADER_REQUIRED | 400 | X-Project-Id header is required. | Send X-Project-Id with every request. |
| BAXVERIFY_API_KEY_PROJECT_MISMATCH | 403 | API key does not belong to this project. | Use an API key created for the same project as X-Project-Id. |
1import { BaxVerifyError } from '@baxcloud/baxverify';
2
3try {
4 await verify.sendOtp({ phone: '+14155552671', purpose: 'LOGIN' });
5} catch (err) {
6 if (err instanceof BaxVerifyError) {
7 switch (err.code) {
8 case 'BAXVERIFY_FEATURE_DISABLED':
9 // err.details?.helpUrl → enable in dashboard
10 break;
11 case 'BAXVERIFY_SENDER_NOT_CONFIGURED':
12 // err.details?.helpUrl → BaxVerify Setup
13 break;
14 case 'BAXVERIFY_INSUFFICIENT_CREDITS':
15 // err.details?.helpUrl → Billing
16 break;
17 default:
18 console.error(err.statusCode, err.code, err.message);
19 }
20 }
21}