Skip to main content
With Chatwoot enabled, GOWA forwards WhatsApp messages (incoming, and outgoing ones sent from the phone) into a Chatwoot API channel inbox, and sends agent replies from Chatwoot back to WhatsApp. Text, images, audio, video, documents, stickers, locations and contacts are supported; group chats appear as one conversation per group, with the sender name prefixed.

Requirements

  • GOWA reachable from Chatwoot over a public URL (for agent-reply webhooks).
  • A Chatwoot account with admin access and a user API access token.
  • At least one logged-in WhatsApp device.

Set up

1

Create an API channel inbox

In Chatwoot go to Settings → Inboxes → Add Inbox → API. Note the numeric inbox ID from the inbox URL, and the webhook secret Chatwoot shows for the channel. Alternatively, let GOWA create the inbox (see Auto-provisioning).
2

Collect IDs and token

The account ID is the number in /app/accounts/<id>/.... Copy a user access token from Profile Settings. Both IDs must be the numeric IDs, not the channel identifier or hmac_token.
3

Configure GOWA

.env
Restart GOWA: the Chatwoot routes are registered at startup.
4

Point Chatwoot at GOWA

Set the API channel’s webhook URL to https://gowa.example.com/chatwoot/webhook (with APP_BASE_PATH, include the base path). This route is public: it is protected by the webhook secret, not Basic Auth.
5

Test

Message the WhatsApp number from another phone; it should appear in the inbox. Reply from Chatwoot and check it arrives on WhatsApp.

Configuration

Most settings also have a --chatwoot-* flag; CHATWOOT_URL, CHATWOOT_API_TOKEN, CHATWOOT_ACCOUNT_ID and CHATWOOT_INBOX_ID are environment-only. See the configuration reference.

Webhook secret

When CHATWOOT_WEBHOOK_SECRET (or a device’s webhook_secret) is set, every webhook must prove it by one of:
  1. Chatwoot’s signature (X-Chatwoot-Signature + X-Chatwoot-Timestamp), recommended. Use the API channel’s own webhook secret. Replays are rejected.
  2. An X-Hub-Signature-256 HMAC of the body.
  3. The secret in an X-Chatwoot-Webhook-Secret or X-Gowa-Chatwoot-Secret header.
  4. ?secret= in the webhook URL, only for Chatwoot versions that cannot sign or add headers. The secret then appears in access logs.
Failures return 401; GOWA logs the reason (signature does not match = wrong secret, timestamp outside the tolerance window = clock drift).

Auto-provisioning

With CHATWOOT_AUTO_CREATE=true and no CHATWOOT_INBOX_ID, GOWA looks for an API inbox named CHATWOOT_INBOX_NAME at startup and reuses it, or creates one with CHATWOOT_WEBHOOK_URL as its webhook.

One inbox per device

The environment variables configure one destination shared by all devices. To route each device to its own inbox, account or Chatwoot server, store per-device configs (Basic Auth applies):
The response includes webhook_url (<base>/chatwoot/webhook/<device_id>); set it as that inbox’s webhook. Other routes: GET /chatwoot/configs, GET and DELETE /devices/{device_id}/chatwoot/config.
Once any per-device config exists, the CHATWOOT_* destination is no longer used as a fallback. Devices without a config are not mirrored.
  • api_token is stored in GOWA’s database and masked in reads; webhook_secret is never returned.
  • chatwoot_url must be http(s), without credentials, and must not resolve to private or loopback addresses. Restrict it further with CHATWOOT_ALLOWED_HOSTS.
  • Changing chatwoot_url, account_id or inbox_id after conversations are linked returns 409; delete and recreate the config instead.
  • A config can be created before the device pairs.
  • Direct database import works only in single (environment) config mode.

Import history

Defaults: days_limit 3, include_media true, include_groups true. The sync reads GOWA’s local chat storage, runs in the background, and only one sync per device can run at a time. Media older than about two weeks may no longer be downloadable from WhatsApp. The REST path cannot set original timestamps; imported messages get the import time. For self-hosted Chatwoot, CHATWOOT_IMPORT_DB_URI writes directly to Chatwoot’s PostgreSQL and preserves them.

Delivery and retries

Live messages that hit a transient Chatwoot error (network error, HTTP 429 or 5xx) are queued in local storage and retried in the background with capped exponential backoff. Other 4xx errors are logged and dropped.

Troubleshooting

Run with APP_DEBUG=true and look for log lines starting with Chatwoot: and Chatwoot Webhook:. The upstream Chatwoot guide covers direct database import and formatting rules in more depth.