Learning objectives
After reading this article you will be able to:
- Choose between Baileys, whatsapp-web.js and the official Cloud API
- Build a Baileys bot that answers a command and keeps its session
- Send your first message through the Cloud API and avoid the common errors
Creating a WhatsApp bot is more of a decision than a tutorial. WhatsApp has no open developer portal for any number, the way Discord does. There are two paths: community libraries that pose as a device linked to your number, and Meta's official Cloud API, built for businesses.
That choice shapes everything after it: which number you use, whether there is ban risk, what it costs, and whether you need a domain with HTTPS. This guide compares the options, builds a working bot with Baileys and shows the first send through the Cloud API.
What are the options for creating a WhatsApp bot?#
| Baileys | whatsapp-web.js | Cloud API | |
|---|---|---|---|
| Official | No | No | Yes |
| How it connects | Direct WebSocket | Chromium with Puppeteer | HTTPS webhook + Graph API |
| Number | Any, including personal | Any, including personal | Business number on a Meta account |
| Ban risk | Yes | Yes | No unofficial-client risk |
| Needs a domain | No | No | Yes, with valid HTTPS |
| Memory | Low | High (runs a browser) | Just your web server |
The Baileys maintainers state that the project is not affiliated with WhatsApp and does not condone use that violates the Terms of Service. So bulk notifications, or the number of a business that cannot go offline, call for the Cloud API. For the full comparison, see Baileys, whatsapp-web.js or the official API.
Rule of thumb: for a personal bot, a study project or a friends' group, use a library with a separate SIM. For support, sales or anything that earns money, use the Cloud API.
What do you need before you start?#
- The current Active LTS of Node.js, which meets the minimum in the
enginesfield of Baileys 7. - A WhatsApp number dedicated to the bot, ideally a SIM that is not your main one.
- WhatsApp installed on the phone of that number.
- For the Cloud API: a Meta developer account, an app with the WhatsApp product and, to receive messages, a public URL with HTTPS.
How do you create a WhatsApp bot with Baileys?#
Create the project. Baileys 7 is an ES module, hence type=module:
npm init -y
npm pkg set type=module
npm install baileysThe code below uses the Baileys 7 API. Check on npm which version the latest tag points to before installing; the 6.x series is still published as legacy and has a different API, so older tutorials may not run.
Set WA_PHONE to the bot's number, digits only with the country code (for example 15551234567). Create index.js:
import makeWASocket, {
DisconnectReason,
useMultiFileAuthState,
} from "baileys";
const AUTH_DIR = process.env.AUTH_DIR ?? "auth_info";
const PHONE = process.env.WA_PHONE;
async function start() {
const { state, saveCreds } = await useMultiFileAuthState(AUTH_DIR);
const sock = makeWASocket({ auth: state });
let pairingRequested = false;
sock.ev.on("creds.update", saveCreds);
sock.ev.on("connection.update", async ({ connection, lastDisconnect, qr }) => {
// First run: no saved session, so request a pairing code.
// The qr event repeats; the code is requested once per connection.
if (qr && !pairingRequested && !sock.authState.creds.registered) {
if (!PHONE) return console.error("Set WA_PHONE to pair.");
pairingRequested = true;
console.log("Pairing code:", await sock.requestPairingCode(PHONE));
}
if (connection === "open") console.log("Connected to WhatsApp.");
if (connection === "close") {
const code = lastDisconnect?.error?.output?.statusCode;
if (code === DisconnectReason.loggedOut) {
console.error("Session ended from the phone. Delete the folder and pair again.");
process.exit(1);
}
start(); // network drop or restart requested by WhatsApp: reconnect
}
});
sock.ev.on("messages.upsert", async ({ messages, type }) => {
if (type !== "notify") return;
for (const msg of messages) {
if (msg.key.fromMe || !msg.message) continue;
const text =
msg.message.conversation ?? msg.message.extendedTextMessage?.text;
if (text?.trim().toLowerCase() === "!ping") {
await sock.sendMessage(msg.key.remoteJid, { text: "pong" });
}
}
});
}
start();Run it with WA_PHONE=15551234567 node index.js. On the bot's phone, open Linked devices, choose to link with a phone number and type the code printed in the log. If you prefer a QR code, it arrives in the qr field of the connection.update event: the printQRInTerminal option from older versions is deprecated and no longer prints the QR.
How do you check that it worked?#
The log prints "Connected to WhatsApp." Send !ping to the bot from another number and expect pong. Stop the process and start it again: it should connect on its own without asking for a code, because the session lives in the auth_info folder.
Treat that folder like a password. Don't commit it to Git and don't wipe it on deploy, or the bot becomes a new device and has to pair again. For the next step, see how to keep the bot online 24/7.
What about whatsapp-web.js?#
whatsapp-web.js drives a real WhatsApp Web inside Chromium through Puppeteer. The API is simple (client.on("message", ...) and msg.reply(...)) and you sign in by scanning a QR code. The cost is memory: a whole browser runs next to the bot. On servers and in containers, especially when running as root, Chromium usually needs flags such as --no-sandbox. If the bot is small and you want low RAM, Baileys is usually lighter.
How do you send the first message through the Cloud API?#
The Cloud API has no pairing. In Meta's developer dashboard, create an app with the WhatsApp product, then under API Setup select the test number and copy its ID. The temporary token generated there expires quickly. For production, create a system user in the business settings and generate a permanent token with the business_management, whatsapp_business_messaging and whatsapp_business_management permissions.
Sending is an HTTP call. The Graph API version goes in the URL, here in the GRAPH_VERSION variable (in the vXX.0 format); use the latest one listed in the Graph API version changelog:
curl -X POST "https://graph.facebook.com/$GRAPH_VERSION/$PHONE_NUMBER_ID/messages" \
-H "Authorization: Bearer $WA_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": "15551234567",
"type": "text",
"text": { "body": "Hello from my bot!" }
}'Two rules shape how you use it. The first is the customer service window: when a user messages you, a 24-hour window opens and resets with each new message from them. Inside it you reply with free-form text; outside it, only with pre-approved templates. The second is pricing: since July 1, 2025, Meta charges per delivered template message. See Meta's pricing page and how much the official API costs.
To receive messages, Meta calls your webhook. It verifies the endpoint with a GET containing hub.mode, hub.verify_token and hub.challenge: if the token matches, answer 200 with the hub.challenge value. POST requests are signed in X-Hub-Signature-256 (HMAC-SHA256 of the raw body using the app secret). The certificate must be valid: self-signed ones are not supported. The full server is in how to host a WhatsApp bot 24/7.
What are the most common mistakes?#
- Asking for a code again after every restart: the auth folder was deleted or is not persistent. See WhatsApp bot disconnecting.
- Two instances sharing one session: one kicks the other out. Run only one.
- Following an old Baileys tutorial:
printQRInTerminaland the old imports changed in version 7. Pin the version innpm install. - Text rejected on the Cloud API: the 24-hour window closed. Use an approved template.
- A webhook Meta will not validate: the URL has no valid HTTPS, or
hub.verify_tokendiffers from the one you configured. - Banned number: unofficial libraries, especially with bulk sending, put the number at risk. Use a separate SIM.
How to do this on Square Cloud#
On Square Cloud, hosting is self-service: you deploy and control memory, variables and restarts. Baileys, WPPConnect, whatsapp-web.js and the official Cloud API are all supported, so the choice between them stays yours.
For the bot in this guide, upload the project as a ZIP in the dashboard, or use the CLI, GitHub or the VS Code extension, and set WA_PHONE and any other variables in the application settings. The auth folder lives on the application's disk, so the session stays connected after a restart. Containers are always on, with automatic restart (on by default for dashboard deploys) when the process exits with an error. If you choose the Cloud API, a web application gets a squareweb.app subdomain with HTTPS that can serve as the webhook URL.
All plans are paid, billed in Brazilian reais. Hobby costs R$12.49 per month, with 1 GB of RAM shared across your applications; the minimum is 256 MB per background application, such as a bot, and 512 MB per web application. See the WhatsApp bot hosting page.
Bot hosting
Keep your bot online 24/7 with automatic restarts
FAQs
Do I have to pay to create a WhatsApp bot?
Unofficial libraries are open source and charge no license fee. On the official Cloud API, Meta charges per delivered template message, while free-form text inside the 24-hour window is not charged. Either way you need a server to keep the process running.
Can I use my personal number for the bot?
With Baileys or whatsapp-web.js you can, but the number may get banned, so use a separate SIM. The Cloud API does not link your personal number: you register a number on a Meta business account.
Do I need to know how to code to make a WhatsApp bot?
For a bot with your own logic, yes: basic JavaScript is enough for the simplest examples. If you do not want to code, an automation tool on top of the Cloud API works, but it is less flexible.
Which language is best for a WhatsApp bot?
Node.js is the most common, because Baileys and whatsapp-web.js are JavaScript libraries. The Cloud API is plain HTTP, so any language that can make requests and receive webhooks works.
More in Bots
See all 33 articles- How much does it cost to host a Discord bot?The cost of hosting a Discord bot depends on RAM, uptime, database and outside services. Learn how to estimate your own bill and where the money goes.
- Running a bot on your phone or PC vs hosting itA bot on your PC or phone (Termux) goes offline when the device shuts down, sleeps or loses its network. Compare the three options and when each one fits.
- How to host a Discord bot 24/7To keep a Discord bot online 24/7, run its process on an always-on machine with automatic restarts. Compare your PC, a VPS with pm2 or systemd, and bot hosting.
- Why your Discord bot keeps going offline (and how to fix it)A Discord bot goes offline when the process dies, the token is invalid, an intent is disabled or memory runs out. Learn to diagnose each cause and fix it.