# Message Flows – Sending and Receiving

This document describes how **sending messages to a contact** and **receiving responses** work end-to-end.

---

## 1. Sending a message (App → Contact / WhatsApp)

### Frontend (ChatWindow)

1. User types in the chat input and submits (or presses Enter).
2. **Payload** is built with:
   - `to`: Contact phone (digits only). Taken from `selectedConversation.contacts?.phone` (or `contact?.phone`, `phone`, `name`). Prisma returns the relation as `contacts`.
   - `messageText`: The typed text.
   - `messageType`: `"text"`.
   - `businessId`: From `localStorage.getItem('business_id')` (or fallback).
   - `user_id`: From `getUserId(user)` for backend context.
3. **Request**: `POST /api/v1/messages/send` via `messageApi.post('/send', payload)`.
4. **Auth**: Request includes `Authorization: Bearer <token>` (messageApi interceptor). Backend route uses `validateUser` middleware.
5. **Optimistic UI**: Message is appended to the list immediately; on success it is updated with real `messageId` and status `"sent"`; on error it is removed.

### Backend (messageOutbound.controller.js)

1. **Validation**: `validateOutboundMessage` requires `to`, `businessId`, and for text messages `messageText`.
2. **Business**: Loads business by `businessId`; returns 404 if not found.
3. **Credentials**: Resolves WhatsApp token and Phone Number ID from:
   - DB `integrations` + `api_keys` for the business, or
   - Env: `META_ACCESS_TOKEN` / `GRAPH_API_TOKEN`, `META_PHONE_NUMBER_ID`.
4. **Test mode**: If no token or phone number ID, message is **not** sent to Meta; it is only saved in DB and emitted via Socket.io. Response includes `testMode: true`.
5. **Live mode**: Builds Meta payload with `buildMetaPayload(to, messageType, ...)` and sends:
   - `POST https://graph.facebook.com/{version}/{phone_number_id}/messages`
   - With `Authorization: Bearer {accessToken}`.
6. **After send**: Finds or creates contact and conversation by `to` and `businessId`, saves the outbound message to `messages` table, calls `emitNewMessage(businessId, messageData)`, and updates `conversations.last_message_at`.
7. **Response**: `{ STATUS: "success", DATA: { messageId, metaMessageId, status } }`.

### Important for sending

- **Phone format**: Backend normalizes `to` to digits only in `findOrCreateContactForOutbound`. Frontend also normalizes for consistency; empty after strip falls back to raw value so `to` is never empty.
- **Auth**: Send endpoint is protected with `validateUser`; frontend must send a valid JWT.
- **Conversation shape**: Conversations from `GET /api/v1/conversations` include `contacts: { id, name, phone, email }`. ChatWindow uses `contacts?.phone` (and fallbacks) for `to`.

---

## 2. Receiving a message (WhatsApp → App)

### Meta → Backend (webhook)

1. Meta sends **POST** to your webhook URL, e.g. `https://node.ekpk.pk/api/v1/webhooks/whatsapp`.
2. **Webhook handler** (`webhook.controller.js`):
   - Validates payload has `object` (e.g. `"whatsapp_business_account"`).
   - Logs payload to `webhook_logs` table.
   - For each `entry[].changes[]` where `field === "messages"`, calls `processMessageChange(change.value)`.
3. **processMessageChange**: Iterates over `value.messages[]` and:
   - **Text**: `processTextMessage(message, messageData)`.
   - **Media** (image, audio, video, document): `processMediaMessage(message, messageData, type)`.
4. **processTextMessage** (and similarly for media):
   - Extracts `from` (customer phone), `message.text.body`, `id`, `timestamp`.
   - **Business ID**: `extractBusinessId(messageData)` uses `messageData.metadata.phone_number_id` to look up `api_keys` → `integrations.business_id`. Falls back to `DEFAULT_BUSINESS_ID` env if not found.
   - **Contact**: `findOrCreateContact(from, messageData)` – find or create contact by phone and `business_id`.
   - **Conversation**: `findOrCreateConversation(contact.id, messageData)` – find open conversation or create one.
   - **Save**: `saveMessage(...)` inserts into `messages` with `sender_type: "customer"`, `channel: "whatsapp"`.
   - **Emit**: `emitNewMessage(contact.business_id, { id, conversation_id, message_text, sender_type, sender, created_at, ... })`.

### Backend → Frontend (Socket.io)

1. **Server** (`socketService.js`): `emitNewMessage(businessId, messageData)` does:
   - `io.to('business_' + businessId).emit('new_message', { type: 'new_message', data: messageData, timestamp })`.
2. **Client** must be in the room `business_{businessId}` to receive. **InboxPage** connects the socket and joins the business room:
   - On mount (when user has `business_id`): `socketService.connect(businessId, userId)`.
   - On connect, the socket emits `join_business` with `businessId`, so the server adds the socket to `business_${businessId}`.
   - On unmount: `socketService.leaveBusinessRoom(businessId)`.
3. **ChatWindow** subscribes to `new_message`:
   - Handler uses `payload.data || payload` and checks `data.conversation_id === selectedConversation.id`.
   - If it matches the open chat, it appends the message: `setMessages((prev) => [...prev, data])`.

### Important for receiving

- **Webhook URL**: Must be HTTPS and registered in Meta App Dashboard (WhatsApp → Configuration → Webhook). Callback URL = `https://your-backend/api/v1/webhooks/whatsapp`.
- **Verify token**: GET to the same URL with `hub.mode`, `hub.verify_token`, `hub.challenge`; backend compares `hub.verify_token` to `WEBHOOK_VERIFY_TOKEN` (or `META_WEBHOOK_VERIFY_TOKEN`) and responds with `hub.challenge`.
- **business_id**: Incoming payload’s `value.metadata.phone_number_id` is used to resolve `business_id`. Ensure your Phone Number ID is linked in DB (`api_keys.phone_number_id`) or set `DEFAULT_BUSINESS_ID` for a single-tenant setup.
- **Socket**: Receiving in the UI only works if the client has called `socketService.connect(businessId, userId)` and the server has received `join_business`. InboxPage does this when the user has a `business_id`.

---

## 3. Quick reference

| Flow        | Endpoint / channel        | Auth / requirement |
|------------|---------------------------|--------------------|
| Send       | POST `/api/v1/messages/send` | Bearer token, `validateUser` |
| Receive    | POST `/api/v1/webhooks/whatsapp` | Meta signature (optional), verify token for GET |
| Real-time  | Socket.io `new_message`    | Client in room `business_{businessId}` |

| Frontend (send)   | Backend (send)                    | Backend (receive)     | Frontend (receive)   |
|-------------------|-----------------------------------|------------------------|----------------------|
| ChatWindow        | messageOutbound.controller        | webhook.controller     | ChatWindow (socket)  |
| messageApi.post('/send') | POST Meta API or test mode  | processMessageChange   | socketService.on('new_message') |
| InboxPage         | –                                 | emitNewMessage         | InboxPage connects socket + join_business |

---

## 4. Troubleshooting

- **Send fails (400)**  
  Check `to`, `businessId`, and `messageText` are present and valid. Ensure JWT is sent and valid.

- **Send works in test mode but not to WhatsApp**  
  Configure `META_PHONE_NUMBER_ID` and `META_ACCESS_TOKEN` (or `GRAPH_API_TOKEN`). Check Meta API error in response or logs.

- **Incoming messages not in DB**  
  Webhook URL must be reachable by Meta; check Nginx and firewall. Ensure webhook is verified and subscribed to “messages”. Check `extractBusinessId` and `DEFAULT_BUSINESS_ID` so messages are attached to the correct business.

- **Incoming messages not in UI**  
  Ensure InboxPage (or equivalent) calls `socketService.connect(businessId, userId)` so the client joins `business_${businessId}`. Check browser console for Socket.io connection and `new_message` events. Ensure backend actually calls `emitNewMessage(contact.business_id, ...)` after saving the message.
