# Someshwar AI WhatsApp Messenger

Someshwar AI WhatsApp Messenger is a multi-client WhatsApp Cloud API platform for managing customer conversations in a browser-based inbox. Each client has isolated users, leads, messages, WhatsApp credentials, settings, media, and optional Groq AI replies.

The application runs on Node.js with Express and Socket.IO, stores data in PostgreSQL, and integrates with Meta's WhatsApp Cloud API for inbound webhooks and outbound messages.

## Contents

- [What it does](#what-it-does)
- [Technology](#technology)
- [Prerequisites](#prerequisites)
- [Install and start](#install-and-start)
- [Environment variables](#environment-variables)
- [Database setup](#database-setup)
- [First-time configuration](#first-time-configuration)
- [WhatsApp webhook setup](#whatsapp-webhook-setup)
- [Roles and client isolation](#roles-and-client-isolation)
- [AI reply and handover workflow](#ai-reply-and-handover-workflow)
- [API reference](#api-reference)
- [Media handling](#media-handling)
- [Project structure](#project-structure)
- [Scripts](#scripts)
- [Production deployment](#production-deployment)
- [Troubleshooting](#troubleshooting)

## What it does

- Receives WhatsApp webhook events and identifies the correct client from the WhatsApp Phone Number ID.
- Creates leads and stores inbound and outbound conversations in PostgreSQL.
- Displays conversations in a live web inbox through Socket.IO events.
- Sends text messages and files (images, audio, video, and documents) through the WhatsApp Cloud API.
- Sends configured welcome messages, an optional brochure, and a custom message to a new lead.
- Supports super-admin, client-admin, and agent accounts.
- Lets super admins create, update, suspend, and manage client accounts.
- Lets client teams configure their WhatsApp account, messages, brochure, staff, and optional Groq AI settings.
- Supports AI auto-replies, human takeover, and resuming AI for each lead.
- Provides password-reset emails and automatic cleanup of old uploaded media.

## Technology

| Area | Implementation |
| --- | --- |
| Server | Node.js, Express 5 |
| Database | PostgreSQL via `pg` |
| Realtime updates | Socket.IO |
| WhatsApp | Meta WhatsApp Cloud API |
| AI replies | Groq Chat Completions API |
| Authentication | Signed, HTTP-only `platform_auth` cookie |
| Uploads | Multer, local disk storage, Meta media upload |
| Background work | Node Cron daily upload cleanup |

## Prerequisites

- Node.js 18 or later
- npm
- PostgreSQL 13 or later
- A PostgreSQL account that can connect to the `postgres` database and create the application database
- A Meta developer app with WhatsApp Cloud API access and a webhook-enabled phone number
- A public HTTPS URL for Meta webhook callbacks in production
- An SMTP mailbox for the password-reset feature (optional, but required when using password reset)
- A Groq API key for any client that uses AI replies (optional)

## Install and start

1. Clone or copy the project, then open a terminal in the project directory.

2. Install dependencies:

   ```bash
   npm install
   ```

3. Create the `.env` file using the configuration in [Environment variables](#environment-variables).

4. Create the database and apply the schema:

   ```bash
   npm run db:setup
   ```

5. Run in development mode:

   ```bash
   npm run dev
   ```

   Or run directly with Node.js:

   ```bash
   npm start
   ```

6. Open `http://localhost:3000` (or your configured `PORT`) and sign in.

## Environment variables

Copy `.env.example` to `.env` in the repository root, then replace every placeholder with the appropriate value. Do not commit `.env` to version control.

```bash
copy .env.example .env
```

```env
# HTTP server
PORT=3000
NODE_ENV=development

# PostgreSQL
DB_HOST=localhost
DB_PORT=5432
DB_NAME=someshwar_ai
DB_USER=postgres
DB_PASSWORD=replace_with_your_postgres_password

# Authentication: minimum 32 characters
AUTH_JWT_SECRET=replace_with_a_long_unique_random_secret

# Used by Meta while verifying GET /webhook/whatsapp
WA_VERIFY_TOKEN=replace_with_a_long_random_webhook_token

# Required for the password-reset email flow
COMPANY_EMAIL=your_smtp_email
COMPANY_PASS=your_smtp_password
```

| Variable | Required | Purpose |
| --- | --- | --- |
| `PORT` | Yes | Port on which the HTTP and Socket.IO server listens. |
| `NODE_ENV` | Yes | Use `development` locally and `production` when deployed. Production enables secure authentication cookies. |
| `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASSWORD` | Yes | PostgreSQL connection details. |
| `AUTH_JWT_SECRET` | Yes | Secret used to sign authentication tokens. It must be at least 32 characters. |
| `WA_VERIFY_TOKEN` | Yes for webhook verification | Value Meta sends while verifying the webhook endpoint. |
| `COMPANY_EMAIL`, `COMPANY_PASS` | Only for password reset | SMTP credentials used to send one-time password reset codes. |

WhatsApp access credentials and Groq settings are saved per client in PostgreSQL through the authenticated settings screens; they are not read from `.env`.

## Database setup

Run the following once after configuring the database environment variables:

```bash
npm run db:setup
```

This command runs [`src/db/dbinit.js`](src/db/dbinit.js), which:

1. Connects to PostgreSQL's `postgres` database using `DB_HOST`, `DB_PORT`, `DB_USER`, and `DB_PASSWORD`.
2. Creates `DB_NAME` if it does not already exist.
3. Executes [`src/db/schema.sql`](src/db/schema.sql).
4. Creates the application tables and indexes.
5. Seeds the initial Someshwar AI client, a default WhatsApp configuration, and the super-admin account.

The schema uses `CREATE ... IF NOT EXISTS` statements, so rerunning the setup command is suitable for applying safe schema setup to an existing database. Review schema changes before using it against a production database.

### Main database entities

| Table | Purpose |
| --- | --- |
| `clients` | Customer companies using the platform. |
| `users` | Super admins, client admins, and agents. |
| `whatsapp_configs` | Per-client WhatsApp credentials, welcome messages, and brochure metadata. |
| `whatsapp_leads` | Leads/contacts, unread count, and AI/handover state. |
| `whatsapp_messages` | Incoming and outgoing message history, including media metadata. |
| `ai_settings` | Per-client Groq model, API key, prompt, FAQ, and reply preferences. |
| `conversation_events` | Audit trail for AI enable/disable, handovers, and AI failures. |
| `password_reset_codes` | Expiring, hashed password-reset codes. |

### Default administrator

Database setup seeds the following super-admin account:

```text
Username: admin@example.com
Password: admin@123
```

Change this password immediately after first login. Never expose these default credentials in a production deployment.

## First-time configuration

1. Sign in with the seeded super-admin account.
2. Create a client company and its initial client-admin account from the admin area.
3. Sign in as that client admin.
4. In client settings, save the client’s WhatsApp phone number, Phone Number ID, access token, and welcome/custom messages.
5. Upload a PDF brochure if the client wants one sent automatically to new leads.
6. Optionally add the Groq API key, model, system prompt, business information, and FAQs, then enable AI replies.
7. Configure the client’s Meta WhatsApp phone number to send webhooks to the production URL.

Each Phone Number ID must be unique. The incoming webhook uses this ID to decide which client owns the message.

## WhatsApp webhook setup

The application exposes this endpoint:

```text
GET  /webhook/whatsapp   # Meta verification
POST /webhook/whatsapp   # Incoming WhatsApp events
```

In the Meta developer dashboard:

1. Set the callback URL to:

   ```text
   https://your-domain.example/webhook/whatsapp
   ```

2. Set Meta's verification token to the same value as `WA_VERIFY_TOKEN` in `.env`.
3. Subscribe the WhatsApp app to message events.
4. Ensure the receiving client has a `whatsapp_configs` record with the exact Phone Number ID delivered in Meta's webhook payload.

For local testing, use an HTTPS tunnel such as ngrok or Cloudflare Tunnel and point Meta to the tunnel URL. Do not send production webhooks to an unsecured HTTP endpoint.

### Incoming message flow

```text
Meta WhatsApp webhook
        |
        v
POST /webhook/whatsapp
        |
        v
Match metadata.phone_number_id to client configuration
        |
        v
Store message and download any media
        |
        +--> New lead: send welcome, brochure, and custom message
        |
        +--> Eligible text message: request Groq AI reply
        |
        v
Emit Socket.IO "new-message" event to connected inboxes
```

The service accepts text, image, document, video, audio, sticker, location, contact, button, and interactive message payloads. Supported incoming media is downloaded into `public/uploads/whatsapp/` and its local URL is stored with the message.

## Roles and client isolation

| Role | Access |
| --- | --- |
| `super_admin` | Manages all clients and their users. Must pass `client_id` when using client-scoped APIs. |
| `client_admin` | Manages its own client settings and team members. |
| `agent` | Uses the client-scoped inbox and lead controls. |

Authenticated client-scoped routes enforce the active client server-side. Client admins and agents are restricted to their own client. Super admins must supply `client_id` as a request query parameter or body field, for example `?client_id=2`.

Authentication is stored in the HTTP-only `platform_auth` cookie. In production, the cookie is marked `Secure`; therefore the application must be served over HTTPS.

## AI reply and handover workflow

AI replies are optional and configured per client in `ai_settings`.

For an inbound text message, the application sends an AI request only when:

- AI is enabled for the client.
- A Groq API key is present for the client.
- The lead has `ai_auto_reply_enabled` enabled.
- The lead is not in human handover mode.

The generated reply uses the configured model, system prompt, business information, FAQ content, language guidance, and recent message history. If the Groq request fails, the app disables AI auto-reply for that lead, records an `ai_error` conversation event, and leaves the conversation available for a human agent.

Agents can:

- Disable or enable AI for a particular lead.
- Take over a conversation, which disables AI replies for that lead.
- Resume AI after a handover ends.

## API reference

All routes except the webhook and public static files require the `platform_auth` cookie created by a successful sign-in. Client-scoped routes also require a client context as described above.

### Authentication

| Method | Route | Description |
| --- | --- | --- |
| `POST` | `/api/auth/login` | Sign in with username, email, or phone plus password. |
| `POST` | `/api/auth/logout` | Clear the login cookie. |
| `GET` | `/api/auth/me` | Return the current authenticated user. |
| `POST` | `/api/auth/forgot-password/request` | Send a six-digit password-reset code by email. |
| `POST` | `/api/auth/forgot-password/verify` | Verify the reset code. |
| `POST` | `/api/auth/forgot-password/reset` | Set a new password after verification. |

### Super-admin client management

These endpoints are available only to `super_admin` users.

| Method | Route | Description |
| --- | --- | --- |
| `GET` | `/api/admin/clients` | List client companies and active user counts. |
| `POST` | `/api/admin/clients` | Create a client and its first client-admin account. |
| `GET` | `/api/admin/clients/:id` | Get client details. |
| `PUT` | `/api/admin/clients/:id` | Update client company, slug, plan, status, or expiry. |
| `PATCH` | `/api/admin/clients/:id/status` | Set a client to `active` or `suspended`. |
| `GET` | `/api/admin/clients/:id/users` | List a client’s users. |
| `POST` | `/api/admin/clients/:id/users` | Add a user to a client. |
| `PUT` | `/api/admin/users/:id` | Update a user. |
| `DELETE` | `/api/admin/users/:id` | Delete a user. |

### Inbox and leads

| Method | Route | Description |
| --- | --- | --- |
| `GET` | `/api/messages` | Return up to 200 most recent conversations/messages. |
| `GET` | `/api/messages/logs` | Return inbound message logs. |
| `POST` | `/api/messages/send` | Send a text message. Body: `phone`, `message`. |
| `POST` | `/api/messages/send-file` | Send up to 30 files. Multipart fields: `files`, `phone`, optional `caption`. |
| `POST` | `/api/messages/mark-read/:phone` | Reset the contact’s unread count. |
| `DELETE` | `/api/messages/clear/:phone` | Delete a contact’s messages but keep the lead. |
| `DELETE` | `/api/messages/contact/:phone` | Delete the contact and its messages. |
| `PUT` | `/api/leads/:id/ai-auto-reply` | Enable/disable AI. Body: `{ "enabled": true }`. |
| `POST` | `/api/leads/:id/takeover` | Begin human takeover and pause AI. |
| `POST` | `/api/leads/:id/resume-ai` | End handover and resume AI. |

### Client settings

| Method | Route | Description |
| --- | --- | --- |
| `GET` | `/api/client/settings` | Get WhatsApp and AI settings for the active client. |
| `PUT` | `/api/client/settings/ai` | Update the active client’s AI settings. |
| `GET` | `/api/client/team` | List active client team members. |
| `POST` | `/api/client/team` | Create a client admin or agent as permitted by role. |
| `PATCH` | `/api/client/team/:id/status` | Enable or disable a team member. |
| `GET` | `/api/whatsapp-config` | Get the active client’s complete WhatsApp configuration. |
| `PUT` | `/api/whatsapp-config` | Update WhatsApp credentials and outgoing messages. |
| `POST` | `/api/whatsapp-config/brochure` | Upload and register a brochure. Multipart field: `brochure`. |
| `DELETE` | `/api/whatsapp-config/brochure` | Remove the configured brochure. |

## Media handling

- The application accepts uploads up to 100 MB per file.
- Outbound files are first saved locally, uploaded to Meta, then sent using the returned Meta media ID.
- Files are classified as image, video, audio, or document from their MIME type and extension.
- Incoming media is downloaded through the Cloud API and saved under `public/uploads/whatsapp/`.
- The daily cleanup job runs at 02:00 and deletes uploaded files older than 60 days.

Plan storage and retention accordingly: cleanup is based on a file’s age, so keep external backups when historical media must remain available.

## Realtime events

Socket.IO runs on the same port as the HTTP server and currently allows all origins. The server emits:

| Event | When emitted |
| --- | --- |
| `new-message` | An inbound message, a sent inbox message, or an AI reply is stored. |
| `config-updated` | A WhatsApp configuration or brochure is changed. |

Clients should filter message payloads by `client_id` before displaying them, because realtime events are broadcast by the server.

## Project structure

```text
.
|-- public/
|   |-- index.html, login.html, admin.html, client-settings.html
|   |-- js/ and css/                    # Browser application code and styles
|   `-- uploads/whatsapp/               # Locally stored incoming/outgoing media
|-- src/
|   |-- app.js                          # Express app and route registration
|   |-- controllers/
|   |   |-- webhookController.js         # Incoming Meta webhook handling
|   |   `-- clientSettingsController.js  # Client settings and team controls
|   |-- db/
|   |   |-- dbinit.js                   # Creates DB and applies schema
|   |   |-- postgres.js                 # PostgreSQL pool
|   |   `-- schema.sql                  # Tables, indexes, and seed data
|   |-- middleware/
|   |   |-- auth.js                     # Cookie authentication and role checks
|   |   |-- tenant.js                   # Client context enforcement
|   |   `-- upload.js                   # Multer disk storage configuration
|   |-- routes/                         # API and webhook endpoints
|   |-- services/                       # WhatsApp, Groq, and media services
|   |-- templates/                      # Password reset email template
|   `-- utils/                          # Auth token, delay, and cleanup job
|-- server.js                           # HTTP + Socket.IO entry point
|-- package.json
|-- requirement.txt
`-- .env                                # Local secrets; do not commit
```

## Scripts

| Command | Description |
| --- | --- |
| `npm install` | Install packages declared in `package.json`. |
| `npm run db:setup` | Create the configured PostgreSQL database when absent and apply `src/db/schema.sql`. |
| `npm run dev` | Run the server with Nodemon. |
| `npm start` | Run the server with Node.js. |

## Production deployment

1. Set `NODE_ENV=production` and use strong, unique values for `AUTH_JWT_SECRET`, `WA_VERIFY_TOKEN`, database credentials, WhatsApp tokens, Groq keys, and SMTP credentials.
2. Run the application behind an HTTPS reverse proxy such as Nginx or Apache.
3. Forward the public webhook path `/webhook/whatsapp` to the application port.
4. Restrict direct access to the Node.js port and PostgreSQL host.
5. Set up PostgreSQL backups and monitor disk usage under `public/uploads/whatsapp/`.
6. Change the seeded administrator password and remove/replace any sample WhatsApp configuration created by the database schema.
7. Configure CORS appropriately before exposing the Socket.IO server to untrusted origins.

## Troubleshooting

| Problem | Checks |
| --- | --- |
| `npm run db:setup` cannot connect | Confirm PostgreSQL is running, the `DB_*` values are correct, and `DB_USER` can create databases. |
| Meta verification returns 403 | Confirm the callback URL ends in `/webhook/whatsapp` and the Meta verify token exactly matches `WA_VERIFY_TOKEN`. |
| Incoming webhooks are ignored | Check that the Meta `phone_number_id` matches a unique `whatsapp_configs.phone_number_id` record. |
| AI does not reply | Ensure AI is enabled for the client and lead, the lead is not in takeover mode, and the Groq API key/model are valid. |
| Password reset cannot send email | Verify `COMPANY_EMAIL` and `COMPANY_PASS`, as well as SMTP access for `smtpout.secureserver.net:465`. |
| Login fails with configuration error | Set `AUTH_JWT_SECRET` to a value of at least 32 characters and restart the server. |
| Media cannot be sent | Check the client’s WhatsApp access token, Phone Number ID, file size (100 MB maximum), and Meta media permissions. |

## Security notes

- Treat `.env`, WhatsApp access tokens, Groq API keys, SMTP credentials, and database passwords as secrets.
- Do not publish the seeded administrator credentials.
- Serve production traffic only over HTTPS.
- Keep PostgreSQL private and use least-privilege database credentials wherever possible. The setup process itself requires database-creation permission.
- Review access controls and Socket.IO origin settings before providing the application to untrusted users.
