Caution
DISCLAIMER: NOT PRODUCTION-READY
This project uses an unofficial, reverse-engineered WhatsApp Web socket client (@whiskeysockets/baileys).
- High Risk of Phone Number Ban: WhatsApp strictly prohibits the use of unofficial clients and automated bots. Linking your number carries a severe risk of permanent suspension/ban by WhatsApp.
- Educational & Testing Use Only: This software is provided "as is" without warranty. It is designed for educational purposes, personal study, prototyping, and local testing. It is not recommended or suitable for production environments. Use at your own risk.
WhatsApp OTP is a free, open-source, self-hostable authentication system that delivers 6-digit OTP codes directly to a user's WhatsApp โ powered by a spare phone number you already own.
By merging the web UI frontend, database storage endpoints, and WhatsApp socket listener into a single, self-contained Node.js Express application, you can run the entire service under a single process and port. No Vercel configurations or complex setups required.
| Feature | Detail |
|---|---|
| ๐ฌ WhatsApp Delivery | Sends OTPs via your own WhatsApp number using Baileys |
| ๐ฅ๏ธ Live Bot Profile | Shows the dispatcher's profile picture & name in the UI |
| ๐ Country Selector | Dropdown with flag emojis + per-country number validation |
| ๐ Redis or Local Cache | Upstash Redis in production; auto-falls back to local file for dev |
| ๐ฑ Mobile-First | Fully responsive โ works great on phone and desktop |
| ๐จ Premium Dark UI | Monochrome black-and-white theme, smooth micro-animations |
| ๐ Anti-Spam Rotation | Randomised OTP message templates to avoid WhatsApp detection |
| โฑ๏ธ 5-Minute TTL | OTP expires automatically; resend cooldown of 30 seconds |
| ๐ One-Command Run | npm run dev starts the entire app (UI + APIs + WhatsApp socket) |
- Prerequisites
- Project Structure
- Configuration
- Running Locally
- Deploying to Production
- Integrating into Your Own Website
- API Reference
- Environment Variables Reference
- Supported Countries
- Security Notes
- Contributing
- FAQ
- License
Before you begin, make sure you have:
- Node.js 18+ โ Download here
- npm (comes with Node.js)
- A spare WhatsApp-registered phone number โ this will be the OTP sender. Do not use your primary number.
- (For production) A free Upstash account (for Redis storage).
whatsapp-otp/
โโโ server.js # Express app hosting all APIs and WhatsApp socket
โโโ index.html # Main authentication UI (served by server.js)
โโโ style.css # Custom styling (monochrome dark theme)
โโโ script.js # Client validation, polling, and verification
โโโ package.json # Scripts and dependencies
โโโ .env.example # Template for configuration settings
โโโ .env # Local configuration (created by you, gitignored)
โโโ CONTRIBUTING.md # Contribution guidelines
โโโ LICENSE # MIT License
โโโ README.md # Project documentation
This project requires only a single .env configuration file at the root level.
Important:
.envfiles are never committed to git (they are listed in.gitignore). You create them yourself by copying the provided.env.exampletemplate.
git clone https://github.com/your-username/whatsapp-otp.git
cd whatsapp-otp
# Install dependencies
npm installCreate the .env file by copying the example:
# On Linux / macOS
cp .env.example .env
# On Windows (PowerShell)
Copy-Item .env.example .envNow open .env and set your values. Here is the template (same as .env.example):
# The port the unified Express server will listen on (defaults to 3000)
PORT=3000
# ==============================================================================
# UPSTASH REDIS CONFIGURATION (OPTIONAL for local development)
# ==============================================================================
# For LOCAL development:
# Leave these commented out. The application will automatically fall back to
# a local JSON cache file (.otp-cache.json) to store pending OTPs.
#
# For PRODUCTION deployments (Render, Railway, Fly.io):
# Production servers are stateless. You must configure a cloud Redis cache
# to persist OTP codes securely. Get these from https://console.upstash.com
#
# UPSTASH_REDIS_REST_URL="https://your-db-name.upstash.io"
# UPSTASH_REDIS_REST_TOKEN="your_upstash_token_here"To start the unified server, run:
npm run devThis boots the Express server on http://localhost:3000.
First-time setup โ Link your WhatsApp number:
- Open
http://localhost:3000in your browser. - A QR code overlay will appear on the screen.
- On your phone: Open WhatsApp โ Settings โ Linked Devices โ Link a Device.
- Scan the QR code displayed on your screen or printed in your terminal.
- Once scanned, the overlay disappears and your profile picture/name will load into the status bar.
๐ The session is saved in
auth_info_baileys/at the root level. On subsequent starts, the server reconnects automatically without requiring you to scan again.
Because this project runs as a standard Node.js Express server on a single port, you can deploy it in one click to any persistent node host:
- Link your GitHub repository to Railway.
- Create a new service from the repository.
- Set your environment variables in the settings tab (
UPSTASH_REDIS_REST_URL,UPSTASH_REDIS_REST_TOKEN). - Important: Add a Persistent Volume mounted at
/app/auth_info_baileysto keep your WhatsApp connection active between builds/restarts.
- Create a new Web Service on Render and connect your Git repository.
- Choose the
Noderuntime and set the start command tonpm start. - Add your environment variables in the dashboard.
- Important: Add a Persistent Disk mounted at
/opt/render/project/src/auth_info_baileys(or equivalent path) to persist the WhatsApp pairing state.
Want to add WhatsApp OTP verification to your existing site? You can call the API endpoints directly from any frontend or backend.
Your site's form โ POST /api/send-otp โ WhatsApp message sent to user
User enters OTP โ POST /api/verify-otp โ โ
Verified / โ Rejected
Make a POST request to /api/send-otp:
const response = await fetch('https://your-deployed-app.com/api/send-otp', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'John Doe', // User's name (used in the WhatsApp message greeting)
mobile: '+919876543210' // Full international number with country code
})
});
const data = await response.json();
// { success: true, message: "OTP dispatched via WhatsApp successfully." }Request body:
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | โ | User's display name (shown in the OTP message) |
mobile |
string | โ | Phone number in international format, e.g. +919876543210 |
Success response (200):
{
"success": true,
"message": "OTP dispatched via WhatsApp successfully.",
"mobile": "+919876543210"
}Once the user enters the code they received, verify it:
const response = await fetch('https://your-deployed-app.com/api/verify-otp', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
mobile: '+919876543210', // Same number used in send-otp
otp: '482931' // The 6-digit code the user entered
})
});
const data = await response.json();
if (data.success) {
// โ
OTP is correct โ log the user in, redirect, etc.
} else {
// โ Wrong OTP or expired โ show error
console.error(data.message);
}Request body:
| Field | Type | Required | Description |
|---|---|---|---|
mobile |
string | โ | Same number used in the send step |
otp |
string | โ | The 6-digit code entered by the user |
Success response (200):
{
"success": true,
"message": "OTP verified successfully."
}| Endpoint | Method | Description |
|---|---|---|
POST /api/send-otp |
POST | Generate and send an OTP via WhatsApp |
POST /api/verify-otp |
POST | Verify a submitted OTP code |
GET /api/profile-pic |
GET | Get a WhatsApp user's profile picture URL (uses query param ?mobile=NUMBER) |
GET /api/bot-status |
GET | Check WhatsApp bot connection status + QR |
| Variable | Description | Example / Format |
|---|---|---|
PORT |
Local port for Express (defaults to 3000) | 3000 |
UPSTASH_REDIS_REST_URL |
Upstash Redis REST database URL | https://your-db-name.upstash.io |
UPSTASH_REDIS_REST_TOKEN |
Upstash Redis REST token | your_upstash_token_here |
The built-in UI supports validation for:
๐ฎ๐ณ India ยท ๐บ๐ธ United States ยท ๐ฌ๐ง United Kingdom ยท ๐ฆ๐ช UAE ยท ๐ธ๐ฆ Saudi Arabia ยท ๐ถ๐ฆ Qatar ยท ๐จ๐ฆ Canada ยท ๐ฆ๐บ Australia ยท ๐ฉ๐ช Germany
To add/edit countries, modify the COUNTRIES array in script.js.
- OTP auto-deletes โ After successful verification, the OTP is deleted from the cache immediately (prevents replay attacks).
- 5-minute TTL โ OTPs expire automatically even if never verified.
- Session files โ
auth_info_baileys/contains your WhatsApp session tokens. These are in.gitignoreand must never be committed. .envfiles are gitignored โ Only.env.exampleis committed. Never commit.env.
Q: Is this against WhatsApp's Terms of Service, and is it production-ready?
A: Yes, it violates WhatsApp's Terms of Service and carries a severe risk of your number being permanently banned. Because of this, this project is NOT production-ready. It is intended strictly for educational, hobby, and testing purposes. For production authentication setups, you should use official WhatsApp Business API platforms (such as Twilio).
Q: My profile picture isn't showing.
A: WhatsApp privacy settings may block picture access for non-contacts. The UI gracefully shows your name initial instead.
Q: Can the sender and receiver be the same number?
A: Yes โ useful for testing. The bot can send a code to itself.
MIT โ free to use, modify, and distribute.