How to Send WhatsApp Messages with Node.js (Step-by-Step)
By SendAPI Engineering · Last updated April 4, 2026 · 8 min read
A complete Node.js tutorial for sending WhatsApp messages via API. Covers setup, sending text and media messages, receiving webhooks, and handling errors — with real code examples.
What You Will Build
By the end of this tutorial you will have a Node.js script that can send WhatsApp text messages, send image and document messages, and receive incoming messages via webhook — all through the SendAPI REST API.
Prerequisites:
- Node.js 18 or later
- A SendAPI account (sign up free)
- A WhatsApp account to connect as your sending session
---
Step 1: Create a WhatsApp Session
Before sending any messages, you need a connected WhatsApp session. SendAPI uses WhatsApp Web protocol — you connect your WhatsApp account by scanning a QR code.
- Log in to your SendAPI dashboard
- Go to WhatsApp → Sessions
- Click New Session, give it a name
- Scan the QR code with your WhatsApp mobile app
- Copy the Session ID — you will need it in your code
> Note: The Starter plan includes 1 WhatsApp session. Growth includes 3, Business includes 10.
---
Step 2: Get Your API Key
- In the dashboard, go to API Keys
- Click Create Key, give it a name, and copy the key
- Store it in a
.envfile — never hardcode it in source
SENDAPI_KEY=your_api_key_here
WHATSAPP_SESSION_ID=your_session_id_here
---
Step 3: Install the SDK
npm install @sendapi/node dotenv
---
Step 4: Send a Text Message
import 'dotenv/config'
import SendAPI from '@sendapi/node'
const client = new SendAPI(process.env.SENDAPI_KEY)
async function sendTextMessage() {
const result = await client.whatsapp.send({
session_id: process.env.WHATSAPP_SESSION_ID,
to: '14155552671', // recipient in E.164 format (no + prefix)
type: 'text',
text: 'Hello from SendAPI! 👋',
})
console.log('Message sent:', result.message_id)
}
sendTextMessage()
Phone numbers must be in E.164 format without the + — for example, US number +1 415 555 2671 becomes 14155552671.
---
Step 5: Send an Image
const result = await client.whatsapp.send({
session_id: process.env.WHATSAPP_SESSION_ID,
to: '14155552671',
type: 'image',
url: 'https://example.com/receipt.png',
caption: 'Your order receipt',
})
console.log('Image sent:', result.message_id)
Supported media types: image, document, audio, video. The url must be a publicly accessible HTTPS URL.
---
Step 6: Send a Document
const result = await client.whatsapp.send({
session_id: process.env.WHATSAPP_SESSION_ID,
to: '14155552671',
type: 'document',
url: 'https://example.com/invoice.pdf',
filename: 'Invoice-2026-001.pdf',
caption: 'Your invoice for April',
})
---
Step 7: Send Bulk Messages
For sending to multiple recipients at once, use sendBulk. SendAPI processes the queue asynchronously.
const result = await client.whatsapp.sendBulk({
session_id: process.env.WHATSAPP_SESSION_ID,
messages: [
{ to: '14155552671', type: 'text', text: 'Your order has shipped!' },
{ to: '447911123456', type: 'text', text: 'Your order has shipped!' },
{ to: '971501234567', type: 'text', text: 'Your order has shipped!' },
],
})
console.log('Bulk job ID:', result.job_id)
---
Step 8: Receive Incoming Messages (Webhooks)
To receive replies and delivery status events, register a webhook URL in your dashboard under Webhooks.
Here is a minimal Express.js webhook handler:
import express from 'express'
const app = express()
app.use(express.json())
app.post('/webhooks/whatsapp', (req, res) => {
const { event, data } = req.body
if (event === 'whatsapp.message.received') {
const { from, type, text } = data
console.log(`Message from ${from}: ${text?.body}`)
// Handle the message — trigger a reply, update a record, etc.
}
if (event === 'whatsapp.message.delivered') {
console.log(`Message ${data.message_id} delivered`)
}
res.sendStatus(200)
})
app.listen(3000)
For local development, use a tunnel like ngrok to expose your localhost:
ngrok http 3000
Then paste the HTTPS URL into your SendAPI dashboard webhook settings.
---
Step 9: Error Handling
Always wrap API calls in try/catch. SendAPI returns structured errors with a code and message.
import 'dotenv/config'
import SendAPI from '@sendapi/node'
const client = new SendAPI(process.env.SENDAPI_KEY)
async function sendMessage(to, text) {
try {
const result = await client.whatsapp.send({
session_id: process.env.WHATSAPP_SESSION_ID,
to,
type: 'text',
text,
})
return result
} catch (err) {
if (err.status === 401) {
console.error('Invalid API key')
} else if (err.status === 402) {
console.error('No active subscription or session limit reached')
} else if (err.status === 429) {
console.error('Rate limit exceeded — back off and retry')
} else {
console.error('Send failed:', err.message)
}
throw err
}
}
---
Without the SDK: Raw fetch
If you prefer not to use the SDK, every endpoint is a standard REST call:
const response = await fetch('https://api.sendapi.co/api/v1/whatsapp/send', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SENDAPI_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
session_id: process.env.WHATSAPP_SESSION_ID,
to: '14155552671',
type: 'text',
text: 'Hello from raw fetch!',
}),
})
const data = await response.json()
console.log(data)
---
One API Key, Three Channels
The same API key and SDK that just sent your WhatsApp message can also send an SMS or a transactional email — no extra accounts, no extra setup:
// WhatsApp
await client.whatsapp.send({ session_id, to, type: 'text', text: 'Order shipped!' })
// SMS (uses top-up credits)
await client.sms.send({ to: '+14155552671', message: 'Order shipped!', sender_id: 'MyApp' })
// Email
await client.email.send({
to: 'user@example.com',
from: 'hello@yourapp.com',
subject: 'Your order has shipped',
html: '<p>Your order is on the way.</p>',
})
Three channels. One integration. Starting at $9.99/month.
---