canove/whaticket-community

A very simple Ticket System based on WhatsApp messages, that allow multi-users in same WhatsApp account.

JavaScript

2,076

760 commits

updated Oct 13, 2026

See the code

README

Whaticket

Whaticket Open Source

The open-source WhatsApp ticketing system that Whaticket grew out of.
Several agents on one WhatsApp number. Every conversation becomes a ticket.

License: MIT Stars Discord Whaticket

English · Português · Español

What is this · Open Source vs Whaticket · Screenshots · Quick start · Configuration · Project status


What is this

In 2020 this repository was a weekend experiment: turn WhatsApp messages into support tickets so a whole team could answer from a single number. It became one of the most forked WhatsApp helpdesk projects on GitHub, and it grew into Whaticket, the commercial platform that today serves 4,500+ companies across Latin America.

This repo is Whaticket Open Source: the original codebase, under an MIT license. You can self-host it, fork it and build on top of it. The code the company started from stays public.

Backend: Node.js + TypeScript + Express + Sequelize. It talks to WhatsApp through a pluggable provider layer (whatsapp-web.js or zapo-js) and stores everything in MySQL/MariaDB.

Frontend: a React + Material UI chat app built with Vite. It communicates with the backend over REST and WebSockets, in English, Portuguese and Spanish.

[!NOTE] Prefer not to run servers? Whaticket is the managed product built by the same team. It works with the official WhatsApp API and brings Instagram, Facebook, TikTok, Telegram and web chat into one inbox, with AI chatbots, campaigns and 24/7 support. 14-day free trial, no credit card.

Open Source vs Whaticket

Both are built by the same team. The difference is how much infrastructure you want to run.

Whaticket Open SourceWhaticket
License / priceMIT, free foreverFrom US$49/month for 3 users
Hosting, updates, backupsYou run itManaged for you
WhatsApp via QR code✅✅
Official WhatsApp Cloud API❌✅
Multiple agents on one number✅✅
Multiple WhatsApp connections✅✅
Queues / departments✅✅
Quick replies✅✅
Media messages (image/audio/video/files)✅✅
Instagram, Facebook, TikTok, Telegram, web chat❌✅
Rule-based and AI chatbots (Wäbot)❌✅
Bulk campaigns❌✅
Analytics, CSAT and performance reportsBasic dashboard✅
Integrations (Zapier, Shopify, Slack, Google…)❌✅
Mobile apps (iOS / Android)❌✅
SupportCommunity, on Discord and issues24/7 over WhatsApp

The commercial plan starts at 3 users, which comes to about US$16 per user per month.

Screenshots

Ticket queues

Ticket conversationChat with contact
Ticket conversationAgent chat view
Multiple WhatsApp connectionsContacts
Multiple WhatsApp connectionsContact management

Features

  • 💬 Shared inbox: several agents answering from the same WhatsApp number
  • 📱 Multiple connections: link more than one WhatsApp account, receive it all in one place
  • 🎫 Ticket lifecycle: pending → open → resolved, with assignment per agent
  • 🏷️ Queues: route incoming conversations to the right department
  • ⚡ Quick replies: canned answers for the questions you get every day
  • 🖼️ Media: send and receive images, audio, video and documents
  • 👥 Contacts: start conversations with new contacts without touching the phone
  • 📊 Dashboard: ticket and agent activity at a glance
  • 🔌 Pluggable WhatsApp provider: whatsapp-web.js (Puppeteer) or zapo (WebSocket)
  • 🌍 i18n: English, Portuguese and Spanish out of the box

How tickets work

Every new message on a connected WhatsApp number creates a Ticket. Tickets land in a queue on the Tickets page, where an agent accepts one, answers it, and eventually resolves it.

Follow-up messages from the same contact attach to the first open/pending ticket found. If a contact writes again within 2 hours and has no pending or open ticket, the most recent closed ticket is reopened instead of a new one being created.

Requirements

  • Node.js 14+ for the wwebjs provider, Node.js 22+ for the zapo provider (the backend CI and Dockerfile build on Node 22)
  • MySQL 5.7+ or MariaDB 10.6+
  • Docker (optional, but the fastest way to get a database up)
  • A Linux server if you are deploying to production. Ubuntu 20.04+ is what these instructions assume.

[!WARNING] The QR-code providers used here are unofficial WhatsApp clients. WhatsApp does not allow bots or unofficial clients on their platform, and numbers connected this way can be blocked. The supported alternative is the official WhatsApp Cloud API, which Whaticket provides.

Quick start with Docker

git clone https://github.com/canove/whaticket.git
cd whaticket
cp .env.example .env

Edit .env. At minimum, set MYSQL_ROOT_PASSWORD, JWT_SECRET and JWT_REFRESH_SECRET:

# MYSQL
MYSQL_ENGINE=mariadb
MYSQL_VERSION=10.6
MYSQL_ROOT_PASSWORD=change-me
MYSQL_DATABASE=whaticket
MYSQL_PORT=3306
TZ=America/Fortaleza

# BACKEND
BACKEND_PORT=8080
BACKEND_SERVER_NAME=api.mydomain.com
BACKEND_URL=https://api.mydomain.com
PROXY_PORT=443
JWT_SECRET=change-me
JWT_REFRESH_SECRET=change-me-too

# FRONTEND
FRONTEND_PORT=80
FRONTEND_SSL_PORT=443
FRONTEND_SERVER_NAME=myapp.mydomain.com
FRONTEND_URL=https://myapp.mydomain.com

Bring everything up:

docker-compose up -d --build

On the first run only, seed the database:

docker-compose exec backend npx sequelize db:seed:all

Open the frontend, log in with the seeded account, go to Connections, create your first WhatsApp connection and scan the QR code. Every message that number receives now shows up in the ticket list.

Default credentials: [email protected] / admin. Change them immediately.

Optional Docker services

phpMyAdmin, to inspect the database (port 9000 by default, configurable with PMA_PORT):

docker-compose -f docker-compose.phpmyadmin.yaml up -d

Browserless, to run Chrome outside the backend container (set MAX_CONCURRENT_SESSIONS):

docker-compose -f docker-compose.browserless.yaml up -d
SSL certificates for the Docker setup

Drop your certificates into ssl/certs, with one folder per service:

.
├── certs
│   ├── backend
│   │   ├── fullchain.pem
│   │   └── privkey.pem
│   └── frontend
│       ├── fullchain.pem
│       └── privkey.pem
└── www

The frontend nginx container is already set up to answer certbot's webroot challenge:

certbot certonly --cert-name backend  --webroot --webroot-path ./ssl/www/ -d api.mydomain.com
certbot certonly --cert-name frontend --webroot --webroot-path ./ssl/www/ -d myapp.mydomain.com

Running locally for development

Step-by-step development setup

1. Start a database

docker run --name whaticketdb \
  -e MYSQL_ROOT_PASSWORD=strongpassword \
  -e MYSQL_DATABASE=whaticket \
  -e MYSQL_USER=whaticket \
  -e MYSQL_PASSWORD=whaticket \
  --restart always -p 3306:3306 -d mariadb:10.6 \
  --character-set-server=utf8mb4 --collation-server=utf8mb4_bin

2. Install Puppeteer's system dependencies (only needed for the wwebjs provider):

sudo apt-get install -y libxshmfence-dev libgbm-dev wget unzip fontconfig locales gconf-service \
  libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 \
  libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 \
  libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 \
  libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates \
  fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils

3. Backend

cd backend
cp .env.example .env   # then fill it in, see Configuration below
npm install
npm run build
npx sequelize db:migrate
npx sequelize db:seed:all
npm run dev            # or: npm start

4. Frontend, in a second terminal:

cd frontend
cp .env.example .env
npm install
npm run dev

frontend/.env only needs to point at the backend:

VITE_BACKEND_URL = http://localhost:8080/

5. Open http://localhost:3000/signup, create a user, then go to Connections and scan the QR code.

Production deployment on a VPS

Ubuntu 20.04 + pm2 + nginx + Let's Encrypt

These steps assume you are not running as root, since Puppeteer refuses to start as root. Point two subdomains at your server first; this walkthrough uses myapp.mydomain.com for the frontend and api.mydomain.com for the backend.

Create a deploy user

adduser deploy
usermod -aG sudo deploy
su deploy

Install Node and Docker

sudo apt update && sudo apt upgrade -y

curl -fsSL https://deb.nodesource.com/setup_14.x | sudo -E bash -
sudo apt-get install -y nodejs

sudo apt install -y apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
sudo apt update && sudo apt install -y docker-ce
sudo usermod -aG docker ${USER}
su - ${USER}

Database, code and backend

docker run --name whaticketdb \
  -e MYSQL_ROOT_PASSWORD=strongpassword -e MYSQL_DATABASE=whaticket \
  -e MYSQL_USER=whaticket -e MYSQL_PASSWORD=whaticket \
  --restart always -p 3306:3306 -d mariadb:10.6 \
  --character-set-server=utf8mb4 --collation-server=utf8mb4_bin

cd ~ && git clone https://github.com/canove/whaticket.git whaticket
cp whaticket/backend/.env.example whaticket/backend/.env
nano whaticket/backend/.env    # use https:// URLs and PROXY_PORT=443

cd whaticket/backend
npm install
npm run build
npx sequelize db:migrate
npx sequelize db:seed:all

Confirm it boots with npm start (you should see Server started on port...), stop it with CTRL + C, then hand it to pm2:

sudo npm install -g pm2
pm2 start dist/server.js --name whaticket-backend
pm2 startup ubuntu -u $USER     # run the command it prints back

Frontend

cd ../frontend
npm install
echo "VITE_BACKEND_URL = https://api.mydomain.com/" > .env
npm run build
pm2 start server.js --name whaticket-frontend
pm2 save

nginx

sudo apt install -y nginx
sudo rm /etc/nginx/sites-enabled/default
sudo nano /etc/nginx/sites-available/whaticket-frontend
server {
  server_name myapp.mydomain.com;

  location / {
    proxy_pass http://127.0.0.1:3333;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_cache_bypass $http_upgrade;
  }
}

Copy it for the backend, changing server_name to api.mydomain.com and proxy_pass to http://127.0.0.1:8080, then enable both:

sudo cp /etc/nginx/sites-available/whaticket-frontend /etc/nginx/sites-available/whaticket-backend
sudo nano /etc/nginx/sites-available/whaticket-backend
sudo ln -s /etc/nginx/sites-available/whaticket-frontend /etc/nginx/sites-enabled
sudo ln -s /etc/nginx/sites-available/whaticket-backend  /etc/nginx/sites-enabled

nginx caps request bodies at 1MB, which is too small for media uploads. In /etc/nginx/nginx.conf, inside the http { } block:

client_max_body_size 20M;
sudo nginx -t && sudo service nginx restart

HTTPS, required for notifications and audio messages:

sudo snap install --classic certbot
sudo certbot --nginx

Configuration

Backend (backend/.env)

VariableDescriptionDefault
WHATSAPP_PROVIDERWhatsApp driver: wwebjs or zapowwebjs
NODE_ENVDEVELOPMENT gives you verbose debugging
PORTPort the backend listens on8080
PROXY_PORTPublic port behind your reverse proxy (443 in production)8080
BACKEND_URLPublic backend URLhttp://localhost:8080
FRONTEND_URLPublic frontend URL. CORS depends on thishttp://localhost:3000
DB_HOST / DB_PORTDatabase host and portlocalhost / 3306
DB_DIALECTmysqlmysql
DB_NAME / DB_USER / DB_PASSDatabase credentials
JWT_SECRET / JWT_REFRESH_SECRETToken signing secrets, change these
CHROME_BIN / CHROME_WS / CHROME_ARGSPuppeteer/Chrome settings (wwebjs only)
LOG_LEVELsilent, fatal, error, warn, info, debug, traceinfo
ZAPO_LOG_LEVELLog level for the zapo providererror
ZAPO_AUTH_PATHSQLite file where the zapo provider keeps session state.zapo_auth/state.sqlite

Frontend (frontend/.env)

VariableDescription
VITE_BACKEND_URLBackend URL the app talks to
VITE_HOURS_CLOSE_TICKETS_AUTOHours of inactivity before tickets auto-close

Choosing a WhatsApp provider

wwebjszapo
How it connectsPuppeteer driving WhatsApp WebDirect WebSocket protocol
Memory footprintHeavy, one Chrome per sessionLight
System dependenciesChrome + many lib* packagesNode 22+ and SQLite (better-sqlite3)
Maturity in this repoDefault, in production for yearsNewer, in active development

Switch between them with WHATSAPP_PROVIDER in backend/.env.

The zapo provider uploads media without any extra dependency, but thumbnails, media dimensions and voice-note waveforms come from the optional @zapo-js/media-utils package (which needs ffmpeg/ffprobe and sharp). Install it and the provider picks it up on the next restart; without it media still sends, just without previews.

Updating an existing installation

Always diff .env.example against your .env before upgrading. New variables show up there first.

#!/bin/bash
echo "Updating Whaticket, please wait."

cd ~/whaticket
git pull

cd backend
npm install
rm -rf dist
npm run build
npx sequelize db:migrate
npx sequelize db:seed

cd ../frontend
npm install
rm -rf build
npm run build

pm2 restart all
echo "Update finished. Enjoy!"

Coming from the whaileys provider

The whaileys provider was replaced by zapo. Connections paired under whaileys carry over on their own, without scanning a new QR code:

  1. Move the server to Node.js 22+ (a better-sqlite3 requirement).
  2. Optionally, set WHATSAPP_PROVIDER=zapo in backend/.env. The old whaileys value keeps working and points to the same provider.
  3. Run the update script above. When the backend restarts, each connection's session is copied from the database into .zapo_auth/state.sqlite and the connection comes back without a new QR code.

The copy reads the session column of Whatsapps and the WppKeys table, so keep WppKeys until every connection is back online. Redis is no longer read and can be turned off. A connection whose session can't be copied shows a new QR code, like a fresh pairing.

Project status

Maintenance mode, with active development returning.

For the last few years this repository has received security and dependency patches while the team's effort went into the commercial platform. That is changing: we are planning a return to regular feature work here. Recent commits are the first steps: the frontend moved to Vite, and the WhatsApp integration moved to a pluggable provider layer with the new zapo-js driver.

What that means for you today:

  • Issues and pull requests are read. Reviews may be slow.
  • The codebase is stable and widely deployed, but expect rough edges and dated dependencies.
  • Breaking changes will be called out in release notes.

If you depend on this project, tell us on Discord what you need most. That is what shapes the priorities.

Contributing

Pull requests are welcome: bug fixes, dependency upgrades, documentation and translations. For anything large, open an issue first so we can agree on the approach.

  • 💬 Discord: questions and discussion
  • 🐛 Issues: bugs and feature requests
  • zapo-js: the WhatsApp WebSocket library used by the zapo provider
  • Whaticket: the managed commercial platform

License

MIT © Whaticket and contributors.

Disclaimer

This project is not affiliated with, associated with, authorized by, endorsed by, or in any way officially connected to WhatsApp or any of its subsidiaries or affiliates. The official WhatsApp website is https://whatsapp.com. "WhatsApp" and related names, marks, emblems and images are registered trademarks of their respective owners.

Using unofficial clients to connect to WhatsApp can get your number blocked. Run this software at your own risk, and review it against your own security requirements before exposing it to the internet.

bot
chat
javascript
tickets
whatsapp

canove/whaticket-community

A very simple Ticket System based on WhatsApp messages, that allow multi-users in same WhatsApp account.

JavaScript

2,076

760 commits

updated Oct 13, 2026

See the code

README

Whaticket

Whaticket Open Source

The open-source WhatsApp ticketing system that Whaticket grew out of.
Several agents on one WhatsApp number. Every conversation becomes a ticket.

License: MIT Stars Discord Whaticket

English · Português · Español

What is this · Open Source vs Whaticket · Screenshots · Quick start · Configuration · Project status


What is this

In 2020 this repository was a weekend experiment: turn WhatsApp messages into support tickets so a whole team could answer from a single number. It became one of the most forked WhatsApp helpdesk projects on GitHub, and it grew into Whaticket, the commercial platform that today serves 4,500+ companies across Latin America.

This repo is Whaticket Open Source: the original codebase, under an MIT license. You can self-host it, fork it and build on top of it. The code the company started from stays public.

Backend: Node.js + TypeScript + Express + Sequelize. It talks to WhatsApp through a pluggable provider layer (whatsapp-web.js or zapo-js) and stores everything in MySQL/MariaDB.

Frontend: a React + Material UI chat app built with Vite. It communicates with the backend over REST and WebSockets, in English, Portuguese and Spanish.

[!NOTE] Prefer not to run servers? Whaticket is the managed product built by the same team. It works with the official WhatsApp API and brings Instagram, Facebook, TikTok, Telegram and web chat into one inbox, with AI chatbots, campaigns and 24/7 support. 14-day free trial, no credit card.

Open Source vs Whaticket

Both are built by the same team. The difference is how much infrastructure you want to run.

Whaticket Open SourceWhaticket
License / priceMIT, free foreverFrom US$49/month for 3 users
Hosting, updates, backupsYou run itManaged for you
WhatsApp via QR code✅✅
Official WhatsApp Cloud API❌✅
Multiple agents on one number✅✅
Multiple WhatsApp connections✅✅
Queues / departments✅✅
Quick replies✅✅
Media messages (image/audio/video/files)✅✅
Instagram, Facebook, TikTok, Telegram, web chat❌✅
Rule-based and AI chatbots (Wäbot)❌✅
Bulk campaigns❌✅
Analytics, CSAT and performance reportsBasic dashboard✅
Integrations (Zapier, Shopify, Slack, Google…)❌✅
Mobile apps (iOS / Android)❌✅
SupportCommunity, on Discord and issues24/7 over WhatsApp

The commercial plan starts at 3 users, which comes to about US$16 per user per month.

Screenshots

Ticket queues

Ticket conversationChat with contact
Ticket conversationAgent chat view
Multiple WhatsApp connectionsContacts
Multiple WhatsApp connectionsContact management

Features

  • 💬 Shared inbox: several agents answering from the same WhatsApp number
  • 📱 Multiple connections: link more than one WhatsApp account, receive it all in one place
  • 🎫 Ticket lifecycle: pending → open → resolved, with assignment per agent
  • 🏷️ Queues: route incoming conversations to the right department
  • ⚡ Quick replies: canned answers for the questions you get every day
  • 🖼️ Media: send and receive images, audio, video and documents
  • 👥 Contacts: start conversations with new contacts without touching the phone
  • 📊 Dashboard: ticket and agent activity at a glance
  • 🔌 Pluggable WhatsApp provider: whatsapp-web.js (Puppeteer) or zapo (WebSocket)
  • 🌍 i18n: English, Portuguese and Spanish out of the box

How tickets work

Every new message on a connected WhatsApp number creates a Ticket. Tickets land in a queue on the Tickets page, where an agent accepts one, answers it, and eventually resolves it.

Follow-up messages from the same contact attach to the first open/pending ticket found. If a contact writes again within 2 hours and has no pending or open ticket, the most recent closed ticket is reopened instead of a new one being created.

Requirements

  • Node.js 14+ for the wwebjs provider, Node.js 22+ for the zapo provider (the backend CI and Dockerfile build on Node 22)
  • MySQL 5.7+ or MariaDB 10.6+
  • Docker (optional, but the fastest way to get a database up)
  • A Linux server if you are deploying to production. Ubuntu 20.04+ is what these instructions assume.

[!WARNING] The QR-code providers used here are unofficial WhatsApp clients. WhatsApp does not allow bots or unofficial clients on their platform, and numbers connected this way can be blocked. The supported alternative is the official WhatsApp Cloud API, which Whaticket provides.

Quick start with Docker

git clone https://github.com/canove/whaticket.git
cd whaticket
cp .env.example .env

Edit .env. At minimum, set MYSQL_ROOT_PASSWORD, JWT_SECRET and JWT_REFRESH_SECRET:

# MYSQL
MYSQL_ENGINE=mariadb
MYSQL_VERSION=10.6
MYSQL_ROOT_PASSWORD=change-me
MYSQL_DATABASE=whaticket
MYSQL_PORT=3306
TZ=America/Fortaleza

# BACKEND
BACKEND_PORT=8080
BACKEND_SERVER_NAME=api.mydomain.com
BACKEND_URL=https://api.mydomain.com
PROXY_PORT=443
JWT_SECRET=change-me
JWT_REFRESH_SECRET=change-me-too

# FRONTEND
FRONTEND_PORT=80
FRONTEND_SSL_PORT=443
FRONTEND_SERVER_NAME=myapp.mydomain.com
FRONTEND_URL=https://myapp.mydomain.com

Bring everything up:

docker-compose up -d --build

On the first run only, seed the database:

docker-compose exec backend npx sequelize db:seed:all

Open the frontend, log in with the seeded account, go to Connections, create your first WhatsApp connection and scan the QR code. Every message that number receives now shows up in the ticket list.

Default credentials: [email protected] / admin. Change them immediately.

Optional Docker services

phpMyAdmin, to inspect the database (port 9000 by default, configurable with PMA_PORT):

docker-compose -f docker-compose.phpmyadmin.yaml up -d

Browserless, to run Chrome outside the backend container (set MAX_CONCURRENT_SESSIONS):

docker-compose -f docker-compose.browserless.yaml up -d
SSL certificates for the Docker setup

Drop your certificates into ssl/certs, with one folder per service:

.
├── certs
│   ├── backend
│   │   ├── fullchain.pem
│   │   └── privkey.pem
│   └── frontend
│       ├── fullchain.pem
│       └── privkey.pem
└── www

The frontend nginx container is already set up to answer certbot's webroot challenge:

certbot certonly --cert-name backend  --webroot --webroot-path ./ssl/www/ -d api.mydomain.com
certbot certonly --cert-name frontend --webroot --webroot-path ./ssl/www/ -d myapp.mydomain.com

Running locally for development

Step-by-step development setup

1. Start a database

docker run --name whaticketdb \
  -e MYSQL_ROOT_PASSWORD=strongpassword \
  -e MYSQL_DATABASE=whaticket \
  -e MYSQL_USER=whaticket \
  -e MYSQL_PASSWORD=whaticket \
  --restart always -p 3306:3306 -d mariadb:10.6 \
  --character-set-server=utf8mb4 --collation-server=utf8mb4_bin

2. Install Puppeteer's system dependencies (only needed for the wwebjs provider):

sudo apt-get install -y libxshmfence-dev libgbm-dev wget unzip fontconfig locales gconf-service \
  libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 \
  libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 \
  libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 \
  libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates \
  fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils

3. Backend

cd backend
cp .env.example .env   # then fill it in, see Configuration below
npm install
npm run build
npx sequelize db:migrate
npx sequelize db:seed:all
npm run dev            # or: npm start

4. Frontend, in a second terminal:

cd frontend
cp .env.example .env
npm install
npm run dev

frontend/.env only needs to point at the backend:

VITE_BACKEND_URL = http://localhost:8080/

5. Open http://localhost:3000/signup, create a user, then go to Connections and scan the QR code.

Production deployment on a VPS

Ubuntu 20.04 + pm2 + nginx + Let's Encrypt

These steps assume you are not running as root, since Puppeteer refuses to start as root. Point two subdomains at your server first; this walkthrough uses myapp.mydomain.com for the frontend and api.mydomain.com for the backend.

Create a deploy user

adduser deploy
usermod -aG sudo deploy
su deploy

Install Node and Docker

sudo apt update && sudo apt upgrade -y

curl -fsSL https://deb.nodesource.com/setup_14.x | sudo -E bash -
sudo apt-get install -y nodejs

sudo apt install -y apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
sudo apt update && sudo apt install -y docker-ce
sudo usermod -aG docker ${USER}
su - ${USER}

Database, code and backend

docker run --name whaticketdb \
  -e MYSQL_ROOT_PASSWORD=strongpassword -e MYSQL_DATABASE=whaticket \
  -e MYSQL_USER=whaticket -e MYSQL_PASSWORD=whaticket \
  --restart always -p 3306:3306 -d mariadb:10.6 \
  --character-set-server=utf8mb4 --collation-server=utf8mb4_bin

cd ~ && git clone https://github.com/canove/whaticket.git whaticket
cp whaticket/backend/.env.example whaticket/backend/.env
nano whaticket/backend/.env    # use https:// URLs and PROXY_PORT=443

cd whaticket/backend
npm install
npm run build
npx sequelize db:migrate
npx sequelize db:seed:all

Confirm it boots with npm start (you should see Server started on port...), stop it with CTRL + C, then hand it to pm2:

sudo npm install -g pm2
pm2 start dist/server.js --name whaticket-backend
pm2 startup ubuntu -u $USER     # run the command it prints back

Frontend

cd ../frontend
npm install
echo "VITE_BACKEND_URL = https://api.mydomain.com/" > .env
npm run build
pm2 start server.js --name whaticket-frontend
pm2 save

nginx

sudo apt install -y nginx
sudo rm /etc/nginx/sites-enabled/default
sudo nano /etc/nginx/sites-available/whaticket-frontend
server {
  server_name myapp.mydomain.com;

  location / {
    proxy_pass http://127.0.0.1:3333;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_cache_bypass $http_upgrade;
  }
}

Copy it for the backend, changing server_name to api.mydomain.com and proxy_pass to http://127.0.0.1:8080, then enable both:

sudo cp /etc/nginx/sites-available/whaticket-frontend /etc/nginx/sites-available/whaticket-backend
sudo nano /etc/nginx/sites-available/whaticket-backend
sudo ln -s /etc/nginx/sites-available/whaticket-frontend /etc/nginx/sites-enabled
sudo ln -s /etc/nginx/sites-available/whaticket-backend  /etc/nginx/sites-enabled

nginx caps request bodies at 1MB, which is too small for media uploads. In /etc/nginx/nginx.conf, inside the http { } block:

client_max_body_size 20M;
sudo nginx -t && sudo service nginx restart

HTTPS, required for notifications and audio messages:

sudo snap install --classic certbot
sudo certbot --nginx

Configuration

Backend (backend/.env)

VariableDescriptionDefault
WHATSAPP_PROVIDERWhatsApp driver: wwebjs or zapowwebjs
NODE_ENVDEVELOPMENT gives you verbose debugging
PORTPort the backend listens on8080
PROXY_PORTPublic port behind your reverse proxy (443 in production)8080
BACKEND_URLPublic backend URLhttp://localhost:8080
FRONTEND_URLPublic frontend URL. CORS depends on thishttp://localhost:3000
DB_HOST / DB_PORTDatabase host and portlocalhost / 3306
DB_DIALECTmysqlmysql
DB_NAME / DB_USER / DB_PASSDatabase credentials
JWT_SECRET / JWT_REFRESH_SECRETToken signing secrets, change these
CHROME_BIN / CHROME_WS / CHROME_ARGSPuppeteer/Chrome settings (wwebjs only)
LOG_LEVELsilent, fatal, error, warn, info, debug, traceinfo
ZAPO_LOG_LEVELLog level for the zapo providererror
ZAPO_AUTH_PATHSQLite file where the zapo provider keeps session state.zapo_auth/state.sqlite

Frontend (frontend/.env)

VariableDescription
VITE_BACKEND_URLBackend URL the app talks to
VITE_HOURS_CLOSE_TICKETS_AUTOHours of inactivity before tickets auto-close

Choosing a WhatsApp provider

wwebjszapo
How it connectsPuppeteer driving WhatsApp WebDirect WebSocket protocol
Memory footprintHeavy, one Chrome per sessionLight
System dependenciesChrome + many lib* packagesNode 22+ and SQLite (better-sqlite3)
Maturity in this repoDefault, in production for yearsNewer, in active development

Switch between them with WHATSAPP_PROVIDER in backend/.env.

The zapo provider uploads media without any extra dependency, but thumbnails, media dimensions and voice-note waveforms come from the optional @zapo-js/media-utils package (which needs ffmpeg/ffprobe and sharp). Install it and the provider picks it up on the next restart; without it media still sends, just without previews.

Updating an existing installation

Always diff .env.example against your .env before upgrading. New variables show up there first.

#!/bin/bash
echo "Updating Whaticket, please wait."

cd ~/whaticket
git pull

cd backend
npm install
rm -rf dist
npm run build
npx sequelize db:migrate
npx sequelize db:seed

cd ../frontend
npm install
rm -rf build
npm run build

pm2 restart all
echo "Update finished. Enjoy!"

Coming from the whaileys provider

The whaileys provider was replaced by zapo. Connections paired under whaileys carry over on their own, without scanning a new QR code:

  1. Move the server to Node.js 22+ (a better-sqlite3 requirement).
  2. Optionally, set WHATSAPP_PROVIDER=zapo in backend/.env. The old whaileys value keeps working and points to the same provider.
  3. Run the update script above. When the backend restarts, each connection's session is copied from the database into .zapo_auth/state.sqlite and the connection comes back without a new QR code.

The copy reads the session column of Whatsapps and the WppKeys table, so keep WppKeys until every connection is back online. Redis is no longer read and can be turned off. A connection whose session can't be copied shows a new QR code, like a fresh pairing.

Project status

Maintenance mode, with active development returning.

For the last few years this repository has received security and dependency patches while the team's effort went into the commercial platform. That is changing: we are planning a return to regular feature work here. Recent commits are the first steps: the frontend moved to Vite, and the WhatsApp integration moved to a pluggable provider layer with the new zapo-js driver.

What that means for you today:

  • Issues and pull requests are read. Reviews may be slow.
  • The codebase is stable and widely deployed, but expect rough edges and dated dependencies.
  • Breaking changes will be called out in release notes.

If you depend on this project, tell us on Discord what you need most. That is what shapes the priorities.

Contributing

Pull requests are welcome: bug fixes, dependency upgrades, documentation and translations. For anything large, open an issue first so we can agree on the approach.

  • 💬 Discord: questions and discussion
  • 🐛 Issues: bugs and feature requests
  • zapo-js: the WhatsApp WebSocket library used by the zapo provider
  • Whaticket: the managed commercial platform

License

MIT © Whaticket and contributors.

Disclaimer

This project is not affiliated with, associated with, authorized by, endorsed by, or in any way officially connected to WhatsApp or any of its subsidiaries or affiliates. The official WhatsApp website is https://whatsapp.com. "WhatsApp" and related names, marks, emblems and images are registered trademarks of their respective owners.

Using unofficial clients to connect to WhatsApp can get your number blocked. Run this software at your own risk, and review it against your own security requirements before exposing it to the internet.

bot
chat
javascript
tickets
whatsapp

Significant stargazers

dyegoaurelio

14 followers · starred Oct 2020

webtecnica

10 followers · starred May 2022