rmyndharis/OpenWA

▲ 66 stars today★ 14,960⑂ 3,501

Free, Open Source, Self-Hosted WhatsApp API Gateway

About rmyndharis/OpenWA

rmyndharis/OpenWA is an open-source project on GitHub, mainly written in TypeScript. Free, Open Source, Self-Hosted WhatsApp API Gateway It currently holds 14,960 stars and 3,501 forks with 18 open issues, and was last pushed on 2026-10-02 (repository created 2026-02-02).

Project Overview

Git Homed tracks it on the Today's Trending board, currently at rank #56 with 66 new stars today.

GitHub Repository Details

Repository rmyndharis/OpenWA · default branch main · size 25761 KB · watchers 83 · source: GitHub REST API and repository README

README

https://github.com/rmyndharis/OpenWA/blob/HEAD/OpenWA Logo

OpenWA

Open Source WhatsApp API Gateway

Features • Quick Start • Docs • API • Contributing

https://github.com/rmyndharis/OpenWA/blob/HEAD/CI https://github.com/rmyndharis/OpenWA/blob/HEAD/Version https://github.com/rmyndharis/OpenWA/blob/HEAD/License https://github.com/rmyndharis/OpenWA/blob/HEAD/Node https://github.com/rmyndharis/OpenWA/blob/HEAD/NestJS https://github.com/rmyndharis/OpenWA/blob/HEAD/Docker https://github.com/rmyndharis/OpenWA/blob/HEAD/TypeScript https://github.com/rmyndharis/OpenWA/blob/HEAD/Buy Me a Coffee

---

✨ Why OpenWA?

OpenWA is a free, open-source WhatsApp API Gateway designed for developers who need full control over their messaging infrastructure—without vendor lock-in or hidden paywalls.

Built on a pluggable architecture, OpenWA lets you select database engines (SQLite/PostgreSQL), backup/migration storage backends (Local/S3), and cache layers (disabled/Redis) through configuration rather than application-code changes. Message media itself is returned inline to API and webhook consumers; it is not automatically persisted to the storage backend.

| | | | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | 🔓 100% Open Source | No licensing fees, no feature locks, full source code access | | 🏗️ Pluggable Architecture | Swap adapters for database, storage, and cache via config | | 🖥️ Full Dashboard | Modern React UI for session, webhook, and API key management | | 🔹 Multi-Session Ready | Run multiple WhatsApp sessions concurrently on one instance | | 🐳 Docker Native | Production-ready with zero configuration | | 🧩 Official Plugins | Chatwoot, Typebot & more as sandboxed plugins on the Integration Fabric — OpenWA-plugins | | 🔗 n8n Integration | Community nodes for workflow automation | | 🧩 Community Adapters | Third-party integrations (e.g. ioBroker) — see docs | | 🔐 Session-scoped keys | Operator and viewer (reader) tokens can be limited to chosen sessions — or all sessions if none are selected | | 🔒 Chat-scoped keys | Those same tokens can also be limited to chosen chats — a few groups and contacts — so an agent on a shared account sees only its own |

Session-scoped operator & viewer tokens

When you create or edit an operator or viewer API key in the dashboard, you can tick the WhatsApp sessions that key may use.

Admin keys stay unscoped in the dashboard so they can keep managing other API keys. The HTTP API still accepts allowedSessions on any role if you need that from a client.

Chat-scoped operator & viewer tokens

A session-scoped key still reaches every chat on the sessions it may use. A key can be narrowed further, to chats (a chosen set of groups and individual contacts), with allowedChats on POST /auth/api-keys or PUT /auth/api-keys/{id}. The dashboard sets it on operator and viewer keys (one chat id or phone number per line) and shows each key's chat count in the list.

This lets you point an AI agent or third-party integration at a shared account without handing it every chat. Give the agent a key scoped to the few groups (or DMs) it is meant to handle: it can send and reply there, but it cannot list your other chats, read any other DM, message a contact outside its set, or reach the queue dashboard. It reads its chats' stored messages on either engine through GET /sessions/{sessionId}/messages?chatId=, where chatId is required for such a key, and live history through GET /sessions/{sessionId}/messages/{chatId}/history on whatsapp-web.js only. It receives no pushed events, so it has to poll. It can still read the session's own status (GET /sessions/{sessionId}) so an integration can tell whether it is connected.

Identity is matched through the lid mapping table: a contact allowlisted by phone number also matches the same person's @lid privacy id once the table maps the two, and an unmapped @lid is refused rather than guessed. A lid's digits are never mistaken for a phone number, so 555000111@lid does not admit [email protected].

The default covers REST routes only. Surfaces that authenticate outside the REST guard do not inherit it, so each one that can return chat data refuses a chat-scoped key with its own check: the /events WebSocket, the MCP mount (per tool call), and the Bull Board queue dashboard. Four list routes are usable, each filtered to the key's chats before paging: GET /sessions/{sessionId}/chats, GET /sessions/{sessionId}/groups (id, name and community parent id only), GET /sessions/{sessionId}/contacts and GET /sessions/{sessionId}/labels/{labelId}/chats.

The API also accepts allowedChats on an admin key, but no admin-only route is open to a chat-scoped key, and the last usable admin key cannot be scoped this way.

None of this changes the ban-risk guidance below. It limits what a _key_ can reach, not what WhatsApp makes of the account.

---

⚠️ Before you connect a number — please read

OpenWA is an unofficial, community-maintained gateway. It connects to WhatsApp through reverse-engineered clients (the whatsapp-web.js project and @whiskeysockets/baileys), not through Meta's official Cloud API. This has real consequences you should understand before you link a phone number.

What this means in practice

| Engine | Ban-risk profile | Resource cost | | ----------------- | ------------------------------------------------------------------------------------------------------- | --------------------------------- | | whatsapp-web.js | Lower — drives a real headless Chromium that looks like genuine WhatsApp Web traffic. | High RAM (~300–500 MB / session). | | baileys | Higher — speaks the multi-device WebSocket protocol directly and is easier for WhatsApp to fingerprint. | Low RAM (~30–80 MB / session). |

If account safety is your top priority and you can afford the memory, prefer whatsapp-web.js. If you need density and accept the trade-off, use baileys.

Safe-sending guidelines

These are practical guardrails, not guarantees — but they materially reduce the chance of WhatsApp flagging the account:

1. Warm up fresh numbers. For the first several days, behave like a normal human user: scan the QR, exchange a handful of messages with saved contacts, join a group or two, set a profile photo. Don't blast on day one. 2. Don't cold-blast strangers. Sending the first-ever message to a large batch of numbers that have never messaged you is the single most reliable way to get restricted — on either engine. 3. Pace sends per session. Set SEND_PACING_ENABLED=true (off by default) for a per-session daily cap: an allowance that grows with the session's age (SEND_PACING_WARMUP_SCHEDULE), a separate cap on new conversations (SEND_PACING_COLD_DAILY_CAP) and a consecutive-failure breaker; R002 in the risk guide lists what it counts. No per-minute cap is enforced, so spacing within a day is up to the caller: bulk sends wait delayBetweenMessages between messages, and single text sends pause behind a typing indicator (SIMULATE_TYPING, on by default). A few messages per minute per session is sustainable; "thousands in an hour" is not. The RATE_LIMIT_* variables are API abuse protection, counted per route and client IP, not a send cap: they throttle dashboard and read traffic too. 4. Use opted-in recipients. The safest workloads are replies and alerts to people who already expect to hear from you (OTP to your own users, order updates, support replies). 5. Keep a fallback. For anything auth-critical or revenue-critical, keep an SMS / email / official-Cloud-API path. Do not bet a login flow solely on an unofficial client. 6. Mind the hosting IP. Cheap datacenter IPs are flagged more aggressively than residential ones. A residential proxy (supported per-session via the proxy settings) can help; it is not a license to spam.

Known platform behaviour (not bugs)

A few things that look like bugs but are actually server-side WhatsApp policy, not OpenWA defects — we track them separately so we can distinguish them from real bugs:

Compliance

For any deployment where ethical, legal, or regulatory compliance matters (healthcare, finance, large-scale commercial messaging, anything touching end users in the EU/EEA under DMA/GDPR framings), treat OpenWA as not approved and use Meta's official WhatsApp Cloud API. OpenWA is an excellent fit for personal projects, internal tooling, automation hobbyists, and learning — it is not a drop-in replacement for the official API in regulated environments.

📖 For the deeper, maintainer-side risk analysis (protocol-change exposure, dependency strategy, security posture), see Risk Management (docs/16).

---

🎯 Features

Core Features

| Feature | Status | Description | | ------------- | ------ | ---------------------------------------------------------------------------- | | REST API | ✅ | Full WhatsApp API via HTTP endpoints | | Multi-Session | ✅ | Manage multiple WhatsApp accounts | | Webhooks | ✅ | Real-time events with HMAC signature and optional smart pre-dispatch filters | | Web Dashboard | ✅ | Visual management interface | | API Key Auth | ✅ | Secure API authentication | | Swagger Docs | ✅ | Interactive API documentation |

Messaging

| Feature | Status | Description | | ----------------- | ------ | --------------------------------------------------------- | | Text Messages | ✅ | Send/receive text messages | | Media Messages | ✅ | Images, videos, documents, audio | | Message Reactions | ✅ | React to messages with emoji | | Message Editing | ✅ | Send edits + live message.edited events on both engines | | Bulk Messaging | ✅ | Send to multiple recipients | | Message Status | ✅ | Track delivery and read receipts |

Advanced

| Feature | Status | Description | | ------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Groups API | ✅ | Create, manage, join (invite code), and configure groups | | Profile Management | ✅ | Set own display name, about text, and profile picture | | Call Handling | ✅ | call.received events (not reliable on whatsapp-web.js), reject calls and per-session auto-reject (Baileys only) | | Channels/Newsletter | ✅ | WhatsApp Channels support | | Labels Management | ✅ | Organize chats with labels | | Proxy Support | ✅ | Per-session proxy configuration | | Rate Limiting | ✅ | Configurable request limits | | CIDR Whitelisting | ✅ | IP-based access control | | Chat Scoping | ✅ | Per-key allowedChats allowlist (groups and contacts): a key reaches only those chats, and routes not marked safe for it refuse it | | Audit Logging | ✅ | Audit trail for API-key, session, integration-instance, and infra admin operations (message sends and webhook deliveries are tracked in their own tables, not the audit log) |

Infrastructure

| Feature | Status | Description | | ---------------- | ------ | ---------------------------------------- | | SQLite | ✅ | Zero-config embedded database | | PostgreSQL | ✅ | Production-grade database | | Redis Cache | ✅ | Optional performance caching | | S3/MinIO Storage | ✅ | Media-directory backup/migration backend | | Docker | ✅ | One-command deployment | | Health Checks | ✅ | Kubernetes-ready probes | | Data Migration | ✅ | Export/import between backends |

---

🚀 Quick Start

Option A: Docker (Recommended)

# Clone and start
git clone https://github.com/rmyndharis/OpenWA.git
cd OpenWA
docker compose -f docker-compose.dev.yml up -d

Access (the dashboard is bundled into the API image and served on the same port)

Dashboard: http://localhost:2785

API: http://localhost:2785/api

Swagger: http://localhost:2785/api/docs

Your API key. The first boot generates an admin API key, prints it once in the log and stores it at /app/data/.api-key inside the container. Read it with docker exec openwa-api cat /app/data/.api-key, then use it to sign in to the dashboard and as the X-API-Key header wherever this README shows YOUR_API_KEY. Later boots log only a masked prefix. See API Key.

Using Podman instead of Docker?
Podman rootless mode requires the socket to be running and DOCKER_HOST to be set:
>
> systemctl --user start podman.socket
systemctl --user enable podman.socket
export DOCKER_HOST=unix:///run/user/$(id -u)/podman/podman.sock
> Add the export line to your ~/.bashrc to make it permanent.

Option B: Local Development

# Clone repository
git clone https://github.com/rmyndharis/OpenWA.git
cd OpenWA

Install the locked dependencies (includes dashboard)

npm ci

Start API + Dashboard (config is auto-generated on first run)

npm run dev

Access (in dev the dashboard runs on the Vite server with hot reload)

Dashboard: http://localhost:2886

API: http://localhost:2785/api

Swagger: http://localhost:2785/api/docs

The first boot writes the admin API key to data/.api-key (read it with cat data/.api-key).

Use npm install instead when intentionally changing dependencies. OpenWA's committed lockfile uses registry artifacts only, so npm 12 works with its secure default that blocks Git dependencies; do not disable that policy globally.

---

🔒 Security Architecture

Docker Socket Proxy

The production stack never exposes /var/run/docker.sock directly to the application container. Instead, a dedicated docker-proxy sidecar (based on tecnativa/docker-socket-proxy) acts as the sole gateway to the Docker daemon:

openwa-api  ──TCP 2375──▶  docker-proxy  ──unix──▶  /var/run/docker.sock

Only the operations needed for container orchestration are enabled (CONTAINERS, IMAGES, VOLUMES, INFO, PING, plus the POST method switch). The application connects via the DOCKER_HOST=tcp://docker-proxy:2375 environment variable, which DockerService detects automatically. Note this is an operational gateway, not a fine-grained privilege boundary: with POST enabled the proxy admits every method to the enabled paths and cannot scope container-create payloads, so a compromised API container would be host-root-equivalent — see SECURITY.md for the full threat model, mitigations, and how to disable the proxy if you don't use the built-in datastore orchestration.

Non-root Container Execution

The production image never runs the Node.js process as root. On startup, the container follows this chain:

dumb-init (PID 1)
  └─ docker-entrypoint.sh (root — fixes named-volume ownership via chown)
       └─ gosu openwa node dist/main  (drops to the openwa user)
Named volumes (e.g. openwa-data) get their ownership corrected automatically on every start, so no manual chown step is needed after volume creation.

The image can also start as the openwa user directly (uid/gid 997: --user 997:997, or the Helm chart's podSecurityContext). The entrypoint then skips the chown and the gosu drop and needs no added capabilities, provided /app/data is writable by that uid.

---

🏭 Production Deployment

For production, use the main docker-compose.yml with optional services:

# Basic production (SQLite, local storage)
docker compose up -d

Also start the PostgreSQL container (configure it first, see below)

docker compose --profile postgres up -d

Also start PostgreSQL, Redis and MinIO (configure them first, see below)

docker compose --profile full up -d

| Profile | Services | | ---------- | ----------------------------------------------- | | postgres | PostgreSQL database | | redis | Redis cache | | minio | S3-compatible storage (MinIO fork pgsty/silo) | | full | All services above |

A profile only starts the container; OpenWA keeps using SQLite and local storage until it is told to use the new service. The simpl

GitHub Stars & Activity

14,960Stars
3,501Forks
18Open issues
TypeScriptLanguage

GitHub Popularity

GitHub stars14,960
Forks3,501
Open issues18
Primary languageTypeScript
LicenseMIT
Stars gained today66
Created2026-02-02
Last pushed2026-10-02

Trending History

Daily boardrank #56 · ▲ 66 stars

Related GitHub Projects

1

n8n-io / n8n

TypeScript★ 206,509⑂ 60,971▲ 100 stars
→
2

garrytan / gstack

TypeScript★ 134,771⑂ 20,056▲ 119 stars
→
3

thedotmack / claude-mem

TypeScript★ 95,185⑂ 8,435▲ 113 stars
→
4

modelcontextprotocol / servers

TypeScript★ 90,957⑂ 11,748▲ 34 stars
→
5

ruvnet / ruflo

TypeScript★ 73,725⑂ 8,758▲ 101 stars
→
6

heygen-com / hyperframes

TypeScript★ 55,814⑂ 5,036▲ 584 stars
→
7

vercel-labs / skills

TypeScript★ 32,985⑂ 2,807▲ 84 stars
→
8

mksglu / context-mode

TypeScript★ 24,990⑂ 1,796▲ 276 stars
→

More Trending Repositories