Skip the boilerplate: clone a working bot
- Sending a plain message;
- Sending media: image / file / video;
- Sending a contact (vCard);
- Sending a product;
- Creating a WhatsApp group;
- Sending a group invitation;
- Sending a message to a WhatsApp group;
- Receiving and processing incoming messages;
- Responding to an unknown command (for implementing menus, instructions, or a welcome message).
git clone https://github.com/Whapi-Cloud/nodejs-whatsapp-chatbot
cd nodejs-whatsapp-chatbot
package.json:
npm install
Project structure
- /files — sample media the bot can send (image, PDF, video, vCard). Replace them when you want.
- config.js —
token,botUrl,port, and optionalgroup/productIDs. - index.js — registers the webhook, listens on
/hook/messages, and maps numbers 1–9 to actions.
Connect your WhatsApp number
Scan the QR code in your account
config.js as token. Requests to https://gate.whapi.cloud use it as a Bearer token.
Before you start the bot
- Channel token pasted into
config.js. - A second phone to test; the bot skips messages sent by the connected number.
- Public HTTPS URL. Use ngrok locally, or any host with HTTPS in production.
Give the bot a public HTTPS URL
/hook/messages on your server -> the bot checks the command and replies. You do not paste the URL in the dashboard; the bot registers it from config.js when it starts.config.js as botUrl; on startup the repository registers it through the channel settings API.Local tunnel with ngrok
port in config.js is 80 (do not use 443). Download ngrok, then run ngrok http 80 — or the port you set in config.js.https:// forwarding URL. Set botUrl to that host plus /hook, for example botUrl: "https://84c7-151-33-282-113.ngrok-free.app/hook".
/hook suffix. The server listens for POST on /hook/messages; botUrl is the prefix stored in channel settings.node index.js, the bot sends PATCH /settings with that URL and the messages event. Incoming WhatsApp messages then hit your process. You can ignore the webhook fields in the dashboard unless you want to inspect what was registered.What production needs
botUrl, restart the bot so it re-registers the webhook.Launch the bot
token and botUrl in config.js, then from the project root run node index.js. You should see a listening log. From a different phone, send any text to the connected number. The bot replies with a numbered menu.Command menu
- Simple text message
- Send image
- Send document
- Send video
- Send contact
- Send product
- Create group
- Simple text message for the group
- Get the ids of your three groups
Send the number only, for example 1. Anything else returns this list again.
product and group in config.js. Skip them for the first test. When you need them, list IDs with GET /groups and GET /business/products.Troubleshooting
The bot does not reply
from_me. Confirm the token, that botUrl ends in /hook, that ngrok (or your host) is still running, and that the process printed a listening message.The bot sends messages in a loop
if (message.from_me) continue. Without it the bot answers its own outgoing messages and never stops.It works in some chats but not others
Send WhatsApp messages from Node.js
fetch call to https://gate.whapi.cloud with your Bearer token.Sending a text message
const response = await fetch('https://gate.whapi.cloud/messages/text', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
to: '[email protected]',
body: 'Hello from the cloned bot'
})
});
const data = await response.json();
console.log(data);
Sending a document
const response = await fetch('https://gate.whapi.cloud/messages/document', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
to: '[email protected]',
media: 'https://example.com/file.pdf',
caption: 'Document from the bot'
})
});
const data = await response.json();
console.log(data);
Working with WhatsApp groups
/groups endpoint.List your groups
const response = await fetch('https://gate.whapi.cloud/groups', {
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
});
const { groups } = await response.json();
console.log(groups);
Count group participants
groups.forEach(group => {
console.log(`Group Name: ${group.name}`);
console.log(`Participants: ${group.participants.length}`);
});
Handle group messages
@g.us. Incoming webhooks use the same suffix; the cloned bot already branches on it when a command targets a group.
// From the cloned bot: messages from groups use chat_id ending in @g.us
const messages = req.body.messages;
messages.forEach(message => {
if (message.chat_id.endsWith('@g.us')) {
console.log(`Group message: ${message.text?.body}`);
}
});
config.group. Set that ID before you use those numbers.Keep it online
node index.js running on a machine with a public HTTPS URL, and set botUrl to https://your-host/hook. This bot is a long-running Express process, not a one-shot serverless function — a VPS or any Node host with HTTPS is enough. Restart after you change botUrl so the webhook is registered again.Firebase
- Create a project in Firebase Console;
- Install Firebase CLI, following the instructions;
- Initialize Firebase in your project directory with the command firebase init;
- Deploy your bot using the command firebase deploy --only functions.
AWS (Amazon Web Services)
- Register or log in to AWS Management Console;
- Create a new Lambda function through the AWS console, selecting API Gateway as the trigger;
- Upload your bot's code into the Lambda function;
- Configure the API Gateway to interact with your bot and the outside world.
Heroku
- Create an account on Heroku;
- Install Heroku CLI and log in;
- Create a new Heroku app through the console or using the command heroku create;
- Link your Git repository to Heroku and perform deployment with the commands git push heroku master;
- Set the webhook URL provided by Heroku.