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

medusa-msg91

v0.0.8

Published

MSG91 SMS notification provider for Medusa v2 (Flow API).

Readme

medusa-msg91

Medusa v2 Notification Provider for MSG91 transactional SMS via the Flow API.

Features

  • Plugs into @medusajs/medusa/notification via AbstractNotificationProviderService
  • Flow API (POST /api/v5/flow/) for template-based transactional SMS (DLT-safe — no raw message strings)
  • Process-owned Flow IDs via notification.template (e.g. customer-registration notification_template)
  • Optional legacy options.templates map for key → Flow ID remapping
  • Strict E.164 phone validation before any MSG91 call
  • Per-process sliding-window rate limiting
  • HTTP timeout (default 10s) with no automatic in-provider retry
  • Typed errors: client (4xx) vs server (5xx/timeout) vs rate limit
  • Structured success/failure/latency logs

Known limitations (read before production)

Fire-and-forget: v1 returns the MSG91 request id when the API accepts the send. There is no DLR/delivery webhook. “Sent” does not mean “delivered to handset.” Retain externalId from logs for support. Delivery confirmation is Phase 2.

Per-process rate limiter: The sliding window is per Node process, not cluster-wide. With N Medusa replicas/pods the effective ceiling is approximately rateLimitPerMinute × N. Do not discover this after a bad autoscale event.

Correlation key ≠ guaranteed dedupe: A correlation key (Medusa notification.id or UUID) is always logged. MSG91 Flow v5 docs do not document a client reference field that MSG91 dedupes on. A retried send() can still bill/send twice. Callers must not blindly retry without their own dedupe.

No circuit breaker: During an extended MSG91 outage each call waits the full requestTimeoutMs (default 10s). Fail-fast circuit breaking is Phase 2.

No free-form content.text: Unlike Twilio, MSG91 Flow/DLT cannot send rendered SMS bodies. Processes must set notification_template to a Flow ID (or use the optional options.templates map).

Installation

npm install medusa-msg91
# or
yarn add medusa-msg91

Registration (medusa-config.ts)

Provider options are credentials only. Flow IDs live on the process (customer-registration / order-management):

import { defineConfig } from "@medusajs/framework/utils"

export default defineConfig({
  plugins: [
    {
      resolve: "customer-registration",
      options: {
        phone_auth: {
          channel: "sms",
          // MSG91 Flow ID — sent as notification.template
          notification_template: process.env.MSG91_FLOW_OTP_SEND,
          template: "templates/customer-otp-sms.txt",
          resendThrottleSeconds: 90,
        },
      },
    },
  ],
  modules: [
    {
      resolve: "@medusajs/medusa/notification",
      options: {
        providers: [
          {
            resolve: "medusa-msg91/providers/msg91",
            id: "msg91",
            options: {
              channels: ["sms"],
              authkey: process.env.MSG91_AUTHKEY,
              senderId: process.env.MSG91_SENDER_ID,
              // Confirm real account throughput with MSG91 support/dashboard before production.
              rateLimitPerMinute: Number(process.env.MSG91_RATE_LIMIT_PER_MINUTE),
              // Flow variable for notification.data.otp. Blank defaults to "num" (##num##).
              otpVar: process.env.MSG91_OTP_VAR,
              requestTimeoutMs: 10_000,
            },
          },
        ],
      },
    },
  ],
})

Rate-limit confirmation checklist

  1. Open MSG91 account dashboard / contact MSG91 support
  2. Confirm the actual per-account throughput (requests/minute)
  3. Set MSG91_RATE_LIMIT_PER_MINUTE to that confirmed number (or lower)
  4. If you run multiple Medusa replicas, remember the limiter is per process

Sending notifications

Prefer process-owned template_obj (customer-registration / order-management):

await notificationModule.createNotifications({
  to: "+919876543210", // strict E.164 required
  channel: "sms",
  template: process.env.MSG91_FLOW_OTP_SEND, // Flow ID
  data: {
    otp: "123456",
    customer_name: "Ada",
    template_obj: {
      templateId: process.env.MSG91_FLOW_OTP_SEND,
      varMap: { otp: "VAR1", customer_name: "VAR2" },
    },
  },
})

Without varMap, stringable data fields are passed through as MSG91 variables (keys must match the Flow).

For OTP, set provider otpVar (env MSG91_OTP_VAR) to the Flow placeholder name. When present, data.otp is sent as that variable (e.g. num → ##num##). There is no built-in default — omit otpVar only if you already map otp via varMap. Blank MSG91_OTP_VAR is treated as unset (the provider still boots). If the phone gets the template text with an empty OTP slot, the Flow ##placeholder## does not match otpVar / varMap.

Options

| Option | Required | Description | |---|---|---| | authkey | Yes | MSG91 auth key (sent as authkey header, not query param) | | senderId | Yes | Approved sender ID for Flow API | | rateLimitPerMinute | Yes | Max outbound calls/minute in this process | | otpVar | No* | MSG91 Flow variable for data.otp (e.g. num). From MSG91_OTP_VAR. *Required for OTP when varMap does not map otp. | | templates | No | Optional legacy map of logical keys → { templateId, varMap? }. Prefer process notification_template = Flow ID. | | requestTimeoutMs | No | HTTP timeout (default 10000) |

When options.templates is omitted (recommended), notification.template is the MSG91 Flow ID (flow_id / template_id in the API payload).

Error taxonomy

| Error class | Typical cause | Guidance | |---|---|---| | Msg91ClientError → MedusaError.INVALID_DATA | Bad phone, missing template, MSG91 4xx | Do not retry; fix input/config | | Msg91ServerError → MedusaError.UNEXPECTED_STATE | Timeout, network, MSG91 5xx | Retry later with care (no blind loops) | | Msg91RateLimitError → MedusaError.UNEXPECTED_STATE | Local limiter or MSG91 429 | Back off |

Structured log fields

Success (MSG91 notification sent): template, to (masked), externalId, correlationKey, latencyMs, statusCode

Failure (MSG91 notification failed): same + errorType (client | server | rate_limit), statusCode, message

Caller guidance

  • Do not blindly retry send() / createNotifications without your own dedupe — MSG91 may send twice
  • Prefer a stable Medusa notification id so logs correlate retries
  • Treat accepted API response as “queued/accepted by MSG91”, not “delivered”
  • Set process notification_template to the Flow ID; do not send Twilio-style content.text alone

Phase 2 backlog

  • DLR webhooks + delivery status
  • Persistent idempotency ledger / cluster-safe dedupe
  • Cluster-wide rate limiting (Redis or equivalent)
  • Circuit breaker (fail fast after N consecutive MSG91 failures)
  • Vendor-level MSG91 credentials
  • OTP channel via MSG91 OTP API (out of scope for v1)
  • WhatsApp / Email via MSG91 Hello / Campaign products

Development

cd medusa-msg91
npm install
npm test
npm run build

Medusa version

Peer dependencies target @medusajs/* 2.18.0.