@qubitcodes/msg91
v0.2.2
Published
Type-safe MSG91 WhatsApp, OTP, templates, messaging, and webhook SDK for Node.js and TypeScript.
Maintainers
Readme
MSG91 SDK for Node.js and TypeScript
@qubitcodes/msg91 is a type-safe, server-side MSG91 SDK for Node.js and TypeScript. It provides MSG91 WhatsApp API integration for OTP delivery, approved template messages, session and interactive messages, number management, message logs, webhooks, and WhatsApp calling configuration.
Use it in Next.js server modules, Route Handlers, Server Actions, Express, Fastify, NestJS, React Router framework mode, or other Node.js backends. This is an independent, unofficial SDK and is not affiliated with or endorsed by MSG91.
Current release: version 0.2.1. Core WhatsApp numbers, templates, messaging, OTP, operations, configuration, and framework integrations are implemented; provider-dependent and undocumented capability boundaries are tracked in the documentation.
Current implementation
- Synchronous
createMsg91Client()initialization. - Root
msg91.config.ts, environment, and explicit option resolution. - Strict runtime configuration validation.
- Node/server-only runtime guard.
- Injected or native Fetch API transport.
- Timeouts and abort signals.
- Safe read-only retry classifier with exponential jitter.
- Typed error hierarchy and provider-response parsing.
- Credential redaction and safe telemetry hooks.
- ESM output and TypeScript declarations.
- Integrated WhatsApp number listing through the documented MSG91 endpoint.
- Number lookup by alias, provider ID, normalized number, or configured default.
- Honest capability reporting for undocumented onboarding and number requests.
- Local exact/pattern template visibility and read-only policies.
- Explainable policy results and reusable mutation guards.
- Template list, bounded
listAll, and unambiguous lookup. - Typed template creation with strict component and variable validation.
- Explicit create-new replacement planning and execution.
- Confirmed, policy-protected template deletion.
- Submission results that never misrepresent Meta approval.
Install the MSG91 Node.js SDK
npm install @qubitcodes/msg91
npx msg91 initPackage links: npm · GitHub · issues
The installer prints the initialization command after npm installation. msg91 init securely prompts for MSG91_AUTH_KEY and MSG91_WHATSAPP_NUMBER, then:
- Creates
.env,.env.example, and.gitignorewhen missing. - Preserves every existing key and file entry.
- Appends only missing MSG91 fields under a
# MSG91 SDKsection, separated by two blank lines. - Writes credentials only to
.env;.env.examplereceives empty credential placeholders. - Ensures
.envis ignored by Git. - Remains safe to run repeatedly without duplicating settings.
Package installation itself does not request secrets because npm lifecycle scripts are non-interactive in CI, containers, pnpm, Yarn, and many IDEs. The explicit initializer works consistently in those environments. For non-interactive automation, provide MSG91_AUTH_KEY and MSG91_WHATSAPP_NUMBER in the process environment before running npx msg91 init.
MSG91 WhatsApp API usage
Create a typed MSG91 client, then access WhatsApp numbers, templates, messages, OTP, logs, webhooks, and calls through the msg91.whatsapp namespace.
Example: list numbers and manage MSG91 WhatsApp templates:
const numbers = await msg91.whatsapp.numbers.list();
const support = await msg91.whatsapp.numbers.get({ number: 'support' });
const defaultNumber = await msg91.whatsapp.numbers.getDefault();
const policy = msg91.whatsapp.templates.explainPolicy({ name: 'login_otp' });
msg91.whatsapp.templates.assertMutable({ name: 'order_update' }, 'replace');
const templates = await msg91.whatsapp.templates.list({
number: 'support',
status: 'approved',
page: 1,
pageSize: 100,
});
await msg91.whatsapp.templates.delete({
confirm: true,
name: 'deprecated_notice',
number: 'support',
});Goals
- One framework-neutral SDK for Node.js projects.
- First-class support for Next.js, React Router framework mode, Express, Fastify, and NestJS.
- Strict compile-time types plus runtime validation of MSG91 responses.
- Root configuration through
msg91.config.tsor fixed environment variables. - Safe local policies for hiding templates and preventing consumer-side template mutations.
- No dependency on undocumented MSG91 dashboard endpoints.
Intended API
import { createMsg91Client } from '@qubitcodes/msg91';
const msg91 = createMsg91Client();
const templates = await msg91.whatsapp.templates.list({
number: 'support',
status: 'approved',
});
await msg91.whatsapp.messages.sendTemplate({
from: 'support',
to: '919876543210',
template: {
name: 'order_confirmed',
language: 'en',
variables: {
body: ['John', 'ORD-1001'],
},
},
});The canonical calling convention is:
msg91.<channel>.<resource>.<operation>()Examples:
msg91.whatsapp.numbers.list();
msg91.whatsapp.templates.create(input);
msg91.whatsapp.messages.sendText(input);
msg91.whatsapp.interactive.sendList(input);
msg91.whatsapp.catalog.sendProduct(input);
msg91.whatsapp.logs.list(query);Configuration
Create msg91.config.ts in the consuming project's root:
import { defineMsg91Config, env } from '@qubitcodes/msg91/config';
export default defineMsg91Config({
authKey: env('MSG91_AUTH_KEY'),
whatsapp: {
defaultNumber: 'support',
numbers: {
support: env('MSG91_WHATSAPP_SUPPORT_NUMBER'),
},
templates: {
hidden: ['common_otp'],
hiddenPatterns: ['^internal_'],
readOnly: ['login_otp', 'payment_received'],
readOnlyPatterns: ['^system_'],
},
},
});Environment-only configuration will also be supported:
MSG91_AUTH_KEY=
MSG91_WHATSAPP_NUMBER=
MSG91_TIMEOUT_MS=15000
MSG91_MAX_RETRIES=2
MSG91_DEBUG=false
MSG91_WEBHOOKS_ENABLED=falseConfiguration precedence:
- Explicit
createMsg91Client()options. - Root
msg91.config.ts. - Environment variables.
- Package defaults.
See configuration.
Important security boundary
The SDK is server-only. An MSG91 authentication key must never be bundled into a browser, React Client Component, or public environment variable.
- Next.js: use Route Handlers, Server Actions, or other server-only modules.
- React Router: use loaders/actions in framework mode or a separate backend.
- Browser-only React: call an application-owned backend endpoint.
Template policies
Hidden and read-only settings are local SDK policies:
- Hidden templates are excluded from ordinary SDK list/search results.
- Read-only templates remain visible and sendable.
- Read-only templates cannot be deleted or replaced through the SDK.
- Violations fail locally before an MSG91 request is sent.
- These policies do not alter permissions in the MSG91 dashboard.
Creating authentication templates
Authentication templates use Meta's native OTP components. Meta supplies the localized body, expiry text, and copy-code action from these settings; normal body text is intentionally not accepted for this category.
await msg91.whatsapp.templates.create({
category: 'AUTHENTICATION',
components: [
{ addSecurityRecommendation: true, type: 'BODY' },
{ codeExpirationMinutes: 10, type: 'FOOTER' },
{ buttons: [{ otpType: 'COPY_CODE', type: 'OTP' }], type: 'BUTTONS' },
],
language: 'en',
name: 'common_otp',
number: 'support',
});Creation returns approvalStatus: 'pending'; query template status separately before sending it in production.
Sending WhatsApp messages
import { createMsg91Client, defineWhatsAppTemplate } from '@qubitcodes/msg91';
const msg91 = createMsg91Client();
const receipt = defineWhatsAppTemplate({
category: 'UTILITY',
language: 'en',
name: 'payment_receipt',
variables: { customer: 'body_1', amount: 'body_2' },
});
const commonOtp = defineWhatsAppTemplate({
category: 'AUTHENTICATION',
language: 'en',
name: 'common_otp',
});
await msg91.whatsapp.messages.sendDefinedTemplate({
from: 'support', template: receipt, to: '919000000000',
values: { customer: 'Ada', amount: '10.00' },
});
await msg91.whatsapp.messages.sendDefinedTemplate({
from: 'support', template: commonOtp, to: '919000000000', values: { otp: '246810' },
});
await msg91.whatsapp.messages.sendDefinedTemplateBulk({
from: 'support', template: commonOtp,
recipients: [
{ to: '919000000000', values: { otp: '246810' } },
{ to: '919000000001', values: { otp: '135790' } },
],
});
await msg91.whatsapp.messages.sendText({ from: 'support', text: 'Hello', to: '919000000000' });
await msg91.whatsapp.interactive.sendButtons({
body: 'Continue?', buttons: [{ id: 'yes', title: 'Yes' }], from: 'support', to: '919000000000',
});
await msg91.whatsapp.catalog.sendProduct({
catalogId: 'catalog-id', from: 'support', productRetailerId: 'sku-1', to: '919000000000',
});Generate and send authentication codes
const generated = msg91.whatsapp.otp.generate({
length: 6,
includeNumbers: true,
includeLetters: false,
includeSymbols: false,
});
const defaultCode = msg91.whatsapp.otp.generate(); // Six numeric digits.
const submission = await msg91.whatsapp.otp.send({
from: 'support',
to: ['919000000000', '919000000001'],
generate: {
length: 8,
includeNumbers: true,
includeLetters: true,
},
});
await msg91.whatsapp.otp.send({ from: 'support', to: '919000000000', generate: true });
for (const result of submission.results) {
console.log(result.to, result.code);
}generate: true, generate: {}, and otp.generate() default to a six-digit numeric code. to accepts one number or up to 1,000 unique numbers. With generate, each recipient receives a separately generated code. To send an application-generated code, replace generate with code. If both are supplied, code wins and the SDK emits a warning. Custom generation supports lengths from 3 through 64, uses Node's cryptographic random source, and excludes visually ambiguous characters by default. The SDK returns codes to the caller but never stores or verifies them.
Authentication sends default to the approved common_otp template and English (en). Override these with templateName and language when the integrated number uses another compatible authentication template.
Session, interactive, and catalog sends require an active WhatsApp customer-service window. Provider acceptance is not delivery confirmation. Bulk template calls accept at most 1,000 recipients.
Every static definition requires one Meta category: AUTHENTICATION, UTILITY, or MARKETING. Authentication definitions accept only { otp }; the SDK maps it to MSG91's body_1 and button_1 fields (with sub_type: 'url') used by common_otp. Utility and marketing definitions use the explicitly named variables map. The category describes the approved template and does not let an SDK caller change its category at send time.
Logs, webhooks, and calls
const logs = await msg91.whatsapp.logs.list({ startDate: '2026-08-01', endDate: '2026-08-03' });
const event = msg91.whatsapp.webhooks.parse(requestBody);
const status = await msg91.whatsapp.templates.getStatus({
from: 'support', language: 'en', name: 'payment_receipt',
});
await msg91.whatsapp.calls.configure({
enabled: true,
callHoursEnabled: true,
timezone: 'Asia/Kolkata',
weeklyHours: [{ day: 'monday', open: '09:00', close: '18:00' }],
});Enable typed webhook dispatch in msg91.config.ts:
export default {
authKey: env('MSG91_AUTH_KEY'),
whatsapp: {
webhooks: {
enabled: true,
handlers: {
'template.approved': async (event) => updateTemplate(event.templateId, 'approved'),
'template.rejected': async (event) => reportRejection(event.templateName, event.reason),
'message.delivered': async (event) => markDelivered(event.messageId),
'message.failed': async (event) => markFailed(event.messageId, event.reason),
all: async (event) => auditWebhook(event.kind),
},
},
},
};Then expose an application-owned webhook route and dispatch its JSON body:
const result = await msg91.whatsapp.webhooks.handle(await request.json());
return Response.json(result, { status: result.accepted ? 200 : 503 });MSG91_WEBHOOKS_ENABLED=true can enable dispatch from the environment. Callback functions must be supplied in msg91.config.ts or runtime configuration because environment variables cannot contain functions. Configure the public callback URL in MSG91 separately.
Log queries accept at most three inclusive calendar days. Unknown webhook events and fields are retained. Supported dispatch kinds include template approval/rejection/pause/disable and message sent/delivered/read/failed/received. Webhook signature verification deliberately throws an unsupported-operation error because MSG91 does not publish a WhatsApp signing contract.
Confirmed feasibility boundaries
Documented MSG91 APIs support integrated-number retrieval, template listing and creation, template deletion, template sending, session messages, interactive messages, catalog messages, logs, and WhatsApp call configuration.
The following must not be presented as stable SDK capabilities until official MSG91 contracts are available and tested:
- Fully API-driven WhatsApp number onboarding.
- Requesting or purchasing a new WhatsApp number.
- Direct in-place editing of a submitted template.
- Dashboard permission enforcement.
- Undocumented dashboard/internal API calls.
See the feasibility study.
Documentation
- Software Requirements Specification
- Feasibility Study
- Implementation Plan
- Public API Design
- Configuration
- WhatsApp Features
- Framework Compatibility
- Security
- Roadmap
- Traceability Matrix
- Live API Test Results
License, attribution, and support
Copyright 2026 Qubit Codes. Licensed under the Apache License 2.0. Redistributions and derivative distributions must retain the accompanying NOTICE attribution.
This is an independent, unofficial SDK. It is not affiliated with, endorsed by, sponsored by, or officially maintained by MSG91 or Walkover Web Solutions. MSG91 and related marks, APIs, documentation, and services belong to their respective owners. Package users remain responsible for following applicable MSG91 and Meta terms, policies, pricing, consent, and messaging requirements.
For support, bug reports, fixes, feature requests, or contributions, email [email protected] or open an issue in the GitHub repository.
Development
npm install
npm run typecheck
npm test
npm run buildRun every Phase 1 check:
npm run checkVerify the packed package and framework integrations:
npm run package:verify
npm run fixtures:verifyExamples are available for Next.js App Router, React Router framework mode, React Router SPA with Express, and Express under examples/.
License
To be selected before the first public release.
