Agent-readable wiki
OpenWA: The Friendly WhatsApp Gateway Blueprint
An open-source, zero lock-in API gateway that translates standard web requests into WhatsApp conversations, featuring a modular architecture and web dashboard.
Pages
- Explain It SimplyWhat OpenWA does in plain language: a post office analogy for WhatsApp automation, the core benefits of zero vendor lock-in, and the golden rules of the architecture.
- The Session Conductor (Connecting to WhatsApp)How OpenWA handles the magic of logging in: launching the Chromium browser behind the scenes, showing a QR code to scan, and keeping the connection alive like a persistent web socket.
- The Messaging Post Office (Sending & Receiving)How texts and media travel between your app and WhatsApp: sorting messages in the queue to avoid speed tickets, and ringing webhooks to deliver incoming messages instantly.
- The Plugs & Sockets (Database, Cache & Files)Why OpenWA is like a modular power strip: how it lets you plug in SQLite or PostgreSQL for data, local disks or S3 for files, and RAM or Redis for caching without rewiring the code.
- The Control Center (Dashboard & SDKs)The dashboard and SDK helpers that let you manage your WhatsApp army: scanning QR codes, monitoring system health, and sending API commands from your Python or JavaScript code.
- Plain English Recap & Next StepsThe ultimate one-minute summary of the OpenWA architecture: the postal worker analogy to keep, the golden lessons of rate limits, and the best blueprints to read next.
Complete Markdown
# OpenWA: The Friendly WhatsApp Gateway Blueprint
> An open-source, zero lock-in API gateway that translates standard web requests into WhatsApp conversations, featuring a modular architecture and web dashboard.
## Context Links
- [Agent index](https://grok-wiki.com/public/wiki/rmyndharis-openwa-2c9996a09a22/llms.txt)
- [Human interactive wiki](https://grok-wiki.com/public/wiki/rmyndharis-openwa-2c9996a09a22)
- [GitHub repository](https://github.com/rmyndharis/OpenWA)
## Repository Metadata
- Repository: rmyndharis/OpenWA
- Generated: 2026-05-22T06:02:40.708Z
- Updated: 2026-05-22T06:02:50.468Z
- Runtime: Antigravity CLI
- Format: Explain Like I'm 5
- Pages: 6
## Page Index
- 01. [Explain It Simply](https://grok-wiki.com/public/wiki/rmyndharis-openwa-2c9996a09a22/pages/01-explain-it-simply.md) - What OpenWA does in plain language: a post office analogy for WhatsApp automation, the core benefits of zero vendor lock-in, and the golden rules of the architecture.
- 02. [The Session Conductor (Connecting to WhatsApp)](https://grok-wiki.com/public/wiki/rmyndharis-openwa-2c9996a09a22/pages/02-the-session-conductor-connecting-to-whatsapp.md) - How OpenWA handles the magic of logging in: launching the Chromium browser behind the scenes, showing a QR code to scan, and keeping the connection alive like a persistent web socket.
- 03. [The Messaging Post Office (Sending & Receiving)](https://grok-wiki.com/public/wiki/rmyndharis-openwa-2c9996a09a22/pages/03-the-messaging-post-office-sending-receiving.md) - How texts and media travel between your app and WhatsApp: sorting messages in the queue to avoid speed tickets, and ringing webhooks to deliver incoming messages instantly.
- 04. [The Plugs & Sockets (Database, Cache & Files)](https://grok-wiki.com/public/wiki/rmyndharis-openwa-2c9996a09a22/pages/04-the-plugs-sockets-database-cache-files.md) - Why OpenWA is like a modular power strip: how it lets you plug in SQLite or PostgreSQL for data, local disks or S3 for files, and RAM or Redis for caching without rewiring the code.
- 05. [The Control Center (Dashboard & SDKs)](https://grok-wiki.com/public/wiki/rmyndharis-openwa-2c9996a09a22/pages/05-the-control-center-dashboard-sdks.md) - The dashboard and SDK helpers that let you manage your WhatsApp army: scanning QR codes, monitoring system health, and sending API commands from your Python or JavaScript code.
- 06. [Plain English Recap & Next Steps](https://grok-wiki.com/public/wiki/rmyndharis-openwa-2c9996a09a22/pages/06-plain-english-recap-next-steps.md) - The ultimate one-minute summary of the OpenWA architecture: the postal worker analogy to keep, the golden lessons of rate limits, and the best blueprints to read next.
## Source File Index
- `dashboard/src/App.tsx`
- `dashboard/src/pages/Infrastructure.tsx`
- `dashboard/src/pages/Sessions.tsx`
- `docs/03-system-architecture.md`
- `docs/15-project-roadmap.md`
- `docs/21-glossary.md`
- `package.json`
- `README.md`
- `sdk/README.md`
- `src/app.module.ts`
- `src/common/cache/cache.service.ts`
- `src/common/storage/storage.service.ts`
- `src/config/configuration.ts`
- `src/engine/adapters/whatsapp-web-js.adapter.ts`
- `src/engine/engine.factory.ts`
- `src/main.ts`
- `src/modules/infra/infra.controller.ts`
- `src/modules/message/message.controller.ts`
- `src/modules/message/message.service.ts`
- `src/modules/queue/processors/webhook.processor.ts`
- `src/modules/queue/queue.module.ts`
- `src/modules/session/session.controller.ts`
- `src/modules/session/session.service.ts`
- `src/modules/webhook/webhook.service.ts`
---
## 01. Explain It Simply
> What OpenWA does in plain language: a post office analogy for WhatsApp automation, the core benefits of zero vendor lock-in, and the golden rules of the architecture.
- Page Markdown: https://grok-wiki.com/public/wiki/rmyndharis-openwa-2c9996a09a22/pages/01-explain-it-simply.md
- Generated: 2026-05-22T05:57:37.932Z
### Source Files
- `README.md`
- `package.json`
- `src/main.ts`
- `src/app.module.ts`
<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:
- [README.md](README.md)
- [package.json](package.json)
- [src/main.ts](src/main.ts)
- [src/app.module.ts](src/app.module.ts)
- [src/engine/engine.factory.ts](src/engine/engine.factory.ts)
- [src/config/configuration.ts](src/config/configuration.ts)
- [src/common/cache/cache.service.ts](src/common/cache/cache.service.ts)
- [src/common/storage/storage.service.ts](src/common/storage/storage.service.ts)
- [src/engine/adapters/whatsapp-web-js.adapter.ts](src/engine/adapters/whatsapp-web-js.adapter.ts)
</details>
# Explain It Simply
OpenWA is a powerful, enterprise-grade, open-source WhatsApp API Gateway that lets you run your own WhatsApp automation infrastructure with absolute freedom. While WhatsApp’s official Cloud API charges per message, restricts your templates, and locks you into a rigid corporate billing structure, OpenWA turns your local server into an independent automation hub. This page translates the complexities of WhatsApp session management, NestJS controllers, and Puppeteer headless browser rendering into simple, easy-to-digest terms so anyone can understand how the platform is put together.
Whether you are an architect designing a scalable notification pipeline or a newcomer wondering how OpenWA controls multiple WhatsApp accounts simultaneously, this guide explains it simply. By exploring our relatable "post office" analogy, the principles behind our "zero vendor lock-in" commitment, and the strict rules governing OpenWA's internal code, you will gain a clear mental model of how this gateway works.
## The Post Office Analogy: How OpenWA Automates WhatsApp
To understand OpenWA, imagine WhatsApp is a massive global postal network. Every message sent is a physical letter, and every WhatsApp account is a physical mailbox. Normally, if a business wants to automate sending letters, they must hire an expensive, official courier service (the WhatsApp Business Cloud API) that charges a fee for every single letter, dictates strict rules on what can be written, and requires a lengthy corporate setup process.
OpenWA acts like a high-speed, local post office built directly inside your own warehouse. It allows you to run your own mail carrier operations at zero cost. Here is how its software modules map directly to the daily operations of a post office:
* **The Post Office (API Gateway)**: The main NestJS web server receives standard, digital postal instructions (HTTP REST API requests) from your internal software and translates them into actual message dispatches. It also issues real-time delivery receipts and updates (Webhooks) back to your systems.
* **The Mail Carriers (Sessions)**: Each WhatsApp account you connect represents an individual mail carrier with a dedicated mail truck (a Session). OpenWA is multi-session ready, meaning it can manage dozens of these mail carriers simultaneously, each carrying independent routes and mailbags.
* **The Sorting Machine (Browser Adapter)**: The WhatsApp Web.js Adapter acts as the engine's sorting machine. Since WhatsApp doesn't have an official free API, the adapter drives a headless Chrome browser (using Puppeteer). It logs into the WhatsApp Web interface via QR code authentication and automates the clicks and keypresses to send messages, just as a human mail sorter would physically stamp and sort letters.
* **The Mail Drawers & Safe (Database, Cache, and Storage)**: Pluggable storage drawers where carrier logs, media attachments (images, PDFs, videos), and transient data are temporarily stored or cached to keep the mailroom running fast.
Sources: [README.md:29-43](README.md#L29-L43), [src/engine/adapters/whatsapp-web-js.adapter.ts:67-108](src/engine/adapters/whatsapp-web-js.adapter.ts#L67-L108), [src/app.module.ts:137-162](src/app.module.ts#L137-L162)
```mermaid
flowchart TB
subgraph ClientAPI ["Client & External Ecosystem"]
UserApp["Your Application"]
WebhookConsumer["Webhook Listener / URL"]
end
subgraph OpenWA ["OpenWA Gateway (NestJS)"]
direction TB
MainApp["app.module.ts / main.ts<br>(API Gateway & Auth)"]
SessionMgr["SessionModule<br>(Session Manager)"]
EngineFactory["EngineFactory<br>(Adapter Router)"]
subgraph StorageLayer ["Pluggable Storage Infrastructure"]
direction LR
DBSelect{{"DATABASE_TYPE"}}
SQLiteMain[("main.sqlite<br>(System Auth/API Keys)")]
SQLiteData[("openwa.sqlite<br>(SQLite Adapter)")]
PostgreSQLData[("Postgres DB<br>(Postgres Adapter)")]
CacheService["CacheService<br>(Redis / In-Memory)"]
StorageService["StorageService<br>(Local Disk / S3 / MinIO)"]
end
subgraph SessionEngines ["Isolated WhatsApp Engines"]
direction TB
EngineInstance1["WhatsAppWebJsAdapter (Session 1)"]
Puppeteer1["Puppeteer Headless Browser 1"]
EngineInstance2["WhatsAppWebJsAdapter (Session 2)"]
Puppeteer2["Puppeteer Headless Browser 2"]
end
end
subgraph WhatsAppNet ["WhatsApp Infrastructure"]
WhatsAppServer["WhatsApp Servers"]
end
%% Client / Gateway flows
UserApp -- "1. HTTP API (X-API-Key)" --> MainApp
MainApp -- "2. Manage Session" --> SessionMgr
SessionMgr -- "3. Spawn Engine" --> EngineFactory
EngineFactory -- "4. Instantiate" --> EngineInstance1
EngineFactory -- "4. Instantiate" --> EngineInstance2
%% Storage hooks
MainApp ==> SQLiteMain
SessionMgr ==> DBSelect
DBSelect -.-> SQLiteData
DBSelect -.-> PostgreSQLData
EngineInstance1 -- "Saves Media" --> StorageService
EngineInstance1 -- "Caches State" --> CacheService
%% Engine / Puppeteer / WA
EngineInstance1 ==> Puppeteer1
EngineInstance2 ==> Puppeteer2
Puppeteer1 <-.-> WhatsAppServer
Puppeteer2 <-.-> WhatsAppServer
%% Webhook loop
EngineInstance1 -- "Events (Messages, status)" --> SessionMgr
SessionMgr -- "5. Deliver Webhooks" --> WebhookConsumer
```
Sources: [src/app.module.ts:40-163](src/app.module.ts#L40-L163), [src/engine/engine.factory.ts:63-110](src/engine/engine.factory.ts#L63-L110)
## Zero Vendor Lock-In: Bring Your Own Infrastructure
Unlike closed cloud services or proprietary gateway providers, OpenWA is fully committed to **Bring Your Own Infrastructure (BYOI)** and **Bring Your Own Keys (BYOK)**. It achieves this by decoupling the core application logic from the underlying storage, database, caching, and automation technologies. This design guarantees that you are never locked into any vendor, model provider, or commercial database license.
Here is a breakdown of how the pluggable adapters let you shape your infrastructure dynamically:
* **Pluggable Databases (BYOD)**: OpenWA splits database operations. System authentication and audit logging always go to a lightweight SQLite database (`main.sqlite`), ensuring instant startup. However, the user session, webhook, and message data can be routed dynamically to a production-grade PostgreSQL database simply by changing the `DATABASE_TYPE` env variable.
* **Flexible Cache Layers (BYOC)**: If you are running a single instance, OpenWA bypasses the cache or uses system memory. For high-scale multi-instance deployments, you can enable a distributed Redis cache dynamically without changing any codebase logic.
* **Custom Media Storage (BYOS)**: When WhatsApp media attachments are received, OpenWA can store them locally on the hard disk or stream them to an S3-compatible cloud storage bucket (like MinIO, AWS S3, or Cloudflare R2).
* **Engine Neutrality**: The entire WhatsApp driver interface is designed as an extensibility layer. Instead of coupling directly to Puppeteer or `whatsapp-web.js`, the NestJS application calls `EngineFactory`, which loads engines dynamically via a plugin system, keeping the architecture portable.
### Pluggable Architecture Matrix
| Infrastructure Component | Default Adapter | Pluggable/Alternative Adapters | Primary Config Variables | Decoupling Class / Interface |
| :--- | :--- | :--- | :--- | :--- |
| **Database** | SQLite (`sqlite`) | PostgreSQL (`postgres`) | `DATABASE_TYPE`, `DATABASE_HOST`, `DATABASE_PORT` | `TypeOrmModule.forRootAsync` |
| **Storage** | Local Filesystem (`local`) | AWS S3 / MinIO (`s3`) | `STORAGE_TYPE`, `STORAGE_PATH`, `S3_ENDPOINT` | `StorageService` |
| **Cache** | In-Memory (Redis disabled) | Redis Cache | `REDIS_ENABLED`, `REDIS_HOST`, `REDIS_PORT` | `CacheService` |
| **WhatsApp Engine** | WhatsApp Web.js | Upgradable plugin engine(s) | `ENGINE_TYPE` | `EngineFactory` & `IWhatsAppEngine` |
Sources: [src/app.module.ts:63-110](src/app.module.ts#L63-L110), [src/common/cache/cache.service.ts:39-48](src/common/cache/cache.service.ts#L39-L48), [src/common/storage/storage.service.ts:36-67](src/common/storage/storage.service.ts#L36-L67), [src/engine/engine.factory.ts:63-93](src/engine/engine.factory.ts#L63-L93)
## The Golden Rules of OpenWA's Architecture
To maintain high availability and seamless developer onboarding, OpenWA operates on four rigid architectural rules that govern how the codebase behaves:
### 1. The Boot Priority Rule (Configuration Hierarchy)
In OpenWA, explicit operator commands must always override saved system configurations. This is critical for containerized environments like Docker or Kubernetes. The bootstrap process loads environment variables in three prioritized steps:
1. **System Environment Variables**: Pre-configured variables in the container shell (highest priority).
2. **User `.env` Override**: The local project-level `.env` file managed by developers.
3. **Auto-Generated Dashboard Config (`.env.generated`)**: Saved settings written via the dashboard UI.
This ensures that even if a dashboard user misconfigures a database host, the DevOps administrator can override it using standard environment variables without editing dashboard files.
```typescript
// From src/main.ts
// 2. User-managed .env (does not override real process env)
if (fs.existsSync(userEnvPath)) {
console.log('[Bootstrap] Loading .env from:', userEnvPath);
dotenv.config({ path: userEnvPath, override: false });
}
// 3. Dashboard-saved config (does not override .env or process env)
if (fs.existsSync(generatedEnvPath)) {
console.log('[Bootstrap] Loading saved configuration from:', generatedEnvPath);
dotenv.config({ path: generatedEnvPath, override: false });
}
```
### 2. Dual-Database Separation
To prevent customer-facing transaction loads from slowing down vital security, routing, and access control validation, OpenWA maintains two distinct database connection pools. The system credentials (API keys and administrative settings) are safely isolated in an embedded database (`main.sqlite`), while customer message logs and session details are processed in the pluggable data database.
```typescript
// From src/app.module.ts
// Main Database (always SQLite - boot config)
TypeOrmModule.forRootAsync({
name: 'main',
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
type: 'sqlite' as const,
database: configService.get<string>('database.database', './data/main.sqlite'),
entities: [__dirname + '/modules/auth/**/*.entity{.ts,.js}', __dirname + '/modules/audit/**/*.entity{.ts,.js}'],
synchronize: true,
logging: configService.get<boolean>('database.logging', false),
}),
})
```
### 3. Isolated Web Browser Sandbox
Because WhatsApp Web must be rendered inside Chrome to authenticate, each session runs inside its own isolated headless Puppeteer browser process. If a specific session's browser crashes or stalls due to network issues, OpenWA isolates the failure. The `WhatsAppWebJsAdapter` handles setup, sandbox parameters, and credentials in an isolated `LocalAuth` session directory under `./data/sessions/{sessionId}`, preventing any cross-session interference or cascading system crashes.
### 4. Resilient Fallbacks over Fatal Crashes
No infrastructure component is treated as a single point of failure. If Redis is configured but fails to respond during bootstrap, the `CacheService` will not crash the server. Instead, it logs connection attempts and seamlessly shifts to in-memory handling. Similarly, storage adapters gracefully handle write failures, ensuring the gateway remains operational even during backend degradations.
Sources: [src/main.ts:11-37](src/main.ts#L11-L37), [src/app.module.ts:48-110](src/app.module.ts#L48-L110), [src/common/cache/cache.service.ts:54-99](src/common/cache/cache.service.ts#L54-L99), [src/engine/adapters/whatsapp-web-js.adapter.ts:72-108](src/engine/adapters/whatsapp-web-js.adapter.ts#L72-L108)
## Summary
By prioritizing a highly decoupled, pluggable architecture, OpenWA establishes a resilient, self-hosted alternative for WhatsApp integration. The gateway converts standard HTTP/REST request commands into isolated browser automation sessions, ensuring you retain total control over your customer interactions and private infrastructure without paying per-message developer fees. As shown during system bootstrap, OpenWA successfully orchestrates these services to expose secure, rate-limited, and developer-friendly REST endpoints.
Sources: [src/main.ts:162-167](src/main.ts#L162-L167)
---
## 02. The Session Conductor (Connecting to WhatsApp)
> How OpenWA handles the magic of logging in: launching the Chromium browser behind the scenes, showing a QR code to scan, and keeping the connection alive like a persistent web socket.
- Page Markdown: https://grok-wiki.com/public/wiki/rmyndharis-openwa-2c9996a09a22/pages/02-the-session-conductor-connecting-to-whatsapp.md
- Generated: 2026-05-22T05:58:41.381Z
### Source Files
- `src/modules/session/session.controller.ts`
- `src/modules/session/session.service.ts`
- `src/engine/engine.factory.ts`
- `src/engine/adapters/whatsapp-web-js.adapter.ts`
<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:
- [src/modules/session/session.controller.ts](src/modules/session/session.controller.ts)
- [src/modules/session/session.service.ts](src/modules/session/session.service.ts)
- [src/engine/engine.factory.ts](src/engine/engine.factory.ts)
- [src/engine/adapters/whatsapp-web-js.adapter.ts](src/engine/adapters/whatsapp-web-js.adapter.ts)
- [src/modules/session/entities/session.entity.ts](src/modules/session/entities/session.entity.ts)
- [src/plugins/engines/whatsapp-web-js/index.ts](src/plugins/engines/whatsapp-web-js/index.ts)
</details>
# The Session Conductor (Connecting to WhatsApp)
OpenWA acts as an intelligent coordinator for WhatsApp Web sessions, handling the complexities of browser automation, credential management, and real-time state synchronization. Decoupling application logic from the underlying browser instance ensures that WhatsApp connections remain stable, secure, and highly available.
At the core of this system is the **Session Conductor** workflow. It orchestrates headless Chromium browser instances behind the scenes using Puppeteer, translates raw credential strings into scannable QR codes, and employs a robust exponential backoff retry mechanism to heal broken connections automatically.
---
## 1. Introduction
For production-grade systems, maintaining stable, long-running connections to WhatsApp Web presents a unique set of challenges. Browser crashes, network timeouts, credential expiration, and sleep-state disconnections are commonplace. To mitigate these risks, OpenWA encapsulates the session lifecycle in a dedicated orchestration layer.
Rather than exposing complex browser internals directly to developers, OpenWA wraps session state changes in clean, API-accessible endpoints, pushes real-time notifications via WebSockets and webhooks, and manages auto-reconnections gracefully under the hood.
Sources: [src/modules/session/session.service.ts:28-46]()
---
## 2. Session States & Lifecycle Orchestration
OpenWA handles session status updates using a structured state machine mapped in a persistent relational database. Every connection progresses through defined lifecycle states, enabling operators to track browser boots, scans, and failures in real time.
### Database State Transitions
| State Name | Database String | Description |
| :--- | :--- | :--- |
| **Created** | `created` | Session record is stored in the database but the browser has not been spawned. |
| **Initializing** | `initializing` | Puppeteer is launching Chromium and prepping dependencies in the background. |
| **QR Ready** | `qr_ready` | Chromium successfully loaded the WhatsApp Web app and exposed an authentication QR code. |
| **Authenticating** | `authenticating` | The QR code was scanned and the credential exchange is being validated by WhatsApp servers. |
| **Ready** | `ready` | Credentials verified. The session is active, fully connected, and ready to send/receive messages. |
| **Disconnected** | `disconnected` | Connection was stopped by the operator or lost due to network/browser disruption. |
| **Failed** | `failed` | The initialization or credential handshakes crashed or timed out terminally. |
### Lifecycle State Machine Diagram
```mermaid
stateDiagram-v2
[*] --> created: Session Created
created --> initializing: Operator Starts Session (API)
initializing --> qr_ready: WhatsApp Requests Auth (QR Code)
qr_ready --> authenticating: User Scans QR
authenticating --> ready: Credentials Verified
initializing --> ready: Session Restored (LocalAuth Cache)
ready --> disconnected: Connection Lost / Stopped
disconnected --> initializing: Reconnection Triggered (Backoff)
initializing --> failed: Critical Puppeteer / Auth Failure
authenticating --> failed: Verification Timeout
```
Sources: [src/modules/session/entities/session.entity.ts:5-13](), [src/modules/session/session.service.ts:337-350]()
---
## 3. Spawning the Sandbox: Puppeteer & Chromium Launch
OpenWA implements a decoupled driver system that relies on plugins and adapter patterns to interface with automation libraries. When a session starts, OpenWA invokes the active engine plugin (defaulting to the built-in `whatsapp-web.js` engine wrapper) to create a concrete engine instance.
### Headless Browser Initialization
The adapter initializes a Puppeteer-controlled headless Chromium instance. To guarantee high performance, security, and resource isolation inside containerized environments (like Docker), the engine initializes the client with explicit sandboxing and performance flags:
```typescript
// Location: src/engine/adapters/whatsapp-web-js.adapter.ts
const puppeteerArgs = this.config.puppeteer?.args || [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-accelerated-2d-canvas',
'--no-first-run',
'--no-zygote',
'--disable-gpu',
];
```
### Proxy Support and Sandboxing Controls
If the session configuration contains proxy credentials, the adapter dynamically injects the `--proxy-server` argument into Puppeteer's startup array.
To prevent requiring QR code scans upon every single browser boot, OpenWA leverages a `LocalAuth` strategy. When initialized, the adapter maps a caching directory (`./data/sessions`) segmented by `clientId`. This cache retains all WhatsApp session tokens, indexed database states, and localStorage details on disk. When the browser launches next time, it restores the session invisibly, bypassing the QR scan phase entirely and transitioning straight from `initializing` to `ready`.
```typescript
// Location: src/engine/adapters/whatsapp-web-js.adapter.ts
this.client = new Client({
authStrategy: new LocalAuth({
clientId: this.config.sessionId,
dataPath: path.resolve(this.config.sessionDataPath),
}),
puppeteer: {
headless: this.config.puppeteer?.headless ?? true,
args: puppeteerArgs,
},
});
```
Sources: [src/engine/adapters/whatsapp-web-js.adapter.ts:71-107](), [src/plugins/engines/whatsapp-web-js/index.ts:36-62](), [src/engine/engine.factory.ts:94-110]()
---
## 4. Serving the Credentials: QR Code Generation
When WhatsApp Web requires a new device pairing, it issues a raw string containing the pairing token. The session coordinator captures this event, renders it, and exposes it through API and gateway layers.
### The QR Code Handshake
1. **Capture raw QR code**: The adapter listens to the `qr` event from the underlying automation client.
2. **Convert to Data URL**: Rather than storing raw strings, OpenWA converts the token into a base64-encoded Data URL using the `qrcode` library.
3. **Notify Subscribers**: The database record transitions to `qr_ready`, and the adapter calls `onQRCode(qrCode)` to trigger internal Hooks and emit real-time WebSocket state changes to all listening clients.
4. **API Polling/Retrieval**: The frontend retrieves the base64 QR payload by hitting the `GET /sessions/:id/qr` endpoint, rendering the visual code instantly for end-user scanning.
```typescript
// Location: src/engine/adapters/whatsapp-web-js.adapter.ts
this.client.on('qr', async (qr: string) => {
try {
this.qrCode = await qrcode.toDataURL(qr);
this.setStatus(EngineStatus.QR_READY);
this.callbacks.onQRCode?.(this.qrCode);
} catch (error) {
this.logger.error('Error generating QR code', String(error));
}
});
```
Sources: [src/engine/adapters/whatsapp-web-js.adapter.ts:114-122](), [src/modules/session/session.service.ts:440-461]()
---
## 5. Persistence and Resilience: Auto-Reconnection & Heartbeats
To operate robustly without manual intervention, OpenWA implements automated self-healing. When a network hiccup or WebSocket drop severs the connection, the engine catches the drop and begins a graceful recovery flow.
### Exponential Backoff Reconnection Logic
When a session drops, the adapter triggers the `onDisconnected` callback. The `SessionService` schedules a reconnection timer based on an exponential backoff algorithm with added random jitter. This jitter prevents "thundering herd" problems where multiple disconnected sessions hammer the network at exact simultaneous intervals:
$\text{Delay} = \text{baseDelay} \times 2^{\text{attempts}} + \text{Random Jitter (0-1000ms)}$
```typescript
// Location: src/modules/session/session.service.ts
private scheduleReconnect(id: string, session: Session): void {
const state = this.reconnectStates.get(id);
if (!state) return;
if (state.attempts >= state.maxAttempts) {
this.logger.error(`Max reconnect attempts reached for session: ${session.name}`, undefined, {
sessionId: id,
attempts: state.attempts,
action: 'reconnect_failed',
});
return;
}
// Exponential backoff: baseDelay * 2^attempts (with jitter)
const delay = state.baseDelay * Math.pow(2, state.attempts) + Math.random() * 1000;
state.attempts++;
state.timer = setTimeout(() => {
void this.executeReconnect(id, session, state);
}, delay);
}
```
During `executeReconnect`, the system destroys the old adapter instance, releases Puppeteer resources, launches a fresh engine, and initiates a clean Chromium launch.
### Real-Time Event Sync via WebSockets and Webhooks
To keep external dashboards and downstream services in sync during connections, disconnections, and errors, OpenWA leverages two distinct communication structures:
* **WebSockets (`EventsGateway`)**: Emits real-time state changes (`emitSessionStatus`) so front-end dashboards update their connection meters instantly without API polling.
* **Webhooks (`WebhookService`)**: Dispatches secure HTTP POST webhooks to downstream external platforms, alerting them of disconnect actions or incoming message payloads immediately.
Sources: [src/modules/session/session.service.ts:20-25](), [src/modules/session/session.service.ts:315-386]()
---
## 6. Architecture Flow Blueprint
The following sequence diagram maps out how an API call to start a session maps into a running browser, handles QR code pairing, and establishes messages event routing:
```mermaid
sequenceDiagram
autonumber
participant API as API Client / Dashboard
participant Ctrl as SessionController
participant Svc as SessionService
participant Fact as EngineFactory
participant Adp as WhatsAppWebJsAdapter
participant Pupp as Puppeteer (Chromium)
API->>Ctrl: POST /sessions/:id/start
Ctrl->>Svc: start(id)
Svc->>Fact: create(options)
Fact-->>Svc: returns WhatsAppWebJsAdapter
Svc->>Adp: initialize(callbacks)
Adp->>Pupp: Launch headless Chromium with LocalAuth
Pupp-->>Adp: Browser instance active
alt Session data not found / expired (Fresh Pairing)
Adp-->>Svc: onQRCode(qrCode)
Svc->>API: Emit 'session:qr' via WebSocket / Update DB to 'qr_ready'
else Session data restored from disk (Auto-Login)
Adp-->>Svc: onReady(phone, pushName)
Svc->>API: Emit 'session:ready' via WebSocket / Update DB to 'ready'
end
Note over Pupp, Adp: User scans QR if needed, session transitions to Ready
alt Inbound Message Event
Pupp->>Adp: Raw message event
Adp->>Svc: onMessage(IncomingMessage)
Svc->>Svc: Execute Plugin Hooks
Svc->>API: Dispatch HTTP Webhook & WebSocket Event
end
```
Sources: [src/modules/session/session.controller.ts:94-114](), [src/modules/session/session.service.ts:220-354]()
---
## 7. Developer Conventions & Multi-Engine Portability
To keep the platform's architecture clean, provider-neutral, and extensible, OpenWA structures its engine layers around interfaces. The `EngineFactory` queries registered engine plugins dynamically from the `PluginLoaderService` using a portable system.
Developers looking to introduce alternative connection mechanisms (e.g., Baileys or native web-socket wrappers) can implement the standard `IWhatsAppEngine` interface and register their plugin under `PluginType.ENGINE`. This ensures the session orchestrator remains entirely unaffected by changes to underlying browser drivers or protocol implementations, keeping the repository's core logic lean and fully modular.
Sources: [src/engine/engine.factory.ts:15-25](), [src/engine/engine.factory.ts:63-81]()
---
## 03. The Messaging Post Office (Sending & Receiving)
> How texts and media travel between your app and WhatsApp: sorting messages in the queue to avoid speed tickets, and ringing webhooks to deliver incoming messages instantly.
- Page Markdown: https://grok-wiki.com/public/wiki/rmyndharis-openwa-2c9996a09a22/pages/03-the-messaging-post-office-sending-receiving.md
- Generated: 2026-05-22T05:59:45.729Z
### Source Files
- `src/modules/message/message.controller.ts`
- `src/modules/message/message.service.ts`
- `src/modules/webhook/webhook.service.ts`
- `src/modules/queue/processors/webhook.processor.ts`
- `src/modules/queue/queue.module.ts`
<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:
- [src/modules/message/message.controller.ts](src/modules/message/message.controller.ts)
- [src/modules/message/message.service.ts](src/modules/message/message.service.ts)
- [src/modules/message/bulk-message.service.ts](src/modules/message/bulk-message.service.ts)
- [src/modules/webhook/webhook.service.ts](src/modules/webhook/webhook.service.ts)
- [src/modules/queue/processors/webhook.processor.ts](src/modules/queue/processors/webhook.processor.ts)
- [src/modules/queue/queue.module.ts](src/modules/queue/queue.module.ts)
</details>
# The Messaging Post Office (Sending & Receiving)
At the heart of the OpenWA repository is its messaging infrastructure—conceptualized as a highly organized "Messaging Post Office." Managing communications between a modern web application and the WhatsApp network is a delicate balance. To prevent sending accounts from receiving WhatsApp speed bans (spam and speed tickets), the system must carefully queue, delay, and throttle outgoing bulk campaigns. Simultaneously, to ensure application responsiveness, incoming events and messages must be delivered instantly and reliably via webhooks without losing data during traffic spikes.
This page explains how texts, images, and other media travel between your application and WhatsApp. It details the lifecycle of direct single messages, the rate-limiting and template-interpolation mechanisms used in bulk campaigns, and the Redis-backed BullMQ event-driven webhook architecture that guarantees delivery of incoming events.
---
## Outgoing Messaging Workflow
Outgoing mail is split into two primary lanes: **Single Direct Messaging** (sent synchronously with immediate feedback) and **Bulk Messaging** (processed asynchronously with controlled pacing). Both lanes interact with a lifecycle hook manager, enabling external plugins to intercept, mutate, or block messages before they touch the WhatsApp network.
### Single Direct Messaging
When your application triggers a standard send request (e.g., text, image, video, location, or document), the API flows from the controller directly through to the active WhatsApp engine. The lifecycle follows a synchronous sequence:
1. **API Entry:** The consumer calls an operational endpoint like `/sessions/:sessionId/messages/send-text` or `/sessions/:sessionId/messages/send-image`.
2. **Pre-Send Hook Inspection:** The controller delegates to `MessageService`, which immediately triggers the `message:sending` hook. Registered plugins can examine the payload, modify the message content (e.g., stripping sensitive information), or cancel the delivery entirely.
3. **Database Pending State:** If the hooks permit execution, the service saves the outgoing message record in the database with a status of `MessageStatus.PENDING`.
4. **Engine Delivery:** The active session engine (e.g., Baileys or similar WhatsApp Web wrapper) attempts to transmit the message over the WhatsApp network.
5. **Post-Send Outcomes:**
- On **success**, the database record is updated to `SENT`, the real `waMessageId` returned by WhatsApp is saved, and a `message:sent` hook fires.
- On **failure**, the record transitions to `FAILED` and a `message:failed` hook is executed, alerting plugins and system logs.
```typescript
// From src/modules/message/message.service.ts
const { continue: shouldContinue, data: hookData } = await this.hookManager.execute(
'message:sending',
{ sessionId, input: dto, type: 'text' },
{ sessionId, source: 'MessageService' },
);
if (!shouldContinue) {
throw new BadRequestException('Message sending blocked by plugin');
}
// Save message as pending BEFORE sending
const message = await this.saveOutgoingMessage(sessionId, {
chatId: finalDto.chatId,
body: finalDto.text,
type: 'text',
});
```
Sources: [src/modules/message/message.controller.ts:41-137](src/modules/message/message.controller.ts#L41-L137), [src/modules/message/message.service.ts:25-207](src/modules/message/message.service.ts#L25-L207)
### Bulk Messaging and Rate Limiting
Sending messages to many recipients simultaneously without rate limits is the fastest way to get a phone number flagged and banned. To circumvent this, the "Post Office" includes a specialized **Bulk Messaging Engine** that handles campaigns asynchronously with deliberate delays.
- **Asynchronous Processing:** When a batch is posted, the `BulkMessageService` initializes a `MessageBatch` database entity with a progress tracker (tracking `total`, `sent`, `failed`, `pending`, and `cancelled` counts) and immediately returns an operational tracking URL to the client.
- **Variable Template Interpolation:** Message text or media captions can include variables in the format `{variableName}`. The bulk process evaluates these parameters dynamically per recipient (e.g., inserting names or order numbers) before calling the engine.
- **Pacing Control & Deliberate Delay:** The processor loops through the batch and introduces a sleep delay between successive messages. This delay is computed based on `delayBetweenMessages` (defaulting to `3000`ms) and is combined with `randomizeDelay` (which appends a random variance of 0–2 seconds). This mimics human-like behavior and avoids triggering anti-spam alerts.
- **Mid-Campaign Cancellation:** Campaigns are tracked in-memory using an active batch map. If the client calls the cancellation endpoint, the engine immediately flags the batch, marks remaining messages as `cancelled`, updates the state in the database, and halts the background processing loop.
```typescript
// From src/modules/message/bulk-message.service.ts
// Delay before next message (except for last) to avoid rate limits
if (i < batch.messages.length - 1 && this.processingBatches.get(batch.id)) {
const delay = this.calculateDelay(batch.options);
await this.sleep(delay);
}
```
Sources: [src/modules/message/message.controller.ts:284-364](src/modules/message/message.controller.ts#L284-L364), [src/modules/message/bulk-message.service.ts:37-86](src/modules/message/bulk-message.service.ts#L37-L86), [src/modules/message/bulk-message.service.ts:128-221](src/modules/message/bulk-message.service.ts#L128-L221)
---
## Direct vs. Bulk Messaging Comparison
| Feature | Single Messaging | Bulk Messaging |
| :--- | :--- | :--- |
| **Execution Context** | Synchronous (immediate HTTP response) | Asynchronous (processed in background) |
| **API Endpoint** | `/sessions/:sessionId/messages/send-text` | `/sessions/:sessionId/messages/send-bulk` |
| **Primary Class** | `MessageService` | `BulkMessageService` |
| **Rate Throttling** | None (instantaneous execution) | Multi-second delay + randomized human jitter |
| **Content Templating**| Standard static input payload | Dynamic `{variable}` key replacement per message |
| **Cancellation** | Not applicable (instant) | Cancellable at any time during execution |
---
## Incoming Messaging and Webhook Delivery
When an incoming text or media file is received by the WhatsApp session, the system rings configured external webhooks. To guarantee that webhook payloads are never lost and are delivered in the exact order they occurred, OpenWA uses a Redis-backed BullMQ queueing structure.
```mermaid
flowchart TD
subgraph Client ["Client Target & API Consumer"]
A[API Client]
B[External Webhook Endpoint]
end
subgraph API ["OpenWA NestJS Instance"]
C[MessageController]
D[WebhookService]
E[MessageService]
end
subgraph Engine ["WhatsApp Core"]
F[WhatsApp Engine Instance]
G[WhatsApp Network]
end
subgraph DB ["Data Store"]
H[(Database - Messages & Webhooks)]
end
subgraph QueueServer ["Queue System (Redis & BullMQ)"]
I[(Redis Database)]
J[BullMQ Webhook Queue]
K[WebhookProcessor]
end
%% Direct Outgoing Flow
A -- "1. Post single message" --> C
C -- "2. Trigger sending hooks & save PENDING" --> E
E -- "3. Execute send" --> F
F -- "4. Network dispatch" --> G
F -- "5. Update to SENT & save ID" --> E
E -- "6. Return HTTP 201" --> C
%% Incoming Webhook Flow
G -- "A. Deliver incoming event" --> F
F -- "B. Save incoming message" --> E
E -- "C. Save to DB" --> H
F -- "D. Dispatch event" --> D
D -- "E. Fetch active webhooks" --> H
D -- "F. Push webhook job with headers & retry settings" --> J
J -- "G. Cache job data" --> I
K -- "H. Process job & fetch status" --> J
K -- "I. POST with Signature & Retry Count" --> B
K -- "J. On Success: Update lastTriggeredAt" --> H
```
Sources: [src/modules/queue/queue.module.ts:16-46](src/modules/queue/queue.module.ts#L16-L46)
### Webhook Dispatch and Idempotency
When the active engine generates a new event (such as `message.received`), `WebhookService` handles dispatching:
1. **Webhook Matching:** The service queries all active webhook targets registered for the session. Webhooks are selected if their event subscription array contains the specific incoming event or the wildcard `*` symbol.
2. **Idempotency Safeguard:** To prevent receiving endpoints from processing duplicate notifications, the system generates a single deterministic `idempotencyKey` based on the event details and session information. This key is identical for all registered webhooks receiving that specific event.
3. **Unique Delivery Tracking:** Each destination webhook gets a unique, freshly generated `deliveryId` for precise logs.
4. **Before-Dispatch Hook:** The `webhook:before` hook lets plugins inspect or alter the final payload structure.
Sources: [src/modules/webhook/webhook.service.ts:155-207](src/modules/webhook/webhook.service.ts#L155-L207)
### Reliable Queue Delivery (Redis & BullMQ)
If queue processing is enabled (`queue.enabled` set to `true`), direct synchronous dispatch is skipped. The webhook is added to BullMQ:
- The job is pushed into the `QUEUE_NAMES.WEBHOOK` (which resolves to `'webhook'`) queue.
- It includes the destination URL, custom headers, payload, HMAC signature, and retry settings.
- BullMQ is configured to automatically perform exponential backoffs using the configured `webhook.retryDelay` interval (defaulting to `5000`ms) to handle intermittent outages on the receiving end.
Sources: [src/modules/webhook/webhook.service.ts:209-262](src/modules/webhook/webhook.service.ts#L209-L262), [src/modules/queue/queue.module.ts:16-46](src/modules/queue/queue.module.ts#L16-L46)
### Webhook Processor and Retry Backoff
The `WebhookProcessor` acts as the worker consuming jobs from Redis. For each job, it runs a target execution sequence:
1. **Header Updates:** It sets the `X-OpenWA-Retry-Count` header to the current attempt number (`job.attemptsMade`).
2. **Payload Security Signing:** If the webhook has an associated secret key, the processor creates a digital signature of the JSON body via HMAC-SHA256 and attaches it to the `X-OpenWA-Signature` header. This allows the receiving server to verify that the payload is authentic and was sent by your OpenWA instance.
3. **HTTP Fetch Delivery:** It attempts an HTTP POST request to the destination URL with a strict 10-second timeout.
4. **Successful Delivery:** If the response returns an HTTP 2xx code, the worker updates the `lastTriggeredAt` field of the webhook configuration in the database and triggers the `webhook:delivered` plugin hook.
5. **Failure & Re-Queueing:** If the endpoint returns an error or times out, the processor catches it:
- If the job has not reached its maximum retries, the worker logs the issue and throws an error. This signals BullMQ to schedule the job for a retry using exponential backoff.
- If it was the final attempt, the error is logged and the `webhook:error` plugin hook is executed to handle the definitive failure.
```typescript
// From src/modules/queue/processors/webhook.processor.ts
const requestHeaders = {
...headers,
'X-OpenWA-Retry-Count': String(job.attemptsMade),
};
try {
const response = await fetch(url, {
method: 'POST',
headers: requestHeaders,
body: JSON.stringify(payload),
signal: AbortSignal.timeout(10000),
});
const responseTime = Date.now() - startTime;
const success = response.ok;
if (!success) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
// ... Handle success and lastTriggeredAt updates
} catch (error) {
// ... Handle exponential retry propagation or final webhook:error hook
throw error;
}
```
Sources: [src/modules/queue/processors/webhook.processor.ts:30-136](src/modules/queue/processors/webhook.processor.ts#L30-L136)
### Direct Delivery Fallback
If Redis or the queue is disabled in the server configurations, `WebhookService` falls back to direct synchronous HTTP delivery. While this avoids a Redis dependency, it does not offer background queue persistence. It executes a synchronous loop with a custom backoff delay, trying to deliver the message until it succeeds or exhausts the retry configuration.
Sources: [src/modules/webhook/webhook.service.ts:264-354](src/modules/webhook/webhook.service.ts#L264-L354)
---
## Summary
The OpenWA messaging post office bridges the gap between your application logic and the real-time requirements of the WhatsApp protocol. Single messages are dispatched through a rich, plugin-extensible hook pipeline, while bulk campaigns are safely distributed with configurable random delays to protect sending numbers from WhatsApp speed violations. Incoming events are secured through cryptographic HMAC-SHA256 signatures, equipped with idempotency keys to prevent duplicates, and queued using Redis and BullMQ to guarantee that no webhook notifications are lost during peak traffic.
Sources: [src/modules/message/message.service.ts:469-475](src/modules/message/message.service.ts#L469-L475)
---
## 04. The Plugs & Sockets (Database, Cache & Files)
> Why OpenWA is like a modular power strip: how it lets you plug in SQLite or PostgreSQL for data, local disks or S3 for files, and RAM or Redis for caching without rewiring the code.
- Page Markdown: https://grok-wiki.com/public/wiki/rmyndharis-openwa-2c9996a09a22/pages/04-the-plugs-sockets-database-cache-files.md
- Generated: 2026-05-22T06:00:44.439Z
### Source Files
- `src/common/storage/storage.service.ts`
- `src/common/cache/cache.service.ts`
- `src/modules/infra/infra.controller.ts`
- `src/config/configuration.ts`
<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:
- [src/common/storage/storage.service.ts](src/common/storage/storage.service.ts)
- [src/common/cache/cache.service.ts](src/common/cache/cache.service.ts)
- [src/modules/infra/infra.controller.ts](src/modules/infra/infra.controller.ts)
- [src/config/configuration.ts](src/config/configuration.ts)
- [src/app.module.ts](src/app.module.ts)
</details>
# The Plugs & Sockets (Database, Cache & Files)
Imagine you have a premium modular power strip. Depending on what country you are in, or how much power you need, you can plug in different kinds of plugs. You don't rebuild your house's electrical wiring just because you plugged in a laptop charger instead of a lamp.
OpenWA is designed exactly like this modular power strip. It divides the complex job of running a WhatsApp automation server into three distinct "sockets": **Database (Data Persistence)**, **Cache (Speed & Session Status)**, and **Files (Media Storage)**. Through simple environment variables, you can plug in high-performance enterprise systems like PostgreSQL, Redis, and Amazon S3 when deploying in production, or stick to simple, zero-configuration local options like SQLite, local memory, and your local hard drive when running on your personal computer—all without changing a single line of the server's codebase.
```mermaid
flowchart TD
subgraph Client ["Client Interface & REST API"]
IC["infra.controller.ts <br/> (Infrastructure API & Migrations)"]
end
subgraph Logic ["Application Layer"]
appModule["app.module.ts <br/> (Dynamic Modules)"]
storageService["storage.service.ts <br/> (Agnostic Streams)"]
cacheService["cache.service.ts <br/> (Resilient Connections)"]
end
subgraph Persistence ["Sockets: Plug-and-Play Infrastructure"]
subgraph Database ["Database Socket"]
sqliteDB[("Local SQLite <br/> (main.sqlite / openwa.sqlite)")]
postgresDB[("PostgreSQL <br/> (External/Built-in Container)")]
end
subgraph Caching ["Cache Socket"]
ramCache["Memory Cache <br/> (Graceful Fallback)"]
redisCache[("Redis Cache <br/> (ioredis Client)")]
end
subgraph Storage ["File Storage Socket"]
localDisk["Local File System <br/> (./data/media)"]
s3Bucket[("S3 / MinIO Cloud Storage <br/> (AWS S3 Client)")]
end
end
IC --> appModule
IC --> storageService
IC --> cacheService
appModule -.-> |"TypeOrmModule (data connection)"| sqliteDB
appModule -.-> |"TypeOrmModule (data connection)"| postgresDB
storageService -.-> |"STORAGE_TYPE = local"| localDisk
storageService -.-> |"STORAGE_TYPE = s3"| s3Bucket
cacheService -.-> |"REDIS_ENABLED = false (Fallback)"| ramCache
cacheService -.-> |"REDIS_ENABLED = true"| redisCache
```
---
## Pluggable Data Persistence (Database)
OpenWA maintains a clever separation of concerns using two distinct database connections inside NestJS:
1. **The Main Boot Database (`main`)**: This connection is dedicated strictly to low-write credentials (like API keys) and security logs. It *always* uses SQLite (`./data/main.sqlite`), ensuring that the system can boot and authenticate users even if external network systems are temporarily down.
2. **The Data Storage Database (`data`)**: This is the pluggable "socket." It houses all dynamic, high-churn application data: session records, active webhooks, messages, and message batches.
Depending on the `DATABASE_TYPE` environment variable, NestJS instantiates the correct connector:
* **SQLite (Local Development)**: Highly portable and file-based (`./data/openwa.sqlite`). To make the newcomer experience effortless, SQLite is set to `synchronize: true`, meaning the database tables are auto-generated on first boot without any manual migration commands.
* **PostgreSQL (Production)**: When scaled up, it connects to an external or built-in Postgres database using connection pooling. To guarantee data integrity in production, auto-synchronization is strictly disabled (`synchronize: false`). Instead, OpenWA automatically detects and runs safe migration scripts sequentially on startup (`migrationsRun: true`) to preserve production safety.
```typescript
// From src/app.module.ts
if (dbType === 'postgres') {
return {
...baseConfig,
type: 'postgres' as const,
host: configService.get<string>('dataDatabase.host'),
port: configService.get<number>('dataDatabase.port'),
username: configService.get<string>('dataDatabase.username'),
password: configService.get<string>('dataDatabase.password'),
database: 'openwa',
// Never auto-sync Postgres in production; rely on migrations.
synchronize: configService.get<boolean>('dataDatabase.synchronize', false),
migrationsRun: true,
retryAttempts: 10,
retryDelay: 3000,
extra: {
max: configService.get<number>('dataDatabase.poolSize', 10),
},
};
}
```
Sources: [src/app.module.ts:49-110](), [src/config/configuration.ts:21-46]()
---
## Swappable Cache & Session Store (Cache)
WhatsApp automation demands extremely fast reads and writes. For instance, the system needs to immediately fetch whether a session is `CONNECTED` or check an active QR code stream. Reading this from a persistent database every second would create a heavy performance bottleneck.
The `CacheService` acts as the cache socket. It checks if `REDIS_ENABLED` is set to `true`:
* **Redis Mode**: If enabled, the service uses `ioredis` to manage highly efficient key-value lookups in a dedicated Redis container. The service features built-in resilience: it will attempt to connect up to 5 times (`maxConnectionAttempts = 5`), rate-limits reconnect attempts to protect network sockets, and handles connections lazily so a slow-starting container won't crash the server.
* **RAM Fallback (Offline Mode)**: If `REDIS_ENABLED` is `false`, the cache layer acts as a silent, non-blocking pass-through. Instead of throwing runtime errors, it gracefully returns `null` or skips writing cache keys, allowing the server to continue running directly off the database.
Each stored cache item relies on strict Time-To-Live (TTL) constants:
```typescript
// From src/common/cache/cache.service.ts
const TTL = {
SESSION_STATUS: 300, // 5 min
SESSION_INFO: 600, // 10 min
SESSION_QR: 60, // 1 min
SESSIONS_LIST: 30, // 30 sec
SESSIONS_STATS: 15, // 15 sec
};
```
These values ensure that highly volatile data (like QR codes and statistics) is refreshed frequently, while static details (like session info) persist longer, maximizing response speed.
Sources: [src/common/cache/cache.service.ts:21-48](), [src/common/cache/cache.service.ts:119-128](), [src/config/configuration.ts:4-19]()
---
## Flexible File Management (Files)
When automation bots receive images, documents, or voice notes from WhatsApp, they must be stored reliably. The `StorageService` abstracts this under a unified interface, supporting two pluggable backends selected via `STORAGE_TYPE`:
* **Local Disk (`local`)**: Media files are stored inside a local folder structure (defaults to `./data/media`). The service automatically handles path routing, recursive directory creation (`fs.mkdirSync`), and reads/writes using standard Node.js `fs` streams.
* **AWS S3 or MinIO (`s3`)**: In containerized or distributed deployments, media files are pushed directly to an S3-compatible cloud storage bucket. On boot, `StorageService` runs a `HeadBucketCommand` via the AWS S3 SDK. If the target bucket doesn't exist, the system creates it automatically (`CreateBucketCommand`), offering a fully hands-free setup.
```typescript
// From src/common/storage/storage.service.ts
async putFile(filePath: string, data: Buffer): Promise<void> {
if (this.storageType === 's3' && this.s3Client && this.s3Available) {
return this.putS3File(filePath, data);
}
return this.putLocalFile(filePath, data);
}
```
### Portability & Tarball Migrations
A major risk of using different file stores is vendor lock-in. OpenWA solves this by incorporating a high-performance backup/migration system directly inside the `StorageService`. Using the `archiver` and `tar-stream` libraries, the service can compress all files from the active storage backend (whether S3 or local disk) into a gzip-compressed tarball stream (`.tar.gz`), or extract an incoming stream and write files to the active backend.
Sources: [src/common/storage/storage.service.ts:36-67](), [src/common/storage/storage.service.ts:112-124](), [src/common/storage/storage.service.ts:148-220](), [src/config/configuration.ts:80-91]()
---
## The Management Console (Infrastructure Control API)
Connecting these sockets is the `InfraController`, a dedicated dashboard API which acts as the main system plugboard. It allows developers to check status, adjust environment settings, backup databases, or even trigger service restarts on the fly:
* **System Health Monitor (`GET /infra/status`)**: Aggregates connectivity states across all modules—reporting whether the primary and secondary databases are initialized, if Redis is reachable, the active storage engine type, and Puppeteer engine details.
* **Configuration Writer (`PUT /infra/config`)**: Receives payload data containing new credentials and builds a formatted `.env.generated` file in the persistent `/data` directory, persisting settings across container lifecycles.
* **Service Orchestration (`POST /infra/restart`)**: Communicates with the local Docker daemon via `DockerService`. If profiles have changed (e.g., PostgreSQL enabled, SQLite disabled), it orchestrates starting new service containers, removing unused ones, and scheduling a clean graceful shutdown (`shutdownService.shutdown(3000)`) so the app can reboot with the new profiles.
* **Neutral Data Portability (`/infra/export-data` and `/infra/import-data`)**: To migrate data between SQLite and PostgreSQL, this controller performs a complete transaction-safe export/import of all user tables (`sessions`, `webhooks`, `messages`, `message_batches`) using plain environment-neutral JSON.
Sources: [src/modules/infra/infra.controller.ts:152-190](), [src/modules/infra/infra.controller.ts:206-425](), [src/modules/infra/infra.controller.ts:438-664]()
---
## Configuration Reference Matrix
To easily plug and unplug services, use the following environment variable matrix:
| Socket Type | Environment Variable | Accepted Values | Default Value | Description |
| :--- | :--- | :--- | :--- | :--- |
| **Database** | `DATABASE_TYPE` | `sqlite`, `postgres` | `sqlite` | Selects which persistence connector to load for user data. |
| **Database** | `POSTGRES_BUILTIN` | `true`, `false` | `false` | Instructs the orchestrator to spin up the local PostgreSQL container. |
| **Database** | `DATABASE_SYNCHRONIZE` | `true`, `false` | `false` | Toggles automatic schema sync (enabled by default for local SQLite). |
| **Cache** | `REDIS_ENABLED` | `true`, `false` | `false` | Activates Redis connection for queueing and status caching. |
| **Cache** | `REDIS_BUILTIN` | `true`, `false` | `false` | Launches a built-in Redis container via the docker profile orchestrator. |
| **Files** | `STORAGE_TYPE` | `local`, `s3` | `local` | Directs how media attachments are stored (hard drive vs cloud). |
| **Files** | `MINIO_BUILTIN` | `true`, `false` | `false` | Spins up a built-in local S3-compatible MinIO object store container. |
| **Files** | `STORAGE_LOCAL_PATH` | File System Path | `./data/media` | Target folder for media file writes in `local` mode. |
| **Files** | `S3_ENDPOINT` | URL | *None* | Connection endpoint for S3 / MinIO storage APIs. |
Sources: [src/config/configuration.ts:4-46](), [src/config/configuration.ts:80-91](), [src/modules/infra/infra.controller.ts:206-336]()
---
## Summary & Design Philosophy
OpenWA's architecture treats third-party systems as secondary adapters rather than foundational dependencies. By strictly wrapping databases behind NestJS data-source scopes, caching under connection-resilient abstraction services, and file I/O behind path-agnostic streams, the application remains incredibly portable, developer-friendly, and simple to maintain. This design guarantees that whether you are developing offline on a train or scaling to millions of messages in a cloud cluster, your software remains exactly the same.
Sources: [src/app.module.ts:62-110](), [src/common/cache/cache.service.ts:39-48](), [src/common/storage/storage.service.ts:36-67]()
---
## 05. The Control Center (Dashboard & SDKs)
> The dashboard and SDK helpers that let you manage your WhatsApp army: scanning QR codes, monitoring system health, and sending API commands from your Python or JavaScript code.
- Page Markdown: https://grok-wiki.com/public/wiki/rmyndharis-openwa-2c9996a09a22/pages/05-the-control-center-dashboard-sdks.md
- Generated: 2026-05-22T06:01:40.241Z
### Source Files
- `dashboard/src/App.tsx`
- `dashboard/src/pages/Sessions.tsx`
- `dashboard/src/pages/Infrastructure.tsx`
- `sdk/README.md`
<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:
- [dashboard/src/App.tsx](dashboard/src/App.tsx)
- [dashboard/src/pages/Sessions.tsx](dashboard/src/pages/Sessions.tsx)
- [dashboard/src/pages/Infrastructure.tsx](dashboard/src/pages/Infrastructure.tsx)
- [dashboard/src/services/api.ts](dashboard/src/services/api.ts)
- [sdk/README.md](sdk/README.md)
- [sdk/javascript/src/index.ts](sdk/javascript/src/index.ts)
- [sdk/python/openwa/__init__.py](sdk/python/openwa/__init__.py)
</details>
# The Control Center (Dashboard & SDKs)
To manage an army of WhatsApp instances efficiently, you need a single, centralized command post. OpenWA provides this via **The Control Center**: a visual React-based administrative dashboard and a set of lightweight, official SDK client libraries for JavaScript/TypeScript and Python. Together, these tools let you deploy new WhatsApp sessions, scan connection QR codes in real-time, audit system infrastructure logs, and programmatically orchestrate communication flows directly from your own application backend.
Whether you are an administrator monitoring queue performance and memory footprints or a developer embedding WhatsApp messaging triggers into your codebase, the Control Center translates low-level WhatsApp protocol handshakes into a clean, visual workflow and developer-friendly code interfaces.
---
## The Architecture Overview
Think of OpenWA as a system of remote-controlled radios (the WhatsApp sessions) and a Control Center (the dashboard and SDKs). The dashboard and SDKs act as the operator giving commands (like "turn on channel 1" or "send a message") and monitoring the system indicators (such as system memory, connection status, or queue failures).
This modular layer acts as a unified client surface communicating exclusively over authenticated HTTP REST requests and real-time WebSocket events.
```mermaid
flowchart TD
subgraph Client ["Client Layer (Command & Control)"]
direction LR
UI["React Dashboard (App.tsx)"]
JSSDK["JS/TS SDK (index.ts)"]
PySDK["Python SDK (__init__.py)"]
end
subgraph API ["Gateway Layer (OpenWA Node.js App)"]
direction TB
Auth["API Key Auth (X-API-Key)"]
SessionMgr["Session Manager"]
InfraMgr["Infra Config Manager"]
QueueBull["BullMQ Queues"]
end
subgraph Engine ["WhatsApp Session Instances"]
direction LR
WAPh1["WhatsApp Phone Session 1"]
WAPh2["WhatsApp Phone Session 2"]
end
subgraph Storage ["Infrastructure & Storage"]
direction LR
DB[(SQLite / PostgreSQL)]
Cache[(Redis Cache)]
LocalStore[(Local / S3 Media)]
end
UI -->|HTTP requests / WebSockets| Auth
JSSDK -->|HTTP requests| Auth
PySDK -->|HTTP requests| Auth
Auth --> SessionMgr
Auth --> InfraMgr
SessionMgr -->|Manages| Engine
SessionMgr -->|Pushes events| QueueBull
InfraMgr -->|Stores config & migrations| DB
QueueBull -->|Job caching| Cache
SessionMgr -->|Stores media uploads| LocalStore
```
Sources: [dashboard/src/App.tsx:96-116](), [dashboard/src/services/api.ts:148-172](), [sdk/javascript/src/index.ts:55-84]()
---
## The Dashboard: Commanding your Army
The React-based dashboard operates as a single-page application built on top of a centralized API client with custom React hooks.
### 1. Unified Authentication and Routing
Access to the dashboard is secured using dynamic API key checks. Upon entering the admin key, the React client validates it against `/api/auth/validate`, determines the user's role (`admin`, `user`, or `readonly`), and conditionally renders administration views such as API Key Management and Plugin configurations.
Sources: [dashboard/src/App.tsx:31-65](), [dashboard/src/App.tsx:96-116]()
### 2. Live Session Management & QR Synchronization
The **Sessions** panel is where WhatsApp web automation instances are provisioned, monitored, and decommissioned.
- **Status Monitoring**: Sessions transition through various lifecycle stages, including `created`, `idle`, `initializing`, `connecting`, `qr_ready`, `ready`, and `disconnected`.
- **WebSocket Synchronization**: Rather than spamming HTTP endpoints, the dashboard utilizes a custom WebSocket listener hook. Whenever a session connects (`ready`) or disconnects, the server pushes an `onSessionStatus` event, instantly displaying desktop toast notifications and updating the card state.
- **The QR Flow**: If a session is in the `qr_ready` state, the dashboard renders a QR modal that fetches the login QR string from the server. To keep the visual QR code valid (since WhatsApp login codes expire quickly), the dashboard sets up a `5-second polling interval` that fetches and refreshes the QR code until the phone is connected successfully.
```typescript
// dashboard/src/pages/Sessions.tsx:81-91
useEffect(() => {
if (qrData) {
currentSessionName.current = qrData.sessionName;
qrRefreshInterval.current = setInterval(() => {
fetchQR(qrData.sessionId);
}, 5000);
}
return () => {
if (qrRefreshInterval.current) clearInterval(qrRefreshInterval.current);
};
}, [qrData, fetchQR]);
```
Sources: [dashboard/src/pages/Sessions.tsx:12-28](), [dashboard/src/pages/Sessions.tsx:29-43](), [dashboard/src/pages/Sessions.tsx:81-91](), [dashboard/src/pages/Sessions.tsx:431-507]()
---
## System Health & Infrastructure Monitoring
The **Infrastructure** page gives operators full visibility into system resources, performance metrics, and storage strategies, allowing runtime adjustments to the system's operational parameters.
| Subsystem Component | Supported Configurations / Options | Monitoring Metrics Provided |
| :--- | :--- | :--- |
| **Server Engine** | Environment (`production`, `development`), domain routing, public API & dashboard ports, and CORS origin bounds. | Public accessibility check, Node environment state. |
| **Database** | SQLite (zero-config, stored locally) or PostgreSQL (host, port, credentials, pool sizes up to 50, and SSL encryption toggles). | Schema migration status (`Active` / `Pending`), connection status. |
| **Redis Cache** | Toggle switches for enablement, built-in Redis, custom hosts/ports, and authentication passwords. | Connection health (`connected` / `disconnected`). |
| **BullMQ Queues** | Message delivery queues, Webhook event notification queues. | Real-time queue counters broken down by `pending`, `completed`, and `failed` jobs. |
| **Storage Engine** | Local directory path allocation or Amazon S3 bucket storage (bucket name, AWS region, secret/access credentials, custom endpoints). | Active storage strategy type. |
Sources: [dashboard/src/pages/Infrastructure.tsx:96-136](), [dashboard/src/pages/Infrastructure.tsx:701-768](), [dashboard/src/services/api.ts:87-97]()
### Triggering Server Restarts
When crucial configurations (like switching from SQLite to Postgres, or turning on Redis queues) are modified, they must be committed to the server's filesystem (`.env` file). Saving config triggers a safe restart command (`infraApi.saveConfig`), prompting a confirmation modal with an estimated countdown. The dashboard polls the health endpoint `/infra/health` to detect when the gateway is back online and reloads the web application interface.
```typescript
// dashboard/src/pages/Infrastructure.tsx:274-293
const checkServerHealth = async (stopCountdown?: () => void) => {
let attempts = 0;
const maxAttempts = 60;
const check = async () => {
try {
await infraApi.healthCheck();
stopCountdown?.();
setRestartCountdown(0);
setRestartStatus('success');
setTimeout(() => window.location.reload(), 2000);
} catch {
attempts++;
if (attempts < maxAttempts) setTimeout(check, 1000);
else setRestartStatus('error');
}
};
setTimeout(check, 3000);
};
```
Sources: [dashboard/src/pages/Infrastructure.tsx:211-237](), [dashboard/src/pages/Infrastructure.tsx:274-293](), [dashboard/src/services/api.ts:311-328]()
---
## The SDKs: Remote Control via Code
While the dashboard provides an interactive web user interface, the JavaScript/TypeScript and Python SDKs act as lightweight code gateways to programmatically control your session instances.
Both SDK client configurations inject the `X-API-Key` header into outgoing network requests, offering direct object abstractions mapped onto the REST endpoints.
### 1. JavaScript / TypeScript Client
The JavaScript SDK is built on top of native `fetch` client protocols, utilizing TypeScript interfaces to enforce strict compilation-time types.
```typescript
// sdk/javascript/src/index.ts:66-82
get sessions() {
return {
list: () => this.request<Session[]>('GET', '/api/sessions'),
get: (id: string) => this.request<Session>('GET', `/api/sessions/${id}`),
create: (data: { name: string }) => this.request<Session>('POST', '/api/sessions', data),
start: (id: string) => this.request<Session>('POST', `/api/sessions/${id}/start`),
stop: (id: string) => this.request<Session>('POST', `/api/sessions/${id}/stop`),
delete: (id: string) => this.request<void>('DELETE', `/api/sessions/${id}`),
};
}
get messages() {
return {
sendText: (sessionId: string, data: { chatId: string; text: string }) =>
this.request<MessageResponse>('POST', `/api/sessions/${sessionId}/messages/text`, data),
};
}
```
Sources: [sdk/javascript/src/index.ts:27-36](), [sdk/javascript/src/index.ts:55-82](), [sdk/README.md:15-27]()
### 2. Python Client
The Python SDK uses `httpx` to implement synchronous HTTP network operations, offering structured dataclasses for config and response payloads.
```python
# sdk/python/openwa/__init__.py:99-115
def list(self) -> list[dict]:
return self._client._request("GET", "/api/sessions")
def get(self, session_id: str) -> dict:
return self._client._request("GET", f"/api/sessions/{session_id}")
def create(self, name: str) -> dict:
return self._client._request("POST", "/api/sessions", {"name": name})
def start(self, session_id: str) -> dict:
return self._client._request("POST", f"/api/sessions/{session_id}/start")
def stop(self, session_id: str) -> dict:
return self._client._request("POST", f"/api/sessions/{session_id}/stop")
def delete(self, session_id: str) -> None:
self._client._request("DELETE", f"/api/sessions/{session_id}")
```
Sources: [sdk/python/openwa/__init__.py:67-92](), [sdk/python/openwa/__init__.py:95-131](), [sdk/README.md:36-48]()
---
## Summary
The combination of OpenWA's React Dashboard and modern client SDKs constitutes a robust Control Center. By handling session lifecycles, dynamic QR code refreshes, real-time WebSocket state synchronizations, and system configurations under a single unified API wrapper, the platform ensures that developers and DevOps teams can scale their automated WhatsApp communications with minimal effort and maximal operational oversight.
Sources: [dashboard/src/pages/Sessions.tsx:439-506](), [sdk/javascript/src/index.ts:112-113](), [sdk/python/openwa/__init__.py:20-22]()
---
## 06. Plain English Recap & Next Steps
> The ultimate one-minute summary of the OpenWA architecture: the postal worker analogy to keep, the golden lessons of rate limits, and the best blueprints to read next.
- Page Markdown: https://grok-wiki.com/public/wiki/rmyndharis-openwa-2c9996a09a22/pages/06-plain-english-recap-next-steps.md
- Generated: 2026-05-22T06:02:40.705Z
### Source Files
- `docs/03-system-architecture.md`
- `docs/21-glossary.md`
- `docs/15-project-roadmap.md`
<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:
- [docs/03-system-architecture.md](docs/03-system-architecture.md)
- [docs/21-glossary.md](docs/21-glossary.md)
- [docs/15-project-roadmap.md](docs/15-project-roadmap.md)
- [docs/16-risk-management.md](docs/16-risk-management.md)
- [docs/04-security-design.md](docs/04-security-design.md)
- [docs/README.md](docs/README.md)
</details>
# Plain English Recap & Next Steps
OpenWA is a robust, modular, open-source API gateway that translates standard REST/WebSocket commands into raw WhatsApp Web interactions. By embedding a headless Chrome instance inside a scalable NestJS runtime, it acts as a self-hosted alternative to proprietary WhatsApp providers, designed to scale seamlessly from a single-session bot to a multi-tenant enterprise backend.
This guide provides a smart newcomer with an absolute baseline understanding of how the system functions. It models OpenWA's architecture through an intuitive real-world analogy, breaks down the critical two-tier rate-limiting engine that protects your system and numbers, and establishes direct blueprints to read next based on your immediate deployment or development goals.
---
## The Postal Worker Analogy
To grasp how OpenWA orchestrates high-throughput messaging without crumbling or getting flagged by WhatsApp's spam-detection systems, imagine the platform operating as a highly disciplined **Post Office**.
Instead of treating the system as a complex array of Puppeteer scripts and API handlers, we can map each core component directly to a role in this post office:
1. **The Front Counter (REST Controllers & API Gateway):** When you issue a request to send a message or start a session, you are walking up to the front counter. The counter clerk validates your authorization ticket (`X-API-Key`) and checks that the destination format is correct before accepting your mail.
2. **The Postmaster (Session Manager):** The session manager is the administrator running the office. The Postmaster registers new workers, retrieves past work profiles (restoring session auth states from SQLite or PostgreSQL), and coordinates work shifts when the post office starts up.
3. **The Postal Worker (The whatsapp-web.js Adapter):** Every active phone number is mapped to a dedicated worker. The worker sits at a terminal (a headless Chrome browser) and physically types messages and reads status updates off the screen, mimicking human interactions to remain undetectable.
4. **The Outbound Sorting Bin (Redis & Bull Job Queue):** If a client dumps 1,000 outgoing messages at the front counter at once, they are not handed directly to the worker to deliver immediately. Instead, they are queued in the outbound sorting bin. The worker pulls messages one-by-one at a sustainable pace to prevent the system from getting banned or overloaded.
5. **The Delivery Receipt Service (Webhook Manager):** When the postal worker successfully delivers a message and sees the checkmark status (ACK) change on their screen, the office broadcasts a delivery receipt back to the sender's external webhook endpoint.
```mermaid
flowchart TB
subgraph Analogy["The Postal Analogy"]
direction TB
C_Counter["The Front Counter<br/>(Validates & accepts mail)"]
C_Postmaster["The Postmaster<br/>(Oversees staff shifts)"]
C_SortingBin["The Sorting Bin<br/>(Buffer to prevent overload)"]
C_Worker["The Postal Worker<br/>(Delivers to the address)"]
C_Terminal["The Terminal Screen<br/>(Worker's view)"]
end
subgraph Implementation["OpenWA Architecture"]
direction TB
I_API["REST Controllers / NestJS<br/>src/modules/message/message.controller.ts"]
I_SM["Session Manager<br/>src/modules/session/session.service.ts"]
I_Queue["Redis & Bull Job Queue<br/>src/queue/queue.module.ts"]
I_Engine["whatsapp-web.js Adapter<br/>src/engine/adapters/whatsapp-web-js.adapter.ts"]
I_Browser["Headless Chrome / Puppeteer<br/>Chrome/Chromium Browser"]
end
C_Counter <--> I_API
C_Postmaster <--> I_SM
C_SortingBin <--> I_Queue
C_Worker <--> I_Engine
C_Terminal <--> I_Browser
I_API --> I_SM
I_API --> I_Queue
I_Queue --> I_Engine
I_Engine --> I_Browser
```
Sources: [docs/03-system-architecture.md:15-53](), [docs/21-glossary.md:14-26](), [src/engine/adapters/whatsapp-web-js.adapter.ts:67-100]()
---
## The Golden Lessons of Rate Limits
Operating a WhatsApp gateway requires managing **two entirely separate layers of rate limits**. Mixing these up is the number-one cause of either server CPU exhaustion or instant phone number bans.
OpenWA implements safeguards at both boundaries:
| Attribute | Tier 1: Gateway API Rate Limits | Tier 2: WhatsApp Anti-Ban Throttling |
| :--- | :--- | :--- |
| **What it Protects** | OpenWA Server (NestJS, Redis, SQLite/PG) | Linked WhatsApp Phone Number (Account Ban) |
| **Reasoning** | Prevents client scripts or DDoS attacks from crashing the NestJS engine. | Prevents undocumented WhatsApp automated checks from detecting automated bulk spam. |
| **Where it occurs** | REST API Layer (using `@nestjs/throttler` or Traefik). | Message Job Queue / WhatsApp Adapter. |
| **Key Settings** | <ul><li>**Session Create:** 5/min</li><li>**Message Send:** 30/min</li><li>**Global Limit:** 1000/min</li></ul> | <ul><li>**Message Delay:** 3-5 seconds interval</li><li>**Media Send Delay:** 5 seconds interval</li><li>**Daily Max Limit:** 1,000 messages</li></ul> |
| **Action on Breach**| `429 Too Many Requests` status code returned. | Message remains in Redis queue and is processed slower. |
### WhatsApp Anti-Ban Warmup Configuration
For new or unestablished phone numbers, OpenWA features dedicated warmup constraints to build up reputation and prevent early account termination:
```typescript
// Anti-ban configuration defaults
const RATE_LIMIT_CONFIG = {
// Per session limits
messagesPerMinute: 20,
messagesPerHour: 200,
messagesPerDay: 1000,
// Delays
minDelayBetweenMessages: 3000, // 3 seconds
maxDelayBetweenMessages: 5000, // 5 seconds
delayAfterMedia: 5000, // 5 seconds after media
// Bulk messaging
bulkBatchSize: 50,
bulkDelayBetweenBatches: 60000, // 1 minute
// New number warmup
newNumberDailyLimit: 50,
newNumberWarmupDays: 14,
};
```
Sources: [docs/04-security-design.md:337-372](), [docs/16-risk-management.md:490-526]()
---
## Best Blueprints to Read Next
Depending on your engineering goals, your path forward in the OpenWA documentation will vary. To get up and running, follow these targeted blueprints:
### 1. The Production Deployment Blueprint
If your objective is to deploy OpenWA to staging or production for actual client use, skip the minimal setup and jump straight to the multi-node scaling documents:
* **[docs/10-devops-infrastructure.md](docs/10-devops-infrastructure.md):** Details container configurations, volume persistence (crucial for storing WhatsApp session cookies so workers don't have to re-scan the QR code on every launch), and Traefik load balancer options.
* **[docs/13-horizontal-scaling.md](docs/13-horizontal-scaling.md):** Guides you through running multiple OpenWA instances concurrently behind a load balancer with Shared S3/MinIO storage for media attachments and PostgreSQL/Redis tracking session status.
### 2. The Developer Customization Blueprint
If you want to modify OpenWA's engine, add hooks, or build third-party modules:
* **[docs/19-plugin-architecture.md](docs/19-plugin-architecture.md):** Covers the plugin lifecycles, enabling you to intercept, modify, or block outbound and inbound messages before they hit the WhatsApp network.
* **[docs/08-development-guidelines.md](docs/08-development-guidelines.md):** Explains standard NestJS coding conventions, environment setup, and the testing harness necessary to successfully submit pull requests to the core gateway.
### 3. The Integration Blueprint
If you are connecting an existing product, dashboard, or workflow engine:
* **[docs/06-api-specification.md](docs/06-api-specification.md):** The comprehensive endpoint list for checking delivery receipts (ACK status: `-1` error, `0` pending, `1` sent, `2` delivered, `3` read, `4` media played).
* **[docs/22-n8n-integration.md](docs/22-n8n-integration.md):** Detailed guide on leveraging the official n8n community node (`@rmyndharis/n8n-nodes-openwa`) to automate WhatsApp interactions inside visually composed business flows without writing code.
Sources: [docs/README.md:29-57](), [docs/15-project-roadmap.md:15-50](), [docs/21-glossary.md:5-13]()
---
OpenWA establishes an exceptionally developer-friendly, robust interface on top of WhatsApp Web by decoupling high-level application flows from low-level browser interaction scripts. By respecting internal API rate limits and configuring anti-ban throttling, you can reliably run a self-hosted messaging infrastructure suited for lightweight development up through enterprise automation flows.
---