The Problem
Self-hosted WhatsApp integrations typically mean either paying for a managed API or wiring together unofficial libraries with hand-rolled auth, webhooks, and dashboards. OpenWA packages that into a single deployable gateway with an API-key auth layer, multi-session support, and a React admin UI.
What This Does
OpenWA is a NestJS-based WhatsApp API gateway. The backend in src/ exposes REST endpoints for messaging, groups, contacts, and labels, with pluggable storage (SQLite/PostgreSQL), cache (Memory/Redis), and object storage (Local/S3). The dashboard/ folder is a React/Vite admin panel for managing sessions, API keys, and webhooks. A small SDK in sdk/javascript and sdk/python wraps the REST API.
The architecture is adapter-based: src/engine/adapters/whatsapp-web-js.adapter.ts is the default WhatsApp engine, and src/engine/engine.factory.ts selects the active one. Plugins load from a directory at runtime via src/core/plugins/plugin-loader.service.ts.
How It Is Wired
Execution starts in src/main.ts (bootstrap), which initializes the NestJS app and reaches 21 functions. The critical path is short: bootstrap -> create -> createApiKey performs a randomBytes(32) call, and dispatch -> generateSignature does an HMAC digest for webhook verification. So a fresh deployment generates API keys and signs webhooks within two hops of boot.
The call graph shows three heavily reused functions: ensureReady (49 call sites), getEngine (48), and request (31). src/modules/session/session.service.ts is the integration point for the WhatsApp engine — 12 modules import it. The logger.service is a hub with 15 dependents but zero outgoing edges; changing its signature ripples across a third of the codebase. The module graph has no circular dependencies, which keeps refactoring tractable.
File-by-file: whatsapp-web-js.adapter.ts owns engine lifecycle (65 functions); src/modules/auth/auth.service.ts handles API key creation and HMAC signing; src/modules/infra/infra.controller.ts manages engine selection and config persistence; dashboard/src/services/api.ts is the frontend's typed client.
How To Use It
Setup: Requires Node 22 and npm. Docker Compose is the fastest path — docker-compose.yml exists with a mounted socket for containerized WhatsApp sessions.
Configuration: Copy .env.example to .env and set database, cache, and storage backends. There's also a committed .env.minimal — see the health section.
Running it:
npm install
npm run start:dev
# or
docker-compose up -d
The dashboard runs separately from dashboard/ via Vite. The SDK is published from sdk/javascript and sdk/python.
Real-World Use
A support team runs OpenWA on a single VPS with SQLite and local storage. The React dashboard lets them scan QR codes for multiple WhatsApp numbers, assign API keys per internal tool, and verify webhook delivery via HMAC signatures. The n8n integration (docs/22-n8n-integration.md) lets them trigger WhatsApp sends from workflow automation without writing custom HTTP calls.
Code Health & Issues
Static analysis found 15 findings (2 high, 13 medium). High: duplicated 6-line blocks across 17 dashboard files (e.g., ApiKeys.tsx, Webhooks.tsx), and deep nesting (max depth 8) in Plugins.tsx and ApiKeys.tsx. Medium: whatsapp-web-js.adapter.ts is 774 lines; api-key.guard.ts has 15 branch points over 53 lines. The dependency graph is clean — no cycles, and most modules have low instability.
The SDLC audit flagged three high-severity issues: GitHub Actions pinned to mutable tags (@v5, @v3) instead of commit SHAs; docker-compose.yml uses privileged mode with host networking; and .env.minimal is committed to the repo — rotate anything in it and git rm --cached it.
The Bottom Line
OpenWA is a credible, well-structured WhatsApp gateway with a real dashboard and SDKs. The codebase is organized and the module graph is clean. The security posture needs hardening before production — fix the pinned actions, the privileged container, and the committed .env first. It's a solid choice for teams that want full control over WhatsApp messaging without vendor lock-in.