aldinokemal/go-whatsapp-web-multidevice
GOWA - WhatsApp REST API with support for UI, Multi Account, Webhooks, and MCP, and Chatwoot. Built with Golang for efficient memory use.
About aldinokemal/go-whatsapp-web-multidevice
aldinokemal/go-whatsapp-web-multidevice is an open-source project on GitHub, mainly written in Go. GOWA - WhatsApp REST API with support for UI, Multi Account, Webhooks, and MCP, and Chatwoot. Built with Golang for efficient memory use. It currently holds 4,819 stars and 1,141 forks with 0 open issues, and was last pushed on an unknown date (repository created unknown).
Project Overview
AI Homed tracks it on the Today's Trending board, currently at rank #61 with 26 new stars today.
GitHub Repository Details
README
Go WhatsApp — Built for Efficient Memory Use
If you're using this tool to generate income, consider supporting its development by becoming a Patreon member!
Your support helps ensure the project stays maintained and receives regular updates!
---
ARM, AMD64, and MCP Support
Download:
n8n Community Node
- n8n package
- Go to Settings → Community Nodes, enter
@aldinokemal2104/n8n-nodes-gowa, and select Install.
Breaking Changes
v6- REST mode requires
restinstead of ``. - Example:
./whatsapp restinstead of./whatsapp. - MCP mode required
mcp. - Example:
./whatsapp mcp. v7- Starting with version 7.x, binaries are built with GoReleaser and can be downloaded from the
v8- Multi-device support: You can now connect and manage multiple WhatsApp accounts simultaneously in a single
- New Device Management API: New endpoints under
/devicesmanage multiple devices. - Device scoping required: All device-scoped REST API calls now require either:
X-Device-Idheader, ordevice_idquery parameter.- If only one device is registered, it is used as the default.
- WebSocket device scoping: Connect to
/ws?device_id=to scope the WebSocket connection to a specific device. - Remote UI support: CORS allows the
AuthorizationandX-Device-Idheaders, so a standalone web UI
GET /app/info exposes the version and media size limits. Because browsers cannot set headers on WebSocket
connections, pass /ws?device_id=&authorization= when Basic Auth is enabled
(use TLS—the credential is visible in the URL).
- Webhook payload changes: All webhook payloads now include a top-level
device_idfield identifying which
{
"event": "message",
"device_id": "628123456789@s.whatsapp.net",
"payload": { ... }
}
v9- MCP and API are unified under
rest: MCP is no longer a separate mode or process. Run
./whatsapp rest to serve both the REST API and MCP; MCP is available at /mcp (no standalone mcp
subcommand). See MCP Server (Model Context Protocol) for migration
details.
- UI moved to a separate repository: The web dashboard is no longer bundled in this repo. It now lives at
gowa-ui.html. The server downloads the latest dashboard release at startup, verifies its SHA-256 digest,
caches it under storages/ui/, and serves it at /.
See Web dashboard (gowa-ui) for the APP_UI_* settings, supply-chain pinning,
and air-gapped deployment.
Features
- Send WhatsApp messages through the HTTP API. See docs/openapi.yaml for details.
- MCP (Model Context Protocol) server support — Integrate with AI agents and tools using a standardized protocol.
- Optional MCP OAuth 2.1 — Connect remote MCP clients that cannot supply a Basic Auth header. See
- Mention users:
@phoneNumber- Example:
Hello @628974812XXXX, @628974812XXXX - Ghost mentions (mention all) — Mention group participants without showing
@phonein the message text. - Pass phone numbers in the
mentionsfield to mention users without a visible@in the message. - Use the special keyword
@everyoneto automatically mention all group participants. - Post WhatsApp status updates.
- Mark incoming audio messages and voice notes as played.
- Send stickers — Automatically convert images to WebP sticker format.
- Supports JPG, JPEG, PNG, WebP, and GIF formats.
- Automatically resizes images to 512×512 pixels.
- Preserves transparency in PNG images.
- Animated WebP stickers are supported but must meet WhatsApp requirements:
- Exactly 512×512 pixels.
- Less than 500 KB.
- No more than 10 seconds long.
- If an animated sticker does not meet these requirements, resize it before uploading with a tool such as
- Compress images before sending.
- Compress videos before sending.
- Customize the OS name shown as the linked device name in WhatsApp:
--os=Chromeor--os=MyApplication- Basic Auth with multiple credentials:
--basic-auth=kemal:secret,toni:password,userName:secretPassword- Short form:
-b=kemal:secret,toni:password,userName:secretPassword - Subpath deployment support:
--base-path="/gowa"allows deployment under a path such as/gowa.- Customizable port and debug mode:
--port 8000--debug true- Automatic replies to incoming messages:
--autoreply="Don't reply to this message"- Automatically mark incoming messages as read:
--auto-mark-read=true- Automatically download media from incoming messages:
--auto-download-media=falsedisables automatic media downloads (default:true).- Automatically reject incoming calls:
--auto-reject-call=trueorWHATSAPP_AUTO_REJECT_CALL=true(see
- Configurable presence on connect:
--presence-on-connect=unavailableorWHATSAPP_PRESENCE_ON_CONNECT=unavailableavailable— Mark the account as online (suppresses phone notifications).unavailable— Register the push name without going online (default; preserves phone notifications).none— Skip presence entirely (the push name is not registered, so contacts may see-as the name).- Daily presence pulse:
--presence-pulse-enabled=trueorWHATSAPP_PRESENCE_PULSE_ENABLED=true(default:true).--presence-pulse-interval=24hcontrols how often each connected device is pulsed.--presence-pulse-duration=5mcontrols how long the account staysavailablebefore returning tounavailable.- Webhooks for received messages and other events:
--webhook="http://yourwebhook.site/handler"- Short form:
-w="http://yourwebhook.site/handler" - See Webhook Payload Documentation for details.
- Per-device webhooks — Each device can have its own webhook URL and event filters.
- Set via API:
PATCH /devices/:device_id/webhookwith{"webhook_url": "https://device-webhook.site/handler"}. - Get via API:
GET /devices/:device_id/webhook. - When a device has a custom webhook, events for that device are sent to the device-specific URL.
- When no device webhook is set, events fall back to the global webhook (
--webhook). - Set
webhook_urlto an empty string withPATCHto clear it and use the global webhook. - Webhook signatures — Webhook requests include an HMAC-SHA-256 signature in the
X-Hub-Signature-256
secret.
Change the key with:
--webhook-secret="secret"- Webhook payload documentation — For detailed schemas, security implementation, and integration examples,
- Webhook event filtering — Filter which events are forwarded to your webhook with:
--webhook-events="message,message.ack"(a comma-separated list), orWHATSAPP_WEBHOOK_EVENTS=message,message.ack.
| Event | Description |
|----------------------|-----------------------------------------------|
| message | Text, media, contact, location messages |
| message.reaction | Emoji reactions to messages |
| message.revoked | Deleted/revoked messages |
| message.edited | Edited messages |
| message.ack | Delivery and read receipts |
| message.deleted | Messages deleted for the user |
| chat_presence | Typing and recording indicators from contacts |
| group.participants | Group member join/leave/promote/demote events |
| group.joined | You were added to a group |
| label.edit | WhatsApp label metadata changed |
| label.association | Label applied to or removed from a chat |
| newsletter.joined | You subscribed to a newsletter/channel |
| newsletter.left | You unsubscribed from a newsletter |
| newsletter.message | New message(s) posted in a newsletter |
| newsletter.mute | Newsletter mute setting changed |
| call.offer | Incoming call received |
If this setting is empty, all events are forwarded.
- Webhook JID filtering
--webhook-ignore-jids="@g.us,628123456789@s.whatsapp.net"(a comma-separated list), orWHATSAPP_WEBHOOK_IGNORE_JIDS=@g.us.- Supports the
@g.us/@s.whatsapp.net/@lidwildcards (match a whole address space) and exact JIDs. - This filters by conversation or sender and is independent of
--webhook-events, which filters by event type.
CHATWOOT_IGNORE_JIDS setting.
- Webhook TLS configuration
tls: failed to verify certificate: x509: certificate signed by unknown authority
You can disable TLS certificate verification with:
--webhook-insecure-skip-verify=true, orWHATSAPP_WEBHOOK_INSECURE_SKIP_VERIFY=true.
- Development or testing environments.
- Cloudflare tunnels, which provide their own security layer.
- Internal networks with self-signed certificates.
Configuration
Configuration is loaded in this order of priority:
1. Command-line flags (highest priority)
2. Environment variables
3. .env file (lowest priority)
Environment Variables
To use environment variables:
1. From the repository root, copy the example file: cp src/.env.example src/.env.
2. Update the values in src/.env as needed.
3. Alternatively, set the same variables in the process environment.
Available Environment Variables
| Variable | Description | Default | Example |
|-----------------------------------------|---------------------------------------------------------------|----------------------------------------------|-----------------------------------------------|
| APP_PORT | Application port | 3000 | APP_PORT=8080 |
| APP_HOST | Host address to bind the server | 0.0.0.0 | APP_HOST=127.0.0.1 |
| APP_DEBUG | Enable debug logging | false | APP_DEBUG=true |
| APP_OS | OS name (device name in WhatsApp) | GOWA | APP_OS=MyApp |
| APP_BASIC_AUTH | Basic authentication credentials | - | APP_BASIC_AUTH=user1:pass1,user2:pass2 |
| APP_BASE_PATH | Base path for subpath deployment | - | APP_BASE_PATH=/gowa |
| APP_TRUSTED_PROXIES | Trusted proxy IP ranges for reverse proxy | - | APP_TRUSTED_PROXIES=0.0.0.0/0 |
| APP_CORS_ALLOWED_ORIGINS | Allowed CORS origins (any origin when empty) | - | APP_CORS_ALLOWED_ORIGINS=https://ui.example.com |
| APP_UI_ENABLED | Serve the downloaded gowa-ui dashboard | true | APP_UI_ENABLED=false |
| APP_UI_AUTO_UPDATE | Download and periodically refresh the latest dashboard | true | APP_UI_AUTO_UPDATE=false |
| APP_UI_REPO | GitHub repository containing gowa-ui releases | aldinokemal/gowa-ui | APP_UI_REPO=my-org/gowa-ui |
| APP_UI_ASSET_NAME | Dashboard release asset filename | gowa-ui.html | APP_UI_ASSET_NAME=gowa-ui.html |
| APP_UI_UPDATE_INTERVAL | Interval between dashboard update checks | 3h | APP_UI_UPDATE_INTERVAL=6h |
| APP_UI_GITHUB_TOKEN | Optional GitHub token for a higher API rate limit | - | APP_UI_GITHUB_TOKEN=github_pat_xxx |
| APP_UI_ASSET_SHA256 | Optional SHA-256 pin for the dashboard asset | - | APP_UI_ASSET_SHA256= |
| MCP_ENABLED | Serve the streamable HTTP MCP endpoint at /mcp | true | MCP_ENABLED=false |
| MCP_OAUTH_ENABLED | Enable OAuth 2.1 authentication for MCP | false | MCP_OAUTH_ENABLED=true |
| MCP_OAUTH_ISSUER_URL | Public HTTPS OAuth issuer URL | - | MCP_OAUTH_ISSUER_URL=https://gowa.example.com |
| MCP_OAUTH_RESOURCE_URL | Optional canonical public MCP URL | Derived from issuer and base path | MCP_OAUTH_RESOURCE_URL=https://gowa.example.com/mcp |
| MCP_OAUTH_DB_URI | SQLite URI for OAuth clients, codes, and token hashes | file:storages/oauth.db | MCP_OAUTH_DB_URI=file:storages/oauth.db |
| DB_URI | Database connection URI | file:storages/whatsapp.db | DB_URI=postgres://user:pass@host/db |
| DB_KEYS_URI | Optional database URI for encryption/session key cache. Leave blank to use DB_URI; avoid in-memory storage in production because restarts can lose WhatsApp session state. | - | DB_KEYS_URI=file:storages/whatsapp-keys.db?_foreign_keys=on |
| CHAT_STORAGE_MAX_OPEN_CONNS | Maximum concurrent SQLite connections for chat storage | 5 | CHAT_STORAGE_MAX_OPEN_CONNS=10 |
| WHATSAPP_AUTO_REPLY | Auto-reply message | - | WHATSAPP_AUTO_REPLY="Auto reply message" |
| WHATSAPP_AUTO_MARK_READ | Auto-mark incoming messages as read | false | WHATSAPP_AUTO_MARK_READ=true |
| WHATSAPP_AUTO_DOWNLOAD_MEDIA | Auto-download media from incoming messages | true | WHATSAPP_AUTO_DOWNLOAD_MEDIA=false |
| WHATSAPP_AUTO_REJECT_CALL | Auto-reject incoming WhatsApp calls | false | WHATSAPP_AUTO_REJECT_CALL=true |
| WHATSAPP_WEBHOOK | Webhook URL(s) for events (comma-separated) | - | WHATSAPP_WEBHOOK=https://webhook.site/xxx |
| WHATSAPP_WEBHOOK_SECRET | Webhook secret for validation | secret | WHATSAPP_WEBHOOK_SECRET=super-secret-key |
| WHATSAPP_WEBHOOK_INSECURE_SKIP_VERIFY | Skip TLS verification for webhooks (insecure) | false | WHATSAPP_WEBHOOK_INSECURE_SKIP_VERIFY=true |
| WHATSAPP_WEBHOOK_EVENTS | Whitelist of events to forward (comma-separated, empty = all) | - | WHATSAPP_WEBHOOK_EVENTS=message,message.ack |
| WHATSAPP_WEBHOOK_IGNORE_JIDS | JIDs/wildcards to skip when forwarding (comma-separated) | - | WHATSAPP_WEBHOOK_IGNORE_JIDS=@g.us |
| WHATSAPP_ACCOUNT_VALIDATION | Enable account validation | true | WHATSAPP_ACCOUNT_VALIDATION=false |
| WHATSAPP_PRESENCE_ON_CONNECT | Presence on connect: available, unavailable, or none | unavailable | WHATSAPP_PRESENCE_ON_CONNECT=unavailable |
| WHATSAPP_PROXY | Outbound proxy for the WhatsApp WebSocket (SOCKS5/HTTP/HTTPS) | - | WHATSAPP_PROXY=socks5://user:pass@host:1080 |
| WHATSAPP_PRESENCE_PULSE_ENABLED | Enable daily available/unavailable presence pulse | true | WHATSAPP_PRESENCE_PULSE_ENABLED=false |
| WHATSAPP_PRESENCE_PULSE_INTERVAL | Interval between presence pulses | 24h | WHATSAPP_PRESENCE_PULSE_INTERVAL=24h |
| WHATSAPP_PRESENCE_PULSE_DURATION | Duration to stay available during each pulse | 5m | WHATSAPP_PRESENCE_PULSE_DURATION=5m |
| CHATWOOT_ENABLED | Enable Chatwoot integration | false | CHATWOOT_ENABLED=true |
| CHATWOOT_URL | Chatwoot instance URL | - | CHATWOOT_URL=https://app.chatwoot.com |
| CHATWOOT_API_TOKEN | Chatwoot API access token | - | CHATWOOT_API_TOKEN=your-api-token |
| CHATWOOT_ACCOUNT_ID | Chatwoot account ID | - | CHATWOOT_ACCOUNT_ID=12345 |
| CHATWOOT_INBOX_ID | Chatwoot inbox ID | - | CHATWOOT_INBOX_ID=67890 |
| CHATWOOT_DEVICE_ID | WhatsApp device ID for Chatwoot (single-device/env fallback) | - | CHATWOOT_DEVICE_ID=628xxx@s.whatsapp.net |
| CHATWOOT_ALLOWED_HOSTS | Allowlist of Chatwoot hosts for per-device configs (SSRF guard) | - | CHATWOOT_ALLOWED_HOSTS=app.chatwoot.com,chat.example.com |
| CHATWOOT_IMPORT_MESSAGES | Enable message history sync to Chatwoot | false | CHATWOOT_IMPORT_MESSAGES=true |
| CHATWOOT_DAYS_LIMIT_IMPORT_MESSAGES | Days of history to import | 3 | CHATWOOT_DAYS_LIMIT_IMPORT_MESSAGES=7 |
| CHATWOOT_IMPORT_DB_URI | Direct Chatwoot PostgreSQL URI for history sync | - | CHATWOOT_IMPORT_DB_URI=postgresql://user:pass@host:5432/chatwoot_production?sslmode=disable |
| CHATWOOT_IMPORT_PLACEHOLDER_MEDIA_MESSAGE | Insert text placeholders for media rows during direct DB import | true | CHATWOOT_IMPORT_PLACEHOLDER_MEDIA_MESSAGE=true |
| CHATWOOT_IMPORT_MEDIA_WITH_REST | Upload direct-DB import media rows through Chatwoot REST | false | CHATWOOT_IMPORT_MEDIA_WITH_REST=true |
| CHATWOOT_AUTO_CREATE | Auto-create or reuse the Chatwoot API inbox at startup | false | CHATWOOT_AUTO_CREATE=true |
| CHATWOOT_INBOX_NAME | Inbox name used when auto-create is enabled | WhatsApp | CHATWOOT_INBOX_NAME=WhatsApp Support |
| CHATWOOT_WEBHOOK_URL | Public GOWA Chatwoot reply webhook URL | - | CHATWOOT_WEBHOOK_URL=https://api.example.com/chatwoot/webhook?secret=shared |
| CHATWOOT_WEBHOOK_SECRET | Shared secret required for incoming Chatwoot webhooks | - | CHATWOOT_WEBHOOK_SECRET=shared |
| CHATWOOT_REOPEN_CONVERSATION | Reopen resolved Chatwoot conversations for returning contacts | true | CHATWOOT_REOPEN_CONVERSATION=false |
| CHATWOOT_CONVERSATION_PENDING | Create new Chatwoot conversations as pending | false | CHATWOOT_CONVERSATION_PENDING=true |
| `CHATWOOT_IGNOR