rmyndharis/OpenWAPublic

Free, Open Source, Self-Hosted WhatsApp API Gateway

AI summary: A free, open-source, and self-hosted API gateway for WhatsApp automation with multi-session support.

Stars
15K
+185 today
Forks
3.5K
Watchers
83
Open issues
6
Open PRs
2
Contributors
~74
Commits
3K
Branches
2

TypeScriptMITCreated Feb 2, 2026Last push 1d agoLatest release v0.23.6+428 stars this week+1.3K this month

Quick answers

What is OpenWA?
A free, open-source, and self-hosted API gateway for WhatsApp automation with multi-session support.
What does OpenWA do?
OpenWA is a powerful, open-source API gateway that allows developers to self-host their own WhatsApp automation and bot infrastructure. It provides a comprehensive set of APIs for interacting with WhatsApp Web, enabling automated messaging, chat reading, and group management without relying on the official Meta Cloud API. The project operates as a self-hosted gateway with a highly pluggable architecture, allowing users to seamlessly swap databases and storage backends via simple configuration. It supports running multiple WhatsApp sessions concurrently and includes a modern React dashboard for managing webhooks and API keys.
Who is OpenWA for?
OpenWA is intended for developers looking to build chatbots, notification systems, or automation tools for WhatsApp using their own infrastructure. Users must understand that unauthorized automation violates WhatsApp's Terms of Service and carries a ban risk.
How do I get started with OpenWA?
Check the documentation for Docker or manual installation instructions.
How popular is OpenWA on GitHub?
rmyndharis/OpenWA has 14,972 stars and 3,507 forks on GitHub, and gained 428 stars in the last 7 days.
What license does OpenWA use?
rmyndharis/OpenWA is released under the MIT license.

Star history

since Jul 29, 2026
05K10K15KJul 2026Aug 2026Sep 2026Oct 2026
15K stars as of Oct 3, 2026. Measured daily since Jul 29, 2026; GitHub no longer exposes earlier star timestamps.

Contribution activity

commits per day, last 52 weeks
SepOctNovDecJanFebMarAprMayJunJulAugSepMonWedFri2025-09-27: 0 commits2025-09-28: 0 commits2025-09-29: 0 commits2025-09-30: 0 commits2025-10-01: 0 commits2025-10-02: 0 commits2025-10-03: 0 commits2025-10-04: 0 commits2025-10-05: 0 commits2025-10-06: 0 commits2025-10-07: 0 commits2025-10-08: 0 commits2025-10-09: 0 commits2025-10-10: 0 commits2025-10-11: 0 commits2025-10-12: 0 commits2025-10-13: 0 commits2025-10-14: 0 commits2025-10-15: 0 commits2025-10-16: 0 commits2025-10-17: 0 commits2025-10-18: 0 commits2025-10-19: 0 commits2025-10-20: 0 commits2025-10-21: 0 commits2025-10-22: 0 commits2025-10-23: 0 commits2025-10-24: 0 commits2025-10-25: 0 commits2025-10-26: 0 commits2025-10-27: 0 commits2025-10-28: 0 commits2025-10-29: 0 commits2025-10-30: 0 commits2025-10-31: 0 commits2025-11-01: 0 commits2025-11-02: 0 commits2025-11-03: 0 commits2025-11-04: 0 commits2025-11-05: 0 commits2025-11-06: 0 commits2025-11-07: 0 commits2025-11-09: 0 commits2025-11-10: 0 commits2025-11-11: 0 commits2025-11-12: 0 commits2025-11-13: 0 commits2025-11-14: 0 commits2025-11-15: 0 commits2025-11-16: 0 commits2025-11-17: 0 commits2025-11-18: 0 commits2025-11-19: 0 commits2025-11-20: 0 commits2025-11-21: 0 commits2025-11-22: 0 commits2025-11-23: 0 commits2025-11-24: 0 commits2025-11-25: 0 commits2025-11-26: 0 commits2025-11-27: 0 commits2025-11-28: 0 commits2025-11-29: 0 commits2025-11-30: 0 commits2025-12-01: 0 commits2025-12-02: 0 commits2025-12-03: 0 commits2025-12-04: 0 commits2025-12-05: 0 commits2025-12-06: 0 commits2025-12-07: 0 commits2025-12-08: 0 commits2025-12-09: 0 commits2025-12-10: 0 commits2025-12-11: 0 commits2025-12-12: 0 commits2025-12-13: 0 commits2025-12-14: 0 commits2025-12-15: 0 commits2025-12-16: 0 commits2025-12-17: 0 commits2025-12-18: 0 commits2025-12-19: 0 commits2025-12-20: 0 commits2025-12-21: 0 commits2025-12-22: 0 commits2025-12-23: 0 commits2025-12-24: 0 commits2025-12-25: 0 commits2025-12-26: 0 commits2025-12-27: 0 commits2025-12-28: 0 commits2025-12-29: 0 commits2025-12-30: 0 commits2025-12-31: 0 commits2026-01-01: 0 commits2026-01-02: 0 commits2026-01-03: 0 commits2026-01-04: 0 commits2026-01-05: 0 commits2026-01-06: 0 commits2026-01-07: 0 commits2026-01-08: 0 commits2026-01-09: 0 commits2026-01-10: 0 commits2026-01-11: 0 commits2026-01-12: 0 commits2026-01-13: 0 commits2026-01-14: 0 commits2026-01-15: 0 commits2026-01-16: 0 commits2026-01-17: 0 commits2026-01-18: 0 commits2026-01-19: 0 commits2026-01-20: 0 commits2026-01-21: 0 commits2026-01-22: 0 commits2026-01-23: 0 commits2026-01-24: 0 commits2026-01-25: 0 commits2026-01-26: 0 commits2026-01-27: 0 commits2026-01-28: 0 commits2026-01-29: 0 commits2026-01-30: 0 commits2026-01-31: 0 commits2026-02-01: 0 commits2026-02-02: 0 commits2026-02-03: 0 commits2026-02-04: 0 commits2026-02-05: 1 commit2026-02-06: 0 commits2026-02-07: 0 commits2026-02-08: 0 commits2026-02-09: 0 commits2026-02-10: 1 commit2026-02-11: 0 commits2026-02-12: 0 commits2026-02-13: 0 commits2026-02-14: 0 commits2026-02-15: 0 commits2026-02-16: 0 commits2026-02-17: 7 commits2026-02-18: 31 commits2026-02-19: 0 commits2026-02-20: 0 commits2026-02-21: 0 commits2026-02-22: 0 commits2026-02-23: 3 commits2026-02-24: 0 commits2026-02-25: 0 commits2026-02-26: 5 commits2026-02-27: 0 commits2026-02-28: 0 commits2026-03-01: 1 commit2026-03-02: 1 commit2026-03-03: 0 commits2026-03-04: 0 commits2026-03-05: 0 commits2026-03-06: 0 commits2026-03-07: 0 commits2026-03-08: 0 commits2026-03-09: 0 commits2026-03-10: 0 commits2026-03-11: 0 commits2026-03-12: 0 commits2026-03-13: 0 commits2026-03-14: 0 commits2026-03-15: 0 commits2026-03-16: 0 commits2026-03-17: 0 commits2026-03-18: 0 commits2026-03-19: 0 commits2026-03-20: 0 commits2026-03-21: 0 commits2026-03-22: 0 commits2026-03-23: 0 commits2026-03-24: 0 commits2026-03-25: 0 commits2026-03-26: 0 commits2026-03-27: 0 commits2026-03-28: 0 commits2026-03-29: 0 commits2026-03-30: 0 commits2026-03-31: 0 commits2026-04-01: 4 commits2026-04-02: 0 commits2026-04-03: 0 commits2026-04-04: 0 commits2026-04-05: 0 commits2026-04-06: 0 commits2026-04-07: 0 commits2026-04-08: 0 commits2026-04-09: 0 commits2026-04-10: 0 commits2026-04-11: 0 commits2026-04-12: 0 commits2026-04-13: 0 commits2026-04-14: 0 commits2026-04-15: 0 commits2026-04-16: 0 commits2026-04-17: 0 commits2026-04-18: 0 commits2026-04-19: 0 commits2026-04-20: 0 commits2026-04-21: 0 commits2026-04-22: 0 commits2026-04-23: 0 commits2026-04-24: 0 commits2026-04-25: 0 commits2026-04-26: 0 commits2026-04-27: 3 commits2026-04-28: 0 commits2026-04-29: 0 commits2026-04-30: 0 commits2026-05-01: 0 commits2026-05-02: 0 commits2026-05-03: 0 commits2026-05-04: 0 commits2026-05-05: 0 commits2026-05-06: 0 commits2026-05-07: 0 commits2026-05-08: 0 commits2026-05-09: 0 commits2026-05-10: 0 commits2026-05-11: 1 commit2026-05-12: 0 commits2026-05-13: 0 commits2026-05-14: 0 commits2026-05-15: 0 commits2026-05-16: 0 commits2026-05-17: 6 commits2026-05-18: 0 commits2026-05-19: 0 commits2026-05-20: 11 commits2026-05-21: 0 commits2026-05-22: 0 commits2026-05-23: 0 commits2026-05-24: 0 commits2026-05-25: 0 commits2026-05-26: 0 commits2026-05-27: 0 commits2026-05-28: 0 commits2026-05-29: 0 commits2026-05-30: 0 commits2026-05-31: 0 commits2026-06-01: 0 commits2026-06-02: 0 commits2026-06-03: 0 commits2026-06-04: 0 commits2026-06-05: 0 commits2026-06-06: 0 commits2026-06-07: 0 commits2026-06-08: 0 commits2026-06-09: 0 commits2026-06-10: 0 commits2026-06-11: 0 commits2026-06-12: 0 commits2026-06-13: 28 commits2026-06-14: 3 commits2026-06-15: 12 commits2026-06-16: 9 commits2026-06-17: 66 commits2026-06-18: 42 commits2026-06-19: 23 commits2026-06-20: 16 commits2026-06-21: 26 commits2026-06-22: 22 commits2026-06-23: 22 commits2026-06-24: 23 commits2026-06-25: 15 commits2026-06-26: 10 commits2026-06-27: 3 commits2026-06-28: 28 commits2026-06-29: 40 commits2026-06-30: 19 commits2026-07-01: 13 commits2026-07-02: 27 commits2026-07-03: 17 commits2026-07-04: 0 commits2026-07-05: 7 commits2026-07-06: 15 commits2026-07-07: 4 commits2026-07-08: 8 commits2026-07-09: 10 commits2026-07-10: 6 commits2026-07-11: 10 commits2026-07-12: 24 commits2026-07-13: 3 commits2026-07-14: 1 commit2026-07-15: 10 commits2026-07-16: 0 commits2026-07-17: 26 commits2026-07-18: 10 commits2026-07-19: 17 commits2026-07-20: 20 commits2026-07-21: 10 commits2026-07-22: 10 commits2026-07-23: 44 commits2026-07-24: 35 commits2026-07-25: 2 commits2026-07-26: 2 commits2026-07-27: 64 commits2026-07-28: 12 commits2026-07-29: 16 commits2026-07-30: 38 commits2026-07-31: 10 commits2026-08-01: 28 commits2026-08-02: 48 commits2026-08-03: 40 commits2026-08-04: 34 commits2026-08-05: 35 commits2026-08-06: 49 commits2026-08-07: 48 commits2026-08-08: 29 commits2026-08-09: 20 commits2026-08-10: 38 commits2026-08-11: 74 commits2026-08-12: 96 commits2026-08-13: 23 commits2026-08-14: 40 commits2026-08-15: 28 commits2026-08-16: 37 commits2026-08-17: 37 commits2026-08-18: 30 commits2026-08-19: 19 commits2026-08-20: 20 commits2026-08-21: 11 commits2026-08-22: 1 commit2026-08-23: 7 commits2026-08-24: 23 commits2026-08-25: 2 commits2026-08-26: 1 commit2026-08-27: 2 commits2026-08-28: 2 commits2026-08-29: 1 commit2026-08-30: 5 commits2026-08-31: 8 commits2026-09-01: 10 commits2026-09-02: 32 commits2026-09-03: 28 commits2026-09-04: 5 commits2026-09-05: 11 commits2026-09-06: 18 commits2026-09-07: 1 commit2026-09-08: 0 commits2026-09-09: 3 commits2026-09-10: 0 commits2026-09-11: 0 commits2026-09-12: 0 commits2026-09-13: 9 commits2026-09-14: 20 commits2026-09-15: 51 commits2026-09-16: 5 commits2026-09-17: 7 commits2026-09-18: 19 commits2026-09-19: 11 commits2026-09-20: 55 commits2026-09-21: 101 commits2026-09-22: 27 commits2026-09-23: 4 commits2026-09-24: 8 commits2026-09-25: 0 commits2026-09-26: 0 commits
2,214 commits in the last yearLessMore

Signals and awards

derived from tracked data
  • Widely adopted

    14,972 stars

  • Very active

    2,214 commits in 52 weeks

  • Well documented

    High community health score

  • Permissive license

    MIT

  • Continuous integration

    Automated checks passing

  • Repeat trending

    3 trending appearances

What OpenWA does

OpenWA is a powerful, open-source API gateway that allows developers to self-host their own WhatsApp automation and bot infrastructure. It provides a comprehensive set of APIs for interacting with WhatsApp Web, enabling automated messaging, chat reading, and group management without relying on the official Meta Cloud API. The project operates as a self-hosted gateway with a highly pluggable architecture, allowing users to seamlessly swap databases and storage backends via simple configuration. It supports running multiple WhatsApp sessions concurrently and includes a modern React dashboard for managing webhooks and API keys.

OpenWA is intended for developers looking to build chatbots, notification systems, or automation tools for WhatsApp using their own infrastructure. Users must understand that unauthorized automation violates WhatsApp's Terms of Service and carries a ban risk.

  • Pluggable backend architecture: Allows developers to seamlessly swap database engines and storage layers through simple configuration changes.
  • Multi-engine support: Offers a choice between headless browser automation or direct WebSocket connections to optimize for lower RAM usage.
  • Multi-session management: Capable of running and managing multiple distinct WhatsApp accounts concurrently from a single gateway instance.
  • Modern React dashboard: Provides a comprehensive graphical user interface for intuitively managing active sessions, webhooks, and API keys.
  • Docker native deployment: Ships production-ready via Docker, completely eliminating complex setup and configuration steps for self-hosting.

Where teams use it

Customer support automation

Small businesses build robust automated chatbots to answer frequently asked questions and smartly route complex queries to human operators.

System notifications

IT administrators configure their monitoring tools to send critical server down alerts directly to a dedicated WhatsApp operations group.

Community management

Community leaders deploy automated bots to quickly welcome new members, meticulously moderate spam, and broadcast announcements in large WhatsApp groups.

Workflow integration

Automation engineers seamlessly use community n8n nodes to integrate WhatsApp messaging directly into highly complex, multi-app workflows.

Getting started: Check the documentation for Docker or manual installation instructions.

README

main branch

OpenWA Logo

OpenWA

Open Source WhatsApp API Gateway

Features • Quick Start • Docs • API • Contributing

CI Version License Node NestJS Docker TypeScript


✨ 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.

  • No sessions selected — the key can access every session, including ones created later.
  • One or more sessions selected — the key can only list, read, and (for operator) manage those sessions. A request naming any other session returns 401; session-filtered lists (sessions, audit, webhook delivery failures) return that key's rows rather than an error; and the key-management routes and the queue dashboard, which name no session at all, return 403.

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 does not set or show it yet, and editing a key there leaves its chats unchanged.

  • No chats selected — the key can reach every chat on its sessions.
  • One or more selected — the key reaches only those chats. Every authenticated REST route not explicitly marked as safe for a chat-scoped key refuses it with 403 (a request naming a session outside allowedSessions still answers 401 first), including routes added in later releases: the refusal is the default. Inside its chats an operator key can do what the marked routes allow, which is more than reading and sending: it can also delete or clear a chat, leave or rename a group, and block the contact. It cannot change who belongs to a group: adding, removing, promoting or demoting participants, answering join requests and reading or resetting the invite link all stay closed.
  • The two scopes are independent — a key may be limited to sessions, to chats, to both, or to neither.

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 through GET /sessions/{sessionId}/messages/{chatId}/history, which works on whatsapp-web.js only; on Baileys it sees just each chat's last-message preview. 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. Of the list routes, only GET /sessions/{sessionId}/chats is usable, and it filters to the key's chats before paging.

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

  • There is always a non-zero risk of account restriction or ban. WhatsApp's anti-abuse systems actively look for unofficial automation. No amount of code quality on our side can make that risk zero.

  • Pick the right number. Never connect your primary personal or business number to an automated gateway. Use a dedicated number you can afford to lose. If you're running this for paying clients, pass that guidance on to them.

  • The two engines trade off differently:

    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. Rate-limit yourself. OpenWA ships with a configurable rate limiter (RATE_LIMIT_* env vars). Use it. A few messages per minute per session is sustainable; "thousands in an hour" is not.
  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:

  • First message to a brand-new contact sometimes never arrives. The API returns success because the message leaves OpenWA, but WhatsApp's server-side reach-out / trust policy drops it at delivery. This is independent of OpenWA. We track it in #830.
  • Accounts that get restricted cannot be "unrestricted" by us. If WhatsApp disables a number, you need to appeal through their channels — OpenWA has no lever to pull.

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

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

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)
  • dumb-init is PID 1 and forwards signals (SIGTERM, etc.) for graceful shutdown.
  • docker-entrypoint.sh runs as root only long enough to chown the named-volume mount points so the openwa user can write to them.
  • gosu performs a clean exec-based privilege drop — no su or sudo wrappers, so the node process is the direct child of dumb-init.

Named volumes (e.g. openwa-data) get their ownership corrected automatically on every start, so no manual chown step is needed after volume creation.


🏭 Production Deployment

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

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

# With PostgreSQL database
docker compose --profile postgres up -d

# Full stack (PostgreSQL, Redis, MinIO)
docker compose --profile full up -d
Profile Services
postgres PostgreSQL database
redis Redis cache
minio S3-compatible storage
full All services above

The dashboard is bundled into the API image and served by NestJS on the API port, so it needs no profile — it is always available wherever openwa-api runs. For TLS/public exposure, put your own reverse proxy (nginx, Caddy, a cloud load balancer, or a k8s Ingress) in front; see the nginx example in docs/12-troubleshooting-faq.md.

Development vs Production

  • Development (docker-compose.dev.yml): SQLite, local storage, API serves the bundled dashboard
  • Production (docker-compose.yml): Configurable database, profiles for optional services

Official GHCR images are published as multi-arch manifests for:

  • linux/amd64
  • linux/arm64

🔌 Ports

Service Port Description
API & Dashboard 2785 REST API + bundled web dashboard (same port)
Swagger 2785/api/docs Interactive API docs — off under NODE_ENV=production unless ENABLE_SWAGGER=true
Dashboard (dev) 2886 Vite dev server with hot reload (npm run dev)

📡 API Examples

Create a Session

curl -X POST http://localhost:2785/api/sessions \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -d '{"name": "my-bot"}'

Start Session & Get QR Code

# Start the session
curl -X POST http://localhost:2785/api/sessions/{sessionId}/start \
  -H "X-API-Key: YOUR_API_KEY"

# Get QR code (scan with WhatsApp)
curl http://localhost:2785/api/sessions/{sessionId}/qr \
  -H "X-API-Key: YOUR_API_KEY"

Send a Message

curl -X POST http://localhost:2785/api/sessions/{sessionId}/messages/send-text \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -d '{
    "chatId": "[email protected]",
    "text": "Hello from OpenWA!"
  }'

Setup Webhook

curl -X POST http://localhost:2785/api/sessions/{sessionId}/webhooks \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -d '{
    "url": "https://your-server.com/webhook",
    "events": ["message.received", "session.status"],
    "secret": "your-hmac-secret"
  }'

Smart filters (optional): add a filters object to fire the webhook only when conditions match (AND), e.g. { "conditions": [{ "field": "sender", "operator": "is", "value": ["[email protected]"] }] }. Fields: sender / recipient / chatId / body / type / mentions / fromMe / hasMedia / isGroup / kind. A webhook with no filters behaves exactly as before. Use chatId to allowlist specific groups or DMs (e.g. { "field": "chatId", "operator": "is", "value": ["120…@g.us"] }). See the API specification for the full schema.

🤖 MCP Server (AI Agents)

OpenWA can expose a curated set of tools over the Model Context Protocol so AI agents (Claude, Cursor, …) can drive WhatsApp. It is off by default and additive — every REST route keeps working unchanged.

Set MCP_ENABLED=true to mount a stateless Streamable-HTTP transport at POST /mcp on the existing server (same port, no extra process). It mounts 25 read-only tools by default — session, message, contact, group, webhook, label and automation-rule reads — because the surface is read-only unless you opt out. Add MCP_READONLY=false to mount all 51 tools, adding the write tier (send, reply, group operations). Either way it is a focused surface rather than the full API, so agents aren't overwhelmed.

MCP_ENABLED=true npm run start:prod   # or set MCP_ENABLED in your .env / compose

Point an MCP client at it (e.g. for Claude Code, a .mcp.json at your project root):

{
  "mcpServers": {
    "openwa": {
      "type": "http",
      "url": "http://localhost:2785/mcp",
      "headers": { "Authorization": "Bearer YOUR_API_KEY" }
    }
  }
}

The key can be passed as Authorization: Bearer … or X-API-Key: …. Every tool call goes through the same API-key auth, role, and per-session scoping as REST.

Security guidance:

  • Mint a dedicated, least-privilege key for the agent — a non-admin, session-scoped key (OPERATOR role at most). The plaintext key is shown only once on creation; to rotate, create a new key and delete the old one.
  • The key must not carry an IP allow-list (allowedIps) — there is no genuine client IP over MCP, so such a key is rejected.
  • Set MCP_READONLY=true to mount only the read tools (no sends/writes).
  • Set MCP_RATE_LIMIT_MAX (default 60) to limit tool calls per API key per window.
  • Set MCP_RATE_LIMIT_WINDOW_MS (default 60000) to control the sliding window size in milliseconds.
  • Do not expose /mcp to the public internet without a fronting auth proxy. For a self-hosted, locally-reached deployment the static API key is appropriate; public exposure should use OAuth 2.1 (not yet built).

🛠 Tech Stack

Layer Technology
Runtime Node.js 22 LTS
Framework NestJS 11.x
Language TypeScript 6.x
WA Engine whatsapp-web.js (default) / baileys — set ENGINE_TYPE
Database SQLite / PostgreSQL
Cache Redis (optional)
Storage Local / S3 / MinIO
ORM TypeORM
Container Docker + Docker Compose

📁 Project Structure

openwa/
├── src/
│   ├── main.ts                 # Application entry point
│   ├── app.module.ts           # Root module
│   ├── config/                 # Configuration
│   ├── common/                 # Shared utilities
│   │   ├── cache/              # Redis caching
│   │   └── storage/            # File storage (Local/S3)
│   ├── core/                   # Core systems
│   │   ├── hooks/              # Plugin hooks
│   │   └── plugins/            # Plugin system
│   ├── engine/                 # WhatsApp engine abstraction
│   └── modules/
│       ├── session/            # Session management
│       ├── message/            # Message handling
│       ├── webhook/            # Webhook management
│       ├── group/              # Groups API
│       ├── contact/            # Contacts API
│       ├── auth/               # API key authentication
│       ├── infra/              # Infrastructure management
│       └── health/             # Health checks
├── dashboard/                  # React web dashboard
├── docs/                      # Documentation
├── docker-compose.yml
├── Dockerfile
└── package.json

📚 Documentation

Comprehensive documentation is available in the docs/ folder:

Document Description
Project Overview Introduction and goals
Requirements Feature specifications
Architecture System design
Security Security implementation
Database Data models and migrations
API Spec Complete API reference
Development Coding standards
Migration Guide Database & storage migration

🤝 Contributing

We welcome contributions! Here's how to get started:

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Please read our Development Guidelines for coding standards and best practices.


📄 License

This project is licensed under the MIT License – free for personal and commercial use.

See LICENSE for details.


OpenWA – Free, Open Source WhatsApp API Gateway

📖 Documentation · 🔌 API Docs · 🐛 Report Bug · 💡 Request Feature


Made with ❤️ by Yudhi Armyndharis and the OpenWA Community

View on GitHub

Recent activity

commits and pull requests

Releases and announcements

116 total
  1. v0.23.6v0.23.6Sep 23, 2026

    ### Added - API keys can carry an `allowedChats` allowlist next to `allowedSessions`, scoping a key to a chosen set of groups and contacts (omit or leave empty for unrestricted). A restricted key is refused with `403` on every route not explicitly marked safe, and on a marked route each chat it names is checked against the allowlist, with identity resolved through the `lid_mappings` table so a phone entry also matches its resolved `@lid` form; of the list routes only `GET /sessions/:sessionId/chats` is usable, and it filters before paginating. Thanks @bhavyachopra99 and @lasithadilshan. - Baileys inbound button, template quick-reply, list-row and native-flow replies arrive as `type: "text"` with a structured `button { id, text? }` on `message.received` (whatsapp-web.js still has no interactive reply fields). The REST chat-history route is whatsapp-web.js only and does not carry these fields. Thanks @gabrielmmoraes1999. - Baileys inbound business prompts that offer clickable buttons (or list rows) also carry `buttons: [{ id, text }, …]` on `message.received` (URL/call CTAs are omitted, since they cannot be clicked), so choices like Sim/Não are no longer flattened away into `body`

  2. v0.23.5v0.23.5Sep 15, 2026

    ### Security - The group invite-code read, over REST or the MCP `GroupGetInviteCode` tool, requires the OPERATOR role; the code is a transferable join capability, so a VIEWER key can no longer extract it ([GHSA-45fh-xj7x-vj2x](https://github.com/rmyndharis/OpenWA/security/advisories/GHSA-45fh-xj7x-vj2x)). Thanks Matija Petronijević for the report. - The amd64 image ships Chrome for Testing 153.0.8010.36 instead of 146.0.7680.31, picking up the browser security fixes released since (the arm64 image uses the Debian chromium package). - The `session.qr` WebSocket event reaches only OPERATOR and ADMIN keys, matching `GET /api/sessions/{sessionId}/qr`; a VIEWER key subscribed by name or through a wildcard no longer receives the pairing QR ([GHSA-m427-j4h4-9qwj](https://github.com/rmyndharis/OpenWA/security/advisories/GHSA-m427-j4h4-9qwj)). - An integration ingress route verified with `shared-secret` no longer stores the instance secret from its declared header; the value is redacted in the persisted event, the queued job, the dead-letter row and the `ingress:error` hook payload. - Baileys sessions with an HTTP, HTTPS or SOCKS5 proxy fetch through the proxy instead of connecting direct

  3. v0.23.4v0.23.4Sep 5, 2026

    ### Added - `GET /sessions/{sessionId}/chats` reports `muteExpiration`, the epoch-ms instant a mute ends (`0` = indefinite), alongside `muted` ([#1473](https://github.com/rmyndharis/OpenWA/issues/1473)). Thanks @usmancynosure and @purnamcommunity. - The dashboard Message Tester loads bulk recipients from a `.txt` or `.csv` file, one entry per line, appended to the Recipients box. Thanks @harry0x. - `GET /sessions/{sessionId}/messages` accepts `inlineMedia=false`, omitting inline media payloads while keeping each row's `{ omitted, sizeBytes }` marker and the media endpoint ([#1516](https://github.com/rmyndharis/OpenWA/issues/1516)). - `GET /sessions/{sessionId}/messages` accepts `after`, a keyset cursor on the previous page's last `id`, so a message arriving mid-walk cannot repeat or skip a page; `offset` is unchanged ([#1479](https://github.com/rmyndharis/OpenWA/issues/1479)). - Each engine names the install-time patches its library is missing at startup, not only the message-id backport. Diagnostic only; startup continues. See docs/12. - The dashboard API Keys page can scope an operator or viewer key to chosen sessions, on creation and after; an empty picker ke

  4. v0.23.3v0.23.3Aug 24, 2026

    ### Added - A previously linked session that comes back asking for a QR now logs a warning (`relink_required`) naming the likely causes, since an unlink that happened while the engine was down can leave no other trace. ### Changed - `GET /search` declares the plugin provider's failure answers in the contract: `502` for an invalid result shape and `503` when the provider does not answer; the built-in provider never returns either. - `POST /sessions/{sessionId}/pairing-code`: the 409 description in the OpenAPI contract and API reference now says to wait for `qr_ready`, not `ready`, which on this route means the session is already linked, and to wait for `ready` once a code was accepted. - `GET /sessions/{sessionId}/qr` no longer declares the engine-not-ready 409: the route reads the engine's cached QR and never answers one. Its 400 already covers the not-ready case. ### Fixed - Session auto-start no longer runs twice at boot. The plugin port for `SessionService` was a factory returning the same instance, which made Nest dispatch its lifecycle hooks twice: two auto-start loops raced, each session logged `Auto-start failed` with `Session is already starting`, and th

  5. v0.23.2v0.23.2Aug 23, 2026

    ### Fixed - Baileys: an inbound shared contact card's vCard now populates the message `body` instead of being silently dropped, matching what whatsapp-web.js returns for its `vcard` type. Several contacts shared together (`contactsArrayMessage`) are newline-joined into one multi-vCard body, in the order they were shared. Thanks @memarius. - The message-type filter on webhooks and automation rules accepts `poll`. The dashboard offered the option but saving was refused as invalid. - Baileys: inbound poll questions, shared event names and business button-reply selections now fill the message `body` instead of arriving empty. Webhook filters, search and the dashboard see this text on both engines now. - Baileys: quoting a contact card or poll keeps its text in the quoted-message preview instead of an empty string; the quote reuses the same body extraction as the live message. - `docker compose up -d` builds from a Windows clone again. Git's default `core.autocrlf=true` gave the committed PGDG signing key CRLF endings and the build failed with `NO_PUBKEY 7FCC7D46ACCC4CF8`. The key is now pinned to LF and the build strips CR, so a clone already on disk needs no re-clone

Code frequency

additions and deletions
+71.8K-71.8KWeek of 2026-02-01: +65,337 linesWeek of 2026-02-01: -0 linesWeek of 2026-02-08: +3 linesWeek of 2026-02-08: -0 linesWeek of 2026-02-15: +4,138 linesWeek of 2026-02-15: -2,136 linesWeek of 2026-02-22: +2,325 linesWeek of 2026-02-22: -2,086 linesWeek of 2026-03-01: +6 linesWeek of 2026-03-01: -6 linesWeek of 2026-03-08: +0 linesWeek of 2026-03-08: -0 linesWeek of 2026-03-15: +0 linesWeek of 2026-03-15: -0 linesWeek of 2026-03-22: +0 linesWeek of 2026-03-22: -0 linesWeek of 2026-03-29: +7 linesWeek of 2026-03-29: -7 linesWeek of 2026-04-05: +0 linesWeek of 2026-04-05: -0 linesWeek of 2026-04-12: +0 linesWeek of 2026-04-12: -0 linesWeek of 2026-04-19: +0 linesWeek of 2026-04-19: -0 linesWeek of 2026-04-26: +1,922 linesWeek of 2026-04-26: -2,517 linesWeek of 2026-05-03: +0 linesWeek of 2026-05-03: -0 linesWeek of 2026-05-10: +608 linesWeek of 2026-05-10: -608 linesWeek of 2026-05-17: +3,557 linesWeek of 2026-05-17: -3,205 linesWeek of 2026-05-24: +0 linesWeek of 2026-05-24: -0 linesWeek of 2026-05-31: +0 linesWeek of 2026-05-31: -0 linesWeek of 2026-06-07: +2,545 linesWeek of 2026-06-07: -1,233 linesWeek of 2026-06-14: +36,093 linesWeek of 2026-06-14: -5,390 linesWeek of 2026-06-21: +44,463 linesWeek of 2026-06-21: -13,802 linesWeek of 2026-06-28: +24,447 linesWeek of 2026-06-28: -5,102 linesWeek of 2026-07-05: +15,215 linesWeek of 2026-07-05: -1,962 linesWeek of 2026-07-12: +22,180 linesWeek of 2026-07-12: -2,898 linesWeek of 2026-07-19: +24,139 linesWeek of 2026-07-19: -7,166 linesWeek of 2026-07-26: +46,242 linesWeek of 2026-07-26: -10,885 linesWeek of 2026-08-02: +71,780 linesWeek of 2026-08-02: -28,445 linesWeek of 2026-08-09: +44,996 linesWeek of 2026-08-09: -15,471 linesWeek of 2026-08-16: +15,967 linesWeek of 2026-08-16: -3,186 linesWeek of 2026-08-23: +4,578 linesWeek of 2026-08-23: -1,186 linesWeek of 2026-08-30: +9,671 linesWeek of 2026-08-30: -3,233 linesWeek of 2026-09-06: +2,382 linesWeek of 2026-09-06: -670 linesWeek of 2026-09-13: +2,277 linesWeek of 2026-09-13: -899 linesFeb 1, 2026Sep 13, 2026
+444.9K lines added, -112.1K removed over the last year.

Commits per week

last 52 weeks
3190Week of 2025-09-27: 0 commitsWeek of 2025-10-04: 0 commitsWeek of 2025-10-11: 0 commitsWeek of 2025-10-18: 0 commitsWeek of 2025-10-25: 0 commitsWeek of 2025-11-01: 0 commitsWeek of 2025-11-09: 0 commitsWeek of 2025-11-16: 0 commitsWeek of 2025-11-23: 0 commitsWeek of 2025-11-30: 0 commitsWeek of 2025-12-07: 0 commitsWeek of 2025-12-14: 0 commitsWeek of 2025-12-21: 0 commitsWeek of 2025-12-28: 0 commitsWeek of 2026-01-04: 0 commitsWeek of 2026-01-11: 0 commitsWeek of 2026-01-18: 0 commitsWeek of 2026-01-25: 0 commitsWeek of 2026-02-01: 1 commitsWeek of 2026-02-08: 1 commitsWeek of 2026-02-15: 38 commitsWeek of 2026-02-22: 8 commitsWeek of 2026-03-01: 2 commitsWeek of 2026-03-08: 0 commitsWeek of 2026-03-15: 0 commitsWeek of 2026-03-22: 0 commitsWeek of 2026-03-29: 4 commitsWeek of 2026-04-05: 0 commitsWeek of 2026-04-12: 0 commitsWeek of 2026-04-19: 0 commitsWeek of 2026-04-26: 3 commitsWeek of 2026-05-03: 0 commitsWeek of 2026-05-10: 1 commitsWeek of 2026-05-17: 17 commitsWeek of 2026-05-24: 0 commitsWeek of 2026-05-31: 0 commitsWeek of 2026-06-07: 28 commitsWeek of 2026-06-14: 171 commitsWeek of 2026-06-21: 121 commitsWeek of 2026-06-28: 144 commitsWeek of 2026-07-05: 60 commitsWeek of 2026-07-12: 74 commitsWeek of 2026-07-19: 138 commitsWeek of 2026-07-26: 170 commitsWeek of 2026-08-02: 283 commitsWeek of 2026-08-09: 319 commitsWeek of 2026-08-16: 155 commitsWeek of 2026-08-23: 38 commitsWeek of 2026-08-30: 99 commitsWeek of 2026-09-06: 22 commitsWeek of 2026-09-13: 122 commitsWeek of 2026-09-20: 195 commitsSep 27, 2025Sep 20, 2026
2.2K commits in the last 52 weeks.

When work happens

weekday and hour
SunMonTueWedThuFriSat036912151821Sun 0:00 — 8 commitsSun 1:00 — 11 commitsSun 2:00 — 7 commitsSun 3:00 — 0 commitsSun 4:00 — 0 commitsSun 5:00 — 0 commitsSun 6:00 — 3 commitsSun 7:00 — 6 commitsSun 8:00 — 1 commitsSun 9:00 — 10 commitsSun 10:00 — 12 commitsSun 11:00 — 25 commitsSun 12:00 — 14 commitsSun 13:00 — 31 commitsSun 14:00 — 21 commitsSun 15:00 — 15 commitsSun 16:00 — 12 commitsSun 17:00 — 12 commitsSun 18:00 — 17 commitsSun 19:00 — 36 commitsSun 20:00 — 35 commitsSun 21:00 — 10 commitsSun 22:00 — 17 commitsSun 23:00 — 12 commitsMon 0:00 — 8 commitsMon 1:00 — 3 commitsMon 2:00 — 3 commitsMon 3:00 — 1 commitsMon 4:00 — 1 commitsMon 5:00 — 3 commitsMon 6:00 — 4 commitsMon 7:00 — 19 commitsMon 8:00 — 13 commitsMon 9:00 — 29 commitsMon 10:00 — 39 commitsMon 11:00 — 37 commitsMon 12:00 — 22 commitsMon 13:00 — 26 commitsMon 14:00 — 37 commitsMon 15:00 — 23 commitsMon 16:00 — 18 commitsMon 17:00 — 14 commitsMon 18:00 — 16 commitsMon 19:00 — 18 commitsMon 20:00 — 28 commitsMon 21:00 — 61 commitsMon 22:00 — 17 commitsMon 23:00 — 12 commitsTue 0:00 — 8 commitsTue 1:00 — 1 commitsTue 2:00 — 2 commitsTue 3:00 — 32 commitsTue 4:00 — 1 commitsTue 5:00 — 2 commitsTue 6:00 — 0 commitsTue 7:00 — 23 commitsTue 8:00 — 14 commitsTue 9:00 — 38 commitsTue 10:00 — 16 commitsTue 11:00 — 23 commitsTue 12:00 — 15 commitsTue 13:00 — 14 commitsTue 14:00 — 6 commitsTue 15:00 — 13 commitsTue 16:00 — 15 commitsTue 17:00 — 11 commitsTue 18:00 — 11 commitsTue 19:00 — 12 commitsTue 20:00 — 8 commitsTue 21:00 — 15 commitsTue 22:00 — 18 commitsTue 23:00 — 15 commitsWed 0:00 — 34 commitsWed 1:00 — 5 commitsWed 2:00 — 10 commitsWed 3:00 — 4 commitsWed 4:00 — 1 commitsWed 5:00 — 4 commitsWed 6:00 — 3 commitsWed 7:00 — 12 commitsWed 8:00 — 15 commitsWed 9:00 — 19 commitsWed 10:00 — 30 commitsWed 11:00 — 20 commitsWed 12:00 — 28 commitsWed 13:00 — 20 commitsWed 14:00 — 18 commitsWed 15:00 — 7 commitsWed 16:00 — 14 commitsWed 17:00 — 16 commitsWed 18:00 — 24 commitsWed 19:00 — 17 commitsWed 20:00 — 25 commitsWed 21:00 — 17 commitsWed 22:00 — 30 commitsWed 23:00 — 14 commitsThu 0:00 — 12 commitsThu 1:00 — 7 commitsThu 2:00 — 7 commitsThu 3:00 — 2 commitsThu 4:00 — 0 commitsThu 5:00 — 1 commitsThu 6:00 — 0 commitsThu 7:00 — 8 commitsThu 8:00 — 8 commitsThu 9:00 — 13 commitsThu 10:00 — 22 commitsThu 11:00 — 25 commitsThu 12:00 — 20 commitsThu 13:00 — 18 commitsThu 14:00 — 18 commitsThu 15:00 — 13 commitsThu 16:00 — 15 commitsThu 17:00 — 35 commitsThu 18:00 — 10 commitsThu 19:00 — 13 commitsThu 20:00 — 24 commitsThu 21:00 — 22 commitsThu 22:00 — 13 commitsThu 23:00 — 13 commitsFri 0:00 — 15 commitsFri 1:00 — 9 commitsFri 2:00 — 5 commitsFri 3:00 — 0 commitsFri 4:00 — 1 commitsFri 5:00 — 0 commitsFri 6:00 — 1 commitsFri 7:00 — 17 commitsFri 8:00 — 8 commitsFri 9:00 — 13 commitsFri 10:00 — 11 commitsFri 11:00 — 17 commitsFri 12:00 — 6 commitsFri 13:00 — 17 commitsFri 14:00 — 15 commitsFri 15:00 — 11 commitsFri 16:00 — 18 commitsFri 17:00 — 9 commitsFri 18:00 — 8 commitsFri 19:00 — 15 commitsFri 20:00 — 8 commitsFri 21:00 — 15 commitsFri 22:00 — 23 commitsFri 23:00 — 10 commitsSat 0:00 — 16 commitsSat 1:00 — 16 commitsSat 2:00 — 5 commitsSat 3:00 — 1 commitsSat 4:00 — 0 commitsSat 5:00 — 0 commitsSat 6:00 — 0 commitsSat 7:00 — 9 commitsSat 8:00 — 7 commitsSat 9:00 — 6 commitsSat 10:00 — 9 commitsSat 11:00 — 9 commitsSat 12:00 — 7 commitsSat 13:00 — 13 commitsSat 14:00 — 17 commitsSat 15:00 — 16 commitsSat 16:00 — 5 commitsSat 17:00 — 4 commitsSat 18:00 — 5 commitsSat 19:00 — 9 commitsSat 20:00 — 2 commitsSat 21:00 — 7 commitsSat 22:00 — 7 commitsSat 23:00 — 8 commits
Commit volume by weekday and hour (UTC). Larger dots mean more commits.

Who is committing

last 52 weeks
Maintainer commits2,698 (91%)
Community commits269 (9%)

2,967 commits in total over the last year.

DateListRankStars gained
May 20, 2026daily#25+40
May 19, 2026daily#8+67
May 18, 2026daily#12+77
  • public-apis/public-apis

    A collective list of free APIs

    486.1K stars · Python

  • freeCodeCamp/freeCodeCamp

    freeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.

    456.7K stars · TypeScript

  • openclaw/openclaw

    The AI that really does things. Any OS. Any Platform. The lobster way. 🦞

    391.3K stars · TypeScript

  • awesome-selfhosted/awesome-selfhosted

    A list of Free Software network services and web applications which can be hosted on your own servers

    323.8K stars

  • anomalyco/opencode

    The open source coding agent.

    211.7K stars · TypeScript

  • n8n-io/n8n

    Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.

    206.7K stars · TypeScript