If you want to automate WhatsApp messaging, send customer replies, receive messages, or connect WhatsApp with your form submissions and automation tools like n8n or Zapier — you can do it using WhatsApp Web JS.
In this guide, we’ll set up a full self-hosted WhatsApp automation system on a VPS using Node.js, Docker, and Express. We’ll also integrate it with a sample PHP form to send messages automatically.
🧱 Step 1: Prepare Your VPS
You’ll need a VPS (Ubuntu 20.04 or later) with Docker and Docker Compose installed. We’ll create a project folder where our WhatsApp Web JS app will live.
mkdir -p /www/projects/whatsapp_webjs/session
cd /www/projects/whatsapp_webjs
Here, mkdir means “make directory”.
The session folder stores your WhatsApp login session so that you don’t need to scan the QR every time.
Folder structure should look like this:
/www/projects/whatsapp_webjs/
├─ Dockerfile
├─ package.json
├─ index.js
├─ docker-compose.yml (optional)
└─ session/
📦 Step 2: Create Dockerfile
Create a new file called Dockerfile inside /www/projects/whatsapp_webjs and paste this:
# Use Node 18 base image
FROM node:18
# Install Chromium dependencies required by puppeteer
RUN apt-get update && apt-get install -y \
wget ca-certificates fonts-liberation libasound2 libatk1.0-0 libatk-bridge2.0-0 \
libcups2 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxrandr2 libgbm1 \
libxshmfence1 libnss3 libxss1 libgtk-3-0 libpangocairo-1.0-0 libpango-1.0-0 \
libasound2t64 && rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Copy dependency files and install npm packages
COPY package*.json ./
RUN npm install
# Copy the app source code
COPY . .
# Persist WhatsApp session
VOLUME ["/app/session"]
# Expose API port
EXPOSE 3011
# Start the app
CMD ["node", "index.js"]
📘 Step 3: Create package.json
This file defines project dependencies and metadata.
{
"name": "whatsapp_webjs_api",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"express": "^4.18.2",
"whatsapp-web.js": "^1.23.0",
"qrcode-terminal": "^0.12.0",
"puppeteer": "^21.3.7"
}
}
⚙️ Step 4: Create index.js (WhatsApp API Server)
This is the main backend file that starts the WhatsApp Web JS client and handles incoming API requests.
const express = require('express');
const { Client, LocalAuth, MessageMedia } = require('whatsapp-web.js');
const qrcode = require('qrcode-terminal');
const app = express();
app.use(express.json());
// Root endpoint for testing
app.get('/', (req, res) => res.send('✅ WhatsApp Web JS API is running successfully!'));
// Initialize WhatsApp client
const client = new Client({
authStrategy: new LocalAuth({ dataPath: '/app/session' }),
puppeteer: { headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'] }
});
client.on('qr', qr => {
console.log('📱 Scan this QR code with WhatsApp:');
qrcode.generate(qr, { small: true });
});
client.on('ready', () => console.log('✅ WhatsApp Web JS client is ready!'));
client.on('disconnected', reason => console.log('⚠️ WhatsApp disconnected:', reason));
client.initialize();
// Send text message endpoint
app.post('/sendMessage', async (req, res) => {
const { to, message } = req.body;
try {
await client.sendMessage(to, message);
res.json({ success: true, message: 'Message sent successfully!' });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Send media (image, video, document, etc.)
app.post('/sendMedia', async (req, res) => {
const { to, mediaUrl, caption } = req.body;
try {
const media = await MessageMedia.fromUrl(mediaUrl);
await client.sendMessage(to, media, { caption });
res.json({ success: true, message: 'Media sent successfully!' });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Receive incoming messages
client.on('message', async msg => {
console.log(`📩 Message received from ${msg.from}: ${msg.body}`);
// You can automate replies here, e.g.:
// if (msg.body.toLowerCase() === 'hi') {
// msg.reply('Hello! How can I help you today?');
// }
});
const PORT = process.env.PORT || 3011;
app.listen(PORT, () => console.log(`🚀 WhatsApp API running on port ${PORT}`));
🐳 Step 5: Build and Run Docker Container
cd /www/projects/whatsapp_webjs
docker build --no-cache -t whatsapp_webjs_api .
docker run -d --name whatsapp_webjs_api \
-p 3011:3011 \
-v /www/projects/whatsapp_webjs/session:/app/session \
--restart unless-stopped whatsapp_webjs_api
After container starts, view logs to see the QR code:
docker logs -f whatsapp_webjs_api
Scan that QR code from your phone (WhatsApp → Linked Devices → Link a Device).
🌐 Step 6: Configure Apache Reverse Proxy (optional)
If you want to access your API using a custom domain (like https://whatsapp.example.com), add this configuration to your Apache virtual host file:
ServerName whatsapp.example.com
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
ServerName whatsapp.example.com
SSLEngine On
SSLCertificateFile /path/to/fullchain.pem
SSLCertificateKeyFile /path/to/privkey.pem
ProxyPreserveHost On
ProxyRequests Off
ProxyPass / http://127.0.0.1:3011/
ProxyPassReverse / http://127.0.0.1:3011/
Reload Apache to apply changes:
service apache2 reload
🧪 Step 7: Test API
To send a test WhatsApp message, use this curl command:
curl -X POST https://whatsapp.example.com/sendMessage \
-H "Content-Type: application/json" \
-d '{"to":"919876543210@c.us","message":"Hello! This is a test message from my WhatsApp Web JS API."}'
You should instantly receive the message on the target phone.
💬 Step 8: Integrate with PHP Form
Here’s an example PHP snippet to send an automatic WhatsApp confirmation message after a customer submits a form:
"91" . preg_replace('/\D/', '', $mobile) . "@c.us",
"message" => $message
]);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://whatsapp.example.com/sendMessage');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
} else {
echo json_encode(["error" => "Mobile number is missing"]);
}
?>
🔄 Step 9: Enable Auto Restart
To make sure the WhatsApp container restarts automatically after server reboot:
docker update --restart unless-stopped whatsapp_webjs_api
📦 Step 10: Optional Docker Compose Setup
If you prefer managing services via Docker Compose, create a file named docker-compose.yml:
version: '3'
services:
whatsapp_webjs_api:
build: .
container_name: whatsapp_webjs_api
ports:
- "3011:3011"
volumes:
- ./session:/app/session
restart: unless-stopped
Then simply run:
docker compose up -d
✅ Final Result
Your WhatsApp Web JS API is now live and self-hosted. You can send and receive messages, automate responses, send images and documents, and integrate with automation tools or websites.
The best part — your WhatsApp login stays active even after reboot since your session data is stored in the session directory.
💡 Pro Tip:
If you ever want to change WhatsApp number, just delete the session folder and restart the container — a new QR code will appear for login.
🎯 Summary
- ✅ Works entirely on your VPS — no third-party service needed.
- ✅ Easy to connect with PHP, Python, or Node backends.
- ✅ Can be scaled for multiple WhatsApp numbers by duplicating containers.
- ✅ Compatible with automation tools like n8n and Make.com.
Now you have a fully working, private WhatsApp Web JS automation setup — ready for any project.