This guide summarizes how to explore, modify, and validate the WhatsApp HTTP API (WAHA) codebase when assisting as an automation or coding agent.
- WAHA ships in Core and Plus editions
- Core lives under
src/coreand supports the default session with minimal media features - Plus extends core via
src/plusto add multi-session orchestration, richer media handling, and external storage integrations - Core code must remain free from Plus-only references (pre-commit hook rejects "plus" in core files)
- Commit subjects: changes that touch
src/plusrequire[PLUS] …prefix; everything else uses[core] …
- Runtime: Node.js 22.x, Yarn 3.6 (Berry)
- Framework: NestJS v11 with dependency injection and modular controllers in
src/api - Engines: WhatsApp engines are abstracted (
WEBJS,GOWS,NOWEB,WPP). Core usesSessionManagerCore; Plus swaps toSessionManagerPluswith extra storage backends (Mongo/Postgres/SQLite) - ESM Bridge: ESM-only dependencies (Baileys) load through
src/vendor/esm.ts - Utilities: RxJS streams drive webhook event fan-out. Prefer existing
helpers in
src/utilsandsrc/core/utils
src/main.ts: runtime entry point; dynamically loads AppModule (Core vs Plus)src/api/**: REST controllers and WebSocket gatewaysrc/core/**: shared abstractions (config services, engine bootstrap, storage, session management)src/plus/**: multi-session orchestration, advanced media services, and external persistence layerssrc/structures/**andsrc/utils/**: DTOs, enums (event names followdomain.action), helper utilities
- Favor composability and long-lived solutions
- Reuse existing helpers (
parseBool,DefaultMap, media factories) instead of reinventing logic - Stick to NestJS patterns: inject dependencies through constructors, expose provider tokens from modules
- Logging goes through injected
PinoLoggeror helpers insrc/utils/logging.ts - Respect path aliases (
@waha/...) defined intsconfig.json - Prefer named function declarations over
constarrow functions - Avoid naming unused variables with a leading underscore
- Always use explicit property names in object literals — never shorthand: write
{ key: value }, not{ value }(even when the variable name matches the key) - Do not write verbose ternaries; use idiomatic helpers like
??(nullish coalescing) - Do not place
awaitor other async calls inside ternary expressions (?:) or nullish-coalescing expressions (??); use explicitif/elseblocks or assign the awaited value to a variable first - For configs, prefer runtime configurability over constants (environment keys
follow
WAHA_*andWAHA_SESSION_CONFIG_*) - Do not use decorative comment blocks (lines of dashes/underscores with a
label) such as
// ─────────── NAME ───────────; use plain inline comments or no comment at all
export DEBUG=1
export WAHA_API_KEY=666
export WAHA_DASHBOARD_PASSWORD=666
export WAHA_DASHBOARD_USERNAME=admin
export WWHATSAPP_SWAGGER_USERNAME=admin
export WHATSAPP_SWAGGER_PASSWORD=666
export WHATSAPP_DEFAULT_ENGINE={WEBJS|WPP|NOWEB|GOWS}
export WAHA_DEBUG_MODE=True
export WAHA_HTTP_STRICT_MODE=1
export WAHA_MEDIA_STORAGE=LOCAL
export WHATSAPP_FILES_FOLDER=./.media
npm run start- Add
@Activity()(fromsrc/core/abc/activity.ts) to every engine method that makes a network call to WhatsApp servers - It triggers
maintainPresenceOnline()before the method runs, keeping the session ONLINE during API activity and scheduling an OFFLINE transition after an idle period - Skip it on methods that only throw
NotImplementedByEngineError/AvailableInPlusVersion
MCP tools live in src/apps/mcp/tools/ and expose the HTTP API to AI clients.
Each tool file mirrors an API domain (e.g. chats.tools.ts → chats endpoints).
When you change an existing API endpoint:
- Check the corresponding
*.tools.tsfile and update the tool'sinputSchema, description, or behavior if the API signature changed.
When you add a new API endpoint:
- Ask the user whether an MCP tool is needed for the new endpoint before creating one.
- If yes, add the tool to the matching
*.tools.tsfile (or create a new file for a new domain). - Every
@Tooldecorator must include anannotationsblock with all three fields:annotations: { readOnlyHint: true | false, // true = no side effects (GET-style) destructiveHint: true | false, // true = irreversible deletion/logout idempotentHint: true | false, // true = safe to repeat with same args }
- Input schemas live in the matching
*.zod.tsfile. - Tools call the API via
this.textRequest({ method, url, ... })inherited fromMcpController.
- WEBJS:
../whatsapp-web.js - NOWEB:
../WhiskeySockets-Baileysand../whatsapp-rust-bridge - GOWS:
../gowsand../whatsmeow - WPP:
../wa-js,../wppconnect,../wppconnect-server - ChatWoot:
../chatwoot