cd /blog
GET/blog/send-whatsapp-messages-nodejs-2026200OK
tutorialnodejs

How to Send WhatsApp Messages with Node.js

September 7, 2026|4 min read

Five minutes, three steps: get a key, install the SDK, send a message. No business verification, no message templates, no waiting for anyone to approve anything.

1. Get your credentials

Start a free trial, 14 days and no card, then pair your number by scanning a QR code, the same way you would pair WhatsApp Web.

In wsapi.chat/app/settings, generate an API key. You need two values:

  • X-Api-Key, your account key
  • X-Instance-Id, which paired number to send from

Keep both server-side. Never ship them in browser code.

env.sh
export WSAPI_API_KEY="your_api_key"
export WSAPI_INSTANCE_ID="your_instance_id"

2. Send your first message

install.sh
npm install @wsapichat/client

Node 16 or later.

send-message.js
import { WSApiClientFactory } from "@wsapichat/client";

const client = WSApiClientFactory.create({
  apiKey: process.env.WSAPI_API_KEY,
  instanceId: process.env.WSAPI_INSTANCE_ID,
});

const result = await client.messages.sendTextAsync({
  to: "1234567890@s.whatsapp.net",
  text: "Hello from Node.js",
});

console.log("Message ID:", result.id);

That is the whole integration. The recipient is the phone number in international format, no + and no spaces, followed by @s.whatsapp.net.

Prefer plain HTTP? There is no SDK requirement:

POSTsend-message.sh
curl -X POST https://api.wsapi.chat/messages/text \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: $WSAPI_API_KEY" \
  -H "X-Instance-Id: $WSAPI_INSTANCE_ID" \
  -d '{"to":"1234567890@s.whatsapp.net","text":"Hello world!"}'

A 201 comes back with the message id, which you use later to react, edit, delete or mark as read:

response.json
{ "id": "01234567890123456" }

3. Send an image

Same shape, different endpoint. Pass a URL or base64 data:

send-image.js
await client.messages.sendImageAsync({
  to: "1234567890@s.whatsapp.net",
  imageUrl: "https://example.com/cat.jpg",
  mimeType: "image/jpeg",
  caption: "Cat pic",
});

Video, audio, voice notes, documents, stickers, contact cards and locations all work the same way. One endpoint each, one JSON body.

Every method has a try variant that returns a result object instead of throwing, if you would rather branch than catch: trySendTextAsync, trySendImageAsync, and so on.

Receiving messages

Sending is half an integration. For incoming messages you have two options, and neither needs a public URL if you do not want one.

Webhooks. We POST each event to your endpoint.

SSE. You hold an open connection and events stream in. Useful in development, when your laptop has no public address, and in any environment where you would rather not expose an inbound endpoint.

The SDK exposes both. See webhook and SSE event delivery for the event types and payload shapes.

The errors you will actually hit

CodeWhat it means
400Malformed body, usually the to field missing the @s.whatsapp.net suffix
401Wrong or missing API key
409Device not paired. Scan the QR again
503Instance not available. It is starting up or reconnecting

409 is the one that surprises people. A paired session can drop. The instance reconnects on its own, and until it does, sends are rejected rather than silently lost. Handle it as a retry, not as a failure.

Where to go next