Getting Started¶
This tutorial walks you through a complete @arcaelas/whatsapp session: bootstrapping a project, picking a storage engine, listening to events, pairing the device, replying to a message, and shutting down gracefully.
1. Bootstrap the project¶
Create a fresh directory and add the package:
mkdir whatsapp-bot && cd whatsapp-bot
yarn init -y
yarn add @arcaelas/whatsapp
yarn add -D tsx typescript @types/node
Create an index.ts file at the project root — that is where the rest of this guide will live.
2. Pick an engine¶
The Engine is the persistence layer for credentials, chats, contacts, and messages. The library ships two implementations:
Tip
You can also implement the Engine interface yourself to target SQLite, S3, DynamoDB, or anything else. See Engines.
3. Instantiate the client¶
new WhatsApp(...) does not open a connection — it only wires the delegates and the event emitter.
import { WhatsApp, FileSystemEngine } from "@arcaelas/whatsapp";
const engine = new FileSystemEngine(__dirname + "/.session");
const wa = new WhatsApp({
engine,
phone: 584144709840, // omit to fall back to QR pairing
});
The phone field decides the pairing flow: provide it to receive an 8-character PIN, or omit it to receive a QR PNG buffer.
4. Register listeners before connecting¶
Always attach listeners before calling connect() so you never miss the first events. Every listener receives the primary payload first and the WhatsApp instance last; message and chat events also receive the related Chat in the middle.
wa.on("connected", () => {
console.log("session ready");
});
wa.on("disconnected", () => {
console.log("session closed");
});
wa.on("message:created", async (msg, chat, wa) => {
if (msg.me) return;
console.log(`[${chat.id}] ${msg.caption}`);
});
The full event map (chat:*, contact:*, message:updated, message:reacted, message:seen, etc.) is documented in References.
5. Connect and handle the pairing payload¶
connect(callback) resolves once the session syncs. The callback fires whenever baileys hands you a fresh pairing artifact: a string (PIN) when phone is set, or a Buffer (PNG QR) otherwise. The callback may fire more than once if the previous code expires before the user completes pairing.
import { writeFileSync } from "node:fs";
await wa.connect(async (code) => {
if (typeof code === "string") {
console.log("Pair code:", code);
} else if (Buffer.isBuffer(code)) {
writeFileSync("qr.png", code);
console.log("QR written to qr.png — scan it with WhatsApp");
}
});
Success
When connect() resolves, the engine has the credentials persisted. Subsequent runs reuse them automatically — no second pairing required.
6. Reply to incoming messages¶
The wa.Message delegate exposes text, image, video, audio, location, and poll. Pass the chat id (or any identifier — phone, JID, or LID) and the body:
wa.on("message:created", async (msg, chat, wa) => {
if (msg.me) return;
if (msg.caption?.toLowerCase() === "ping") {
await wa.Message.text(chat.id, "pong");
}
});
Each method also has an instance variant on the message itself (msg.text("...")) that quotes the original message in the reply.
7. Graceful shutdown¶
Cancel pending reconnects and close the socket cleanly on SIGINT:
Pass { destroy: true } to also wipe the engine on the way out — useful in tests or when rotating accounts.
8. Run it¶
The first run prints the PIN (or writes qr.png); pair the device, wait for the connected log, then send a message to your number to see the listener fire.
Going further¶
A few client options worth knowing about:
autoclean(defaulttrue) — on a remoteloggedOut, clears the entire engine so the nextconnect()starts from a clean slate. Set tofalseto preserve chat/message history and only drop credentials.reconnect(defaulttrue) — acceptsboolean, a number of max attempts, or{ max, interval }(interval in seconds, default 60). Transient closes triggered by the protocol do not consume retry budget.sync(defaultfalse) — enables baileys' full history sync; imported chats, contacts, and messages are persisted through the engine.
The complete option list, event map, and delegate APIs live in References. For end-to-end recipes (bots, webhooks, decorators, multi-account setups) browse the Basic Bot example or the Decorator Bot showcase.