MSG91 Node
Send SMS, OTP, WhatsApp messages, voice OTPs, and emails via MSG91 — India's most popular messaging API. Use it for order confirmations, OTP verification flows, bulk campaign messages, and transactional alerts.
Prerequisites
- A MSG91 account at msg91.com
- Auth Key from MSG91 Dashboard → API → Auth Key
- DLT registration for all commercial SMS (mandatory for India — see DLT note below)
- Approved SMS templates on DLT portal (Jio DLT, Vodafone DLT, etc.)
- For WhatsApp operations: an integrated WhatsApp Business number on MSG91
flowId must correspond to an approved DLT template.Credentials
| Field | Description | Where to find it |
|---|---|---|
| Auth Key | Your MSG91 API authentication key | MSG91 Dashboard → API → Auth Key |
- Go to Settings → Credentials
- Click Add Credential
- Select MSG91
- Paste your Auth Key
- Click Save
Operations
Send SMS (Template)
Send a DLT-approved template SMS using a flow ID.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
mobile | string | Yes | — | Phone number with country code, e.g. 919876543210 |
senderId | string | Yes | — | 6-character DLT-approved sender ID, e.g. NODEBS |
flowId | string | Yes | — | MSG91 flow ID linked to a DLT-approved template |
smsVariables | JSON | No | — | Template variables as JSON, e.g. {"VAR1": "500", "VAR2": "ORD-123"} |
{
"msg91": {
"operation": "SEND_SMS",
"requestId": "9123456789",
"status": "success",
"mobile": "919876543210"
}
}Send Transactional SMS
Send a plain-text transactional message using route 4 (transactional) or route 8 (OTP).
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
mobile | string | Yes | — | Phone number with country code |
senderId | string | Yes | — | 6-character DLT-approved sender ID |
message | string | Yes | — | SMS message text |
route | number | Yes | 4 | 4 = transactional, 8 = OTP |
Send Bulk SMS
Send personalized template SMS to multiple numbers in one API call.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
senderId | string | Yes | — | 6-character DLT-approved sender ID |
flowId | string | Yes | — | MSG91 flow ID for the DLT template |
bulkData | JSON array | Yes | — | Array of objects: [{"mobile":"91...", "VAR1":"...", "VAR2":"..."}] |
{
"msg91": {
"operation": "SEND_BULK_SMS",
"requestId": "9123456789",
"count": 50,
"status": "success"
}
}Schedule SMS
Schedule a template SMS to be sent at a future time.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
mobile | string | Yes | — | Phone number with country code |
senderId | string | Yes | — | 6-character sender ID |
flowId | string | Yes | — | Flow ID for template |
smsVariables | JSON | No | — | Template variables |
scheduleTime | string | Yes | — | Schedule time in YYYY-MM-DD HH:mm:ss format (IST) |
Send OTP
Send a one-time password to a mobile number. MSG91 generates the OTP automatically and sends it directly to the user.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
mobile | string | Yes | — | Phone number with country code, e.g. 919876543210 |
otpTemplateId | string | Yes | — | DLT-approved OTP template ID from MSG91 Dashboard |
otpLength | number | Yes | 6 | Number of digits: 4, 5, or 6 |
otpExpiry | number | Yes | 10 | Minutes before OTP expires |
{
"msg91": {
"operation": "SEND_OTP",
"status": "success",
"mobile": "919876543210",
"message": "OTP sent successfully",
"timestamp": "2026-04-01T10:00:00.000Z"
}
}{{body.otp}} from your form or webhook input.Verify OTP
Verify an OTP entered by the user. Returns {{msg91.verified}} as true or false. Use an If / Else node immediately after to gate access.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
mobile | string | Yes | — | Same phone number used in Send OTP |
otpValue | string | Yes | — | OTP entered by user — typically {{body.otp}} from form submission |
{
"msg91": {
"operation": "VERIFY_OTP",
"verified": true,
"mobile": "919876543210"
}
}{{msg91.verified}} equals true. The TRUE branch proceeds with account creation; the FALSE branch returns an error message.Webhook Trigger (user submits phone)
→ MSG91 — Send OTP
mobile: {{body.mobile}}
otpTemplateId: abc123def456
otpLength: 6
otpExpiry: 10
→ HTTP Response: {"message": "OTP sent"}
───────── (second workflow) ─────────
Webhook Trigger (user submits OTP)
→ MSG91 — Verify OTP
mobile: {{body.mobile}}
otpValue: {{body.otp}}
→ If / Else: {{msg91.verified}} equals true
TRUE → Create account → Send welcome SMS
FALSE → HTTP Response: {"error": "Invalid OTP. Try again."}Resend OTP
Resend the OTP via SMS text or voice call.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
mobile | string | Yes | — | Phone number with country code |
retryType | string | Yes | — | "text" to resend via SMS, "voice" to call the number |
Invalidate OTP
Immediately expire the active OTP for a mobile number. Use this after a successful verification or on logout.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
mobile | string | Yes | — | Phone number with country code |
Send WhatsApp (Template)
Send a pre-approved WhatsApp template message via MSG91's integrated WhatsApp Business API.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
mobile | string | Yes | — | Recipient phone with country code |
integratedNumber | string | Yes | — | Your MSG91 integrated WhatsApp number |
whatsappTemplate | string | Yes | — | Approved template name |
whatsappLang | string | Yes | en | Template language code, e.g. en, hi, mr |
whatsappParams | JSON array | No | — | Template variable values: ["Rahul", "ORD-123", "₹500"] |
{
"msg91": {
"operation": "SEND_WHATSAPP",
"messageId": "msg_OFj67X3s9kH5rA",
"status": "success",
"mobile": "919876543210"
}
}Send WhatsApp Media
Send an image, video, document, or audio file via WhatsApp.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
mobile | string | Yes | — | Recipient phone with country code |
integratedNumber | string | Yes | — | Your integrated WhatsApp number |
mediaType | string | Yes | — | image · video · document · audio |
mediaUrl | string | Yes | — | Public URL of the media file |
mediaCaption | string | No | — | Caption shown below the media |
Send Voice OTP
Deliver an OTP via automated voice call. Useful as a fallback when SMS is not delivered.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
mobile | string | Yes | — | Phone number with country code |
otpTemplateId | string | Yes | — | Voice OTP template ID |
otpLength | number | Yes | 6 | OTP digit length: 4, 5, or 6 |
otpExpiry | number | Yes | 10 | Minutes before OTP expires |
Send Email
Send a transactional email via MSG91's email service.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
toEmail | string | Yes | — | Recipient email address |
subject | string | Yes | — | Email subject line |
emailBody | string | Yes | — | HTML or plain-text email body |
fromEmail | string | Yes | — | Sender email address |
fromName | string | No | — | Sender display name |
{
"msg91": {
"operation": "SEND_EMAIL",
"status": "success",
"toEmail": "rahul@example.com"
}
}Get Balance
Check remaining SMS credits in your MSG91 account.
{
"msg91": {
"operation": "GET_BALANCE",
"balance": 4850,
"type": "SMS"
}
}Get Report
Fetch delivery report for a previously sent SMS using its request ID.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
requestId | string | Yes | — | Request ID from a previous Send SMS operation — use {{msg91.requestId}} |
{
"msg91": {
"requestId": "9123456789",
"reports": [
{ "mobile": "919876543210", "status": "delivered", "deliveredAt": "..." }
]
}
}Complete Workflow Examples
Post-Payment Order Confirmation SMS
Use case: Automatically send an order confirmation SMS after a Razorpay payment is captured.
Razorpay Trigger (event: payment.captured)
→ MSG91 — Send SMS
mobile: {{razorpayTrigger.payload.payment.entity.contact}}
senderId: NODEBS
flowId: your_dlt_flow_id
smsVariables: {
"VAR1": "{{razorpayTrigger.payload.payment.entity.notes.customerName}}",
"VAR2": "{{razorpayTrigger.payload.payment.entity.id}}"
}OTP Login Flow
Use case: Allow users to log in to your app using phone number OTP.
Webhook Trigger (POST /send-otp, body: { mobile })
→ MSG91 — Send OTP
mobile: {{body.mobile}}
otpTemplateId: abc123
otpLength: 6
otpExpiry: 10
→ HTTP Response: {"message": "OTP sent to your number"}
─────── Second workflow ───────
Webhook Trigger (POST /verify-otp, body: { mobile, otp })
→ MSG91 — Verify OTP
mobile: {{body.mobile}}
otpValue: {{body.otp}}
→ If / Else: {{msg91.verified}} equals true
TRUE → Set Variable: userId = {{body.mobile}}
→ HTTP Response: {"token": "...", "message": "Login successful"}
FALSE → HTTP Response: {"error": "Invalid OTP"}Common Issues & Solutions
| Issue | Cause | Solution |
|---|---|---|
| SMS not delivered (DND numbers) | Number is on Do Not Disturb registry | Use route 4 (transactional) for order updates — DND does not apply |
| Flow ID error | DLT template not approved or wrong flow ID | Verify template approval in MSG91 Dashboard → DLT Templates |
| OTP verification always fails | OTP expired or wrong mobile number used | Ensure same mobile is used in Send and Verify. Shorten otpExpiry if needed |
| Balance is 0 | Account ran out of SMS credits | Recharge at MSG91 Dashboard or set up auto-recharge |
Related Nodes
- Razorpay Trigger — trigger MSG91 SMS on payment events
- If / Else — gate access based on
{{msg91.verified}} - WhatsApp — send WhatsApp messages directly via Meta API
- Shiprocket — pair with Shiprocket AWB in SMS content