Chatwoot

WAHA includes a built-in WhatsApp integration for Chatwoot. Configure it in a few steps via 🧩 Apps.

Chatwoot Overview

Disclaimer

This project is not affiliated, associated, authorized, endorsed by, or in any way officially connected with WhatsApp, ChatWoot, or any of their respective subsidiaries or affiliates. The official websites can be found at https://whatsapp.com and https://chatwoot.com.

For businesses seeking to integrate with WhatsApp for critical applications, we strongly recommend using officially supported methods.

Installation

We cover all installation and configuration aspects in the following series of articles:

ChatWoot Articles

Series of articles about WhatsApp and ChatWoot integration using 🧩 ChatWoot App:

  1. WhatsApp + ChatWoot - Overview
  2. WhatsApp + ChatWoot - Installation Guide
  3. WhatsApp + ChatWoot - Configuration Guide
  4. WhatsApp + ChatWoot - HTTPS Guide
  5. WhatsApp + ChatWoot - How It Works

You can follow them one by one or skip some parts if you don’t need them.

Configuration

To use 🧩 Apps , you need to configure the following environment variables in addition to the standard ⚙️ Configuration:

Apps:

  • WAHA_APPS_ENABLED=True - Enables the 🧩 Apps functionality
  • WAHA_PUBLIC_URL=https://w.example.com — the publicly available link to the dashboard (use this if WAHA_BASE_URL is set to an internal address for Docker).
  • REDIS_URL=redis://:redis@redis:6379 - Specifies the Redis URL required for processing background jobs
  • WHATSAPP_DEFAULT_ENGINE=GOWS - Sets the recommended engine for reliable WhatsApp automation
  • WAHA_API_KEY_PLAIN=0000000000000000 - plain password required for Apps in environment variables
    • It’s a quick solution, we’re working on removing it so you can use sha512 version as in WAHA_API_KEY

ChatWoot App:

  • WAHA_APPS_CHATWOOT_LANGUAGES_FOLDER=/app/.languages - folder for additional languages (if language exists it’ll override templates in the original templates)
  • RACK_TIMEOUT_SERVICE_TIMEOUT=60 — increases the default Rack timeout to 60s (default is 15s).

Jobs:

You can configure a background worker http://localhost:3000/jobs

  • WAHA_APPS_JOBS_CONCURRENCY=50 - Maximum number of jobs processed concurrently
  • WAHA_APPS_JOBS_REMOVE_ON_COMPLETE_AGE=259200 - Remove completed jobs after 3 days (in seconds)
  • WAHA_APPS_JOBS_REMOVE_ON_COMPLETE_COUNT=1000 - Maximum number of completed jobs to keep
  • WAHA_APPS_JOBS_REMOVE_ON_FAIL_AGE=2678400 - Remove failed jobs after 31 days (in seconds)
  • WAHA_APPS_JOBS_REMOVE_ON_FAIL_COUNT=1000 - Maximum number of failed jobs to keep

Note: *_AGE parameters are specified in seconds by default.

Under the hood it uses bullmq.

Conversations

You can map WhatsApp chats to Chatwoot conversations in two ways. Your Chatwoot and WAHA settings must be kept in sync.

  • Single Conversation — one ongoing thread per WhatsApp chat; new messages append to the same conversation (reopens if resolved).
  • Multiple Conversations — agents resolve threads; the next incoming message creates a new conversation, allowing several per contact.

Single Conversation

One conversation per WhatsApp chat (WhatsApp-like behavior). Each message goes to a single conversation. Every contact in Chatwoot has only one open (or resolved) conversation, and incoming WhatsApp messages are added to that conversation (and reopen it if it’s closed).

Set in Chatwoot:

  • Inbox - Lock to single conversation - Enabled

Set in WAHA:

  • Select conversation behavior - Created: Newest
  • Use Conversation with Status - Use Any Status

Multiple Conversations

Multiple conversations per WhatsApp chat (Chatwoot’s philosophy).

A conversation remains active until an agent resolves it. After resolution, a new incoming message creates a new conversation.

Set in Chatwoot:

  • Inbox - Lock to single conversation - Disabled

Set in WAHA:

  • Select conversation behavior - Activity: Newest
  • Use Conversation with Status - Use Only: Open, Pending, Snoozed

Activity Includes Conversation Status Changes

If you set Select conversation behavior to Activity: Newest, Chatwoot treats conversation status changes as activity.

Language

You can adjust messages, templates, and branding in the content WAHA sends to Chatwoot and WhatsApp by providing key-value overrides in YAML.

What Templates Can I Override?

You can find all languages and keys in the source code /src/apps/chatwoot/i18n/locales

Per-app Language Overrides

The easiest way to test or apply a few template overrides is to use the 📊 Dashboard, then set Language Overrides:

  1. Copy a template from Language Templates, or create your own template.
  2. Save the app.
  3. Test your changes.

Global Language Overrides

If you want to apply the overrides to all apps or add a new language, you can do this globally.

In the steps below, we assume you installed WAHA using the 🔧 Install & Update guide.

  1. Create the chatwoot-languages directory and download the language file you want to modify (or use as a template for a new language):
Download Language
mkdir chatwoot-languages
cd chatwoot-languages
wget https://raw.githubusercontent.com/devlikeapro/waha/refs/heads/core/src/apps/chatwoot/i18n/locales/en-US.yaml
  1. Decide what you want to do:
  • To add a new languagerename the file
  • To make only a few changes to the defaultkeep the file name
Rename File (only if adding a new language)
mv en-US.yaml company.yaml
  1. Edit the file:
  • If you are adding a new language, set a distinct locale.name and provide ALL keys
  • If you are overriding the default, change only the desired keys and remove the rest
Change Templates
vim en-US.yaml
  1. Update docker-compose.yaml to mount the folder with language overrides
docker-compose.yaml
services:
  waha:
    restart: always
    image: devlikeapro/waha-plus
    volumes:
      - './sessions:/app/.sessions'
      - './media:/app/.media'
      - './chatwoot-languages:/app/.chatwoot-languages'  # 👈 Attaches the folder
    environment:
      - WAHA_APPS_CHATWOOT_LANGUAGES_FOLDER=/app/.chatwoot-languages  # 👈 Tells WAHA to use it as an additional languages folder

Templates

Under the hood, WAHA uses the Mustache template engine.

Below are template examples and the objects available to Mustache templates.

chatwoot.to.whatsapp.message.text

Used for any text message you send from Chatwoot to WhatsApp to render the content:

chatwoot.to.whatsapp.message.text
chatwoot.to.whatsapp.message.text: |
    {{{ content }}}
  • content - text from Chatwoot converted to WhatsApp Markdown
content
"Here's the *bold* text"
chatwoot
{
  "event": "message_created",
  "message_type": "outgoing",
  "content_type": "text",
  "content": "Hi there!",
  "sender": {
    "id": 1,
    "name": "Agent Smith",
    "email": "smith@example.com",
    "type": "user"
  },
  "account": {
    "id": 1,
    "name": "admin@example.com"
  },
  "additional_attributes": {},
  "content_attributes": {},
  "conversation": {
    "additional_attributes": {},
    "can_reply": true,
    "channel": "Channel::Api",
    "contact_inbox": {
      "id": 5,
      "contact_id": 5,
      "inbox_id": 1,
      "source_id": "83b72ca2-9bfe-4fd5-AAAA-83e9c81938f1",
      "created_at": "2025-08-10T06:10:40.755Z",
      "updated_at": "2025-08-10T06:10:40.755Z",
      "hmac_verified": false,
      "pubsub_token": "AAAAA"
    },
    "id": 5,
    "inbox_id": 1,
    "messages": [
      {
        "id": 222,
        "content": "Hi there!",
        "account_id": 1,
        "inbox_id": 1,
        "conversation_id": 5,
        "message_type": 1,
        "created_at": 1757111864,
        "updated_at": "2025-08-10T10:41:04.660Z",
        "private": false,
        "status": "sent",
        "source_id": null,
        "content_type": "text",
        "content_attributes": {},
        "sender_type": "User",
        "sender_id": 1,
        "external_source_ids": {},
        "additional_attributes": {},
        "processed_message_content": "t",
        "sentiment": {},
        "conversation": {
          "assignee_id": 1,
          "unread_count": 0,
          "last_activity_at": 1757111864,
          "contact_inbox": {
            "source_id": "83b72ca2-aaaa-4fd5-9118-83e9c81938f1"
          }
        },
        "sender": {
          "id": 1,
          "name": "Agent",
          "available_name": "Agent",
          "avatar_url": "",
          "type": "user",
          "availability_status": null,
          "thumbnail": ""
        }
      }
    ],
    "labels": [],
    "meta": {
      "sender": {
        "additional_attributes": {},
        "custom_attributes": {
          "waha_whatsapp_jid": "11111111111@c.us",
          "waha_whatsapp_lid": "9999999999999@lid",
          "waha_whatsapp_chat_id": "11111111111@c.us"
        },
        "email": null,
        "id": 5,
        "identifier": null,
        "name": "Agent",
        "phone_number": "+11111111111",
        "thumbnail": "http://chatwoot:3009/rails/....",
        "blocked": false,
        "type": "contact"
      },
      "assignee": {
        "id": 1,
        "name": "Agent",
        "available_name": "Agent",
        "avatar_url": "",
        "type": "user",
        "availability_status": null,
        "thumbnail": ""
      },
      "team": null,
      "hmac_verified": false
    },
    "status": "open",
    "custom_attributes": {},
    "snoozed_until": null,
    "unread_count": 0,
    "first_reply_created_at": "2025-08-10T06:40:44.107Z",
    "priority": null,
    "waiting_since": 0,
    "agent_last_seen_at": 1757111864,
    "contact_last_seen_at": 0,
    "last_activity_at": 1757111864,
    "timestamp": 1757111864,
    "created_at": 1757111864,
    "updated_at": 1757111864.676595
  },
  "created_at": "2025-08-10T10:41:04.660Z",
  "id": 222,
  "inbox": {
    "id": 1,
    "name": "Agent"
  },
  "private": false,
  "source_id": null
}
chatwoot.to.whatsapp.message.media.caption

Used for any media message you send from Chatwoot to WhatsApp to render the caption:

chatwoot.to.whatsapp.message.media.caption
chatwoot.to.whatsapp.message.media.caption: |
  {{#singleAttachment}}
  {{#content}}
  {{{ content }}}
  {{/content}}
  {{/singleAttachment}}
  • content: string - same as for chatwoot.to.whatsapp.message.text
  • chatwoot: object - same as for chatwoot.to.whatsapp.message.text
  • singleAttachment: boolean - flag indicating whether the Chatwoot message has a single attachment

Language Templates

Below you will find commonly used override patterns.

Add Agent Name

Prefix all Chatwoot to WhatsApp messages with the agent name.

Agent Name
chatwoot.to.whatsapp.message.text: |-
  *{{{chatwoot.sender.name}}}*:
  {{{ content }}}

chatwoot.to.whatsapp.message.media.caption: |-
  {{#singleAttachment}}
  {{#content}}
  *{{{chatwoot.sender.name}}}*:
  {{{ content }}}
  {{/content}}
  {{/singleAttachment}}

Chatwoot Integration Contact

Customize how the default WhatsApp Integration contact appears in Chatwoot.

Chatwoot Integration Contact

It Does Not Update Existing Contacts

If you already have WAHA connected to any Chatwoot inbox, this template does not retroactively update that contact. It applies only when creating the first WAHA inbox in your Chatwoot account.

However, you can update the contact name and avatar directly in Chatwoot — WAHA searches for the contact by the WhatsApp Chat ID attribute, which should be whatsapp.integration.

app.inbox.contact.name: |-
  WhatsApp Integration (Brand)

app.inbox.contact.avatar.url: |-
  https://upload.wikimedia.org/wikipedia/commons/5/5e/WhatsApp_icon.png

Group Participant At The Bottom

You can move the “participant” part to the bottom in group chat messages

Participant At The Bottom
whatsapp.group.message: |-
  {{{text}}}
  
  👥 *{{{participant}}}*

How it works

Apps connect WhatsApp with external services using Redis as a message broker.

Apps architecture consists of:

  • HTTP API, Worker, and Session on WAHA side
  • Redis acts as the central message broker.
  • External Services, like ChatWoot
diagram

WhatsApp to External Service Flow:

  • When a new message arrives in WhatsApp, WAHA captures it and publishes a message event to Redis.
  • The Worker then picks up this event, processes it, and forwards the message to ChatWoot via its API.
  • After successful delivery, the job is marked as processed in Redis.
diagram

External Service to WhatsApp Flow:

  • When a new message is created in ChatWoot, it calls the WAHA API webhook.
  • The API saves this job to the Redis queue, from which the Worker retrieves it.
  • The Worker then requests the WAHA API to send the message to WhatsApp.
  • After WhatsApp confirms delivery, the API acknowledges the Worker, which then marks the job as processed in Redis.
diagram

Error Handling

In case of any errors, WAHA retries a few times and then gives detailed information about the error:

Error Reports and Retries

You can use the WAHA Jobs Dashboard at http://localhost:3000/jobs for monitoring:

WAHA Jobs Dashboard