How to create a WhatsApp bot (and which approach to pick)

A WhatsApp bot uses either an unofficial library such as Baileys, which links to your number by pairing, or Meta's official Cloud API, which requires a business account. Pick based on the kind of number and the risk you accept.

Updated 6 min read

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
On this page

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.

Serves customers orearns money?yesCloud APIMeta's officialbusiness numberHTTPS webhooknoUnofficial libraryban risk: use a separate SIMShort on server RAM?yesBaileysdirect WebSocketnowhatsapp-web.jsruns Chromium
A business goes to the Cloud API; a personal project uses a library on a separate SIM, and RAM picks Baileys or whatsapp-web.js.

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 engines field 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:

bash
npm init -y
npm pkg set type=module
npm install baileys

The 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:

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:

bash
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: printQRInTerminal and the old imports changed in version 7. Pin the version in npm 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_token differs 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

Learn more

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.

Next articleHow to create a Telegram bot with BotFather and get its tokenCreate a Telegram bot by sending /newbot to @BotFather, choosing a name and a username ending in bot, and copying the token. Includes a working Python test.