FAQ
Do I need to scan the QR every time?
No. The session is saved in ./sessions/. You only scan the QR the first time. If the session is invalidated (you deleted the folder or WhatsApp unlinked the device), you'll need to scan again.
Can I use the bot without scanning QR?
Yes, with a pairing code (Baileys transport):
const bot = new Bot({
provider: new BaileysProvider({
session: 'my-bot',
pairingPhoneNumber: '+57300XXXXXXX', // full international format
}),
})
// v2 emits the auth artifact (QR string / pairing code) through the 'qr' event:
bot.on('qr', (artifact) => console.log('Auth artifact:', artifact))There is no dedicated pairing_code event in v2 — the pairing code arrives as the qr event payload (a string auth artifact the core forwards verbatim from the provider).
Does the bot work in groups?
Yes. when() handlers work in groups by default. To respond only in private:
bot.when('hola', async (ctx) => {
if (ctx.isGroup) return
await ctx.reply('👋')
})In a group, each participant gets an independent conversation by default when they trigger a flow — two people talking to the bot in the same group don't share state. Set conversationScope: 'chat' if you want one shared conversation per group instead.
Why doesn't bot.when('hi') match "hi there"?
The default match strategy is normalized equals, not "contains" — the message must equal the trigger exactly after lowercasing, stripping accents and collapsing spaces. For substring matching, use a TriggerObject:
bot.when({ pattern: 'hi', match: 'contains' }, handler)match also accepts 'startsWith' and 'endsWith'.
Can I send buttons?
It depends on the transport. With the official Cloud API (@botmesh/meta) and Telegram, ctx.buttons(text, buttons) sends native buttons (see Messages & media). On Baileys, no — native interactive buttons/lists are dead in the current WhatsApp Web protocol. Use a numbered menu (.choose() in a flow, c.choose() in a conversation) or a real poll:
await ctx.poll('Confirm your order?', ['Yes', 'No'])Do flows/conversations survive a restart?
Only if you opt in. flow(...).persistent() checkpoints the conversation after each step, but the default MemoryStore lives in memory — a restart loses those checkpoints unless the store is file-backed:
const bot = new Bot({
provider: new BaileysProvider({ session: 'my-bot' }),
store: new MemoryStore({ file: './store.json' }), // or use @botmesh/sqlite-store
})Can I send messages without someone writing first?
Yes, with bot.send:
bot.schedule('0 9 * * *', async () => {
await bot.send('57300XXXXXXX@s.whatsapp.net', '🌅 Good morning!')
})To send one message to many chats use bot.broadcast() (batches, retries on rate limits, cancellable); to start a whole flow for someone use bot.trigger(). On the WhatsApp Cloud API, free-form messages only go out inside the 24-hour window: see Session window.
How do I stop the bot properly?
process.on('SIGINT', async () => {
console.log('🛑 Shutting down...')
await bot.stop({ drain: true }) // wait for in-flight and paced replies (up to `timeout`, 10 s by default)
process.exit(0)
})Can I have multiple bots in the same process?
Yes, with different providers/sessions:
const bot1 = new Bot({ provider: new BaileysProvider({ session: 'bot-1' }) })
const bot2 = new Bot({ provider: new BaileysProvider({ session: 'bot-2' }) })
await bot1.start()
await bot2.start()You can even mix transports — one Bot per provider (e.g. a Baileys bot and a CloudAPIProvider bot in the same process).
Does it work with WhatsApp Business?
Yes. The bot works with both personal and WhatsApp Business accounts.
Can I build a ChatGPT-style AI assistant on WhatsApp?
Not on the WhatsApp Business Platform. Since 2026-01-15 Meta's terms prohibit general-purpose AI assistants (open-ended chatbots whose main purpose is the AI itself). AI that serves your business — customer support, sales, bookings — is allowed. With @botmesh/ai, write a persona scoped to your business; see the AI plugin guide.
Can my account get banned?
WhatsApp can detect excessive automation. To minimize risk:
- Don't send spam or mass messages
- Use
markOnlineOnConnect: false(aBaileysProvideroption) - Respect rate limits (the provider's built-in limiter)
- Don't reply instantly to everything (turn on the typing indicator with
BotConfig.typing) - Consider the official Cloud API (
@botmesh/meta) for business-critical bots — no ban risk
Do I need TypeScript?
No. All examples work in plain JavaScript — just note the package is ESM-only, so your project needs "type": "module" in package.json (or a .mjs file) regardless of whether you use TypeScript. TypeScript gives you autocomplete and type-checking, but it's optional.
Why do I get ERR_REQUIRE_ESM or "require is not defined"?
You're trying to require('@botmesh/core') (or a transport package) from a CommonJS file. There is no CJS build — switch to import (add "type": "module" to your package.json, or use a .mjs extension). Node >= 22.12 can also require() an ESM package via its experimental require(esm) support.
Where is the full API reference?
At the API Reference — generated with TypeDoc from the source code. It covers @botmesh/core (every public export, including the internals-free /internal split); the channel, store and plugin packages are documented in their guide pages and in each package's README.md (packages/<pkg>/README.md).