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

@qubitcodes/msg91

v0.2.2

Published

Type-safe MSG91 WhatsApp, OTP, templates, messaging, and webhook SDK for Node.js and TypeScript.

Readme

MSG91 SDK for Node.js and TypeScript

npm version license

@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 init

Package 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 .gitignore when missing.
  • Preserves every existing key and file entry.
  • Appends only missing MSG91 fields under a # MSG91 SDK section, separated by two blank lines.
  • Writes credentials only to .env; .env.example receives empty credential placeholders.
  • Ensures .env is 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.ts or 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=false

Configuration precedence:

  1. Explicit createMsg91Client() options.
  2. Root msg91.config.ts.
  3. Environment variables.
  4. 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

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 build

Run every Phase 1 check:

npm run check

Verify the packed package and framework integrations:

npm run package:verify
npm run fixtures:verify

Examples 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.