# WhatsCRM – תיעוד API מלא

> מסמך טכני שלם של ה-Backend של WhatsCRM. כולל את כל ה-endpoints, את אופן עבודת ה-QR (Baileys), ואת מסלול הזרימה המלא של הודעה בין שני מספרים – המספר המחובר (instance) והמספר היעד.

---

## תוכן עניינים

1. [סקירה ארכיטקטונית](#1-סקירה-ארכיטקטונית)
2. [אימות (Authentication)](#2-אימות-authentication)
3. [התחברות WhatsApp דרך QR](#3-התחברות-whatsapp-דרך-qr)
4. [זרימת הודעה בין שני מספרים](#4-זרימת-הודעה-בין-שני-מספרים)
5. [Socket.IO – אירועים בזמן אמת](#5-socketio--אירועים-בזמן-אמת)
6. [Webhooks יוצאים](#6-webhooks-יוצאים)
7. [תיעוד Endpoints מלא](#7-תיעוד-endpoints-מלא)
8. [סכמת בסיס הנתונים – טבלאות מרכזיות](#8-סכמת-בסיס-הנתונים)
9. [קודי שגיאה ותגובות סטנדרטיות](#9-קודי-שגיאה)

---

## 1. סקירה ארכיטקטונית

**Stack:** Node.js + Express + Socket.IO + MySQL + Baileys (WhatsApp Web).

**נקודת כניסה:** [wcrm/app.js](wcrm/app.js#L13) – יוצרת Express app, טוענת middleware (cors, fileUpload, body-parser), מרשמת את כל ה-routers תחת prefix `/api/...`, מפעילה את ה-server על `process.env.PORT || 3010`, ולאחר ההאזנה מאתחלת:

- `init()` מ-[helper/addon/qr/index.js](wcrm/helper/addon/qr/index.js#L444) – טוען מחדש את כל ה-instances ב-status `ACTIVE` מטבלת `instance` ופותח להם session ב-Baileys.
- `warmerLoopInit()` – לולאת חימום מספרים.
- `initCampaign()` – מנוע קמפיינים (broadcast).
- `initTele()` – חיבור Telegram.

**Mount של ה-routes** ב-[wcrm/app.js](wcrm/app.js#L22-L39):

| Prefix | Router |
|---|---|
| `/api/user` | routes/user.js |
| `/api/web` | routes/web.js |
| `/api/admin` | routes/admin.js |
| `/api/phonebook` | routes/phonebook.js |
| `/api/chat_flow` | routes/chatFlow.js |
| `/api/inbox` | routes/inbox.js |
| `/api/templet` | routes/templet.js |
| `/api/chatbot` | routes/chatbot.js |
| `/api/broadcast` | routes/broadcast.js |
| `/api/v1` | routes/apiv2.js (Public Developer API – Meta Cloud) |
| `/api/agent` | routes/agent.js |
| `/api/qr` | routes/qr.js (QR / Baileys) |
| `/api/ai` | routes/ai.js |
| `/api/webhook` | routes/webhook.js |
| `/api/wa_call` | routes/waCall.js |
| `/api/telegram` | routes/telegram.js |
| `/api/theme` | routes/theme.js |

**מדיה סטטית:** `/media/*` ו-`/meta-media/*` משרתים קבצים מ-`./client/public/media` עם תמיכה ב-Range requests (סטרימינג של אודיו/וידאו). ה-SPA מוגש כקטץ' אול מ-`./client/public/index.html`.

**Socket.IO** מאותחל ב-[wcrm/socket.js](wcrm/socket.js#L41) ומחובר לאותו HTTP server.

---

## 2. אימות (Authentication)

המערכת תומכת בשלושה סוגי משתמשים:

| Role | Token | Header / Param | Middleware |
|---|---|---|---|
| User (בעל החשבון) | JWT חתום ב-`JWTKEY` | `Authorization: Bearer <token>` | [validateUser](wcrm/middlewares/user.js#L4) |
| Agent (סוכן בצוות) | JWT עם `role: "agent"` | `Authorization: Bearer <token>` | `validateAgent` (middlewares/agent.js) |
| Admin | JWT אדמין | `Authorization: Bearer <token>` | `adminValidator` (middlewares/admin.js) |
| Developer API (apiv2) | `api_key` קבוע השמור בעמודה `user.api_key` | `?token=<api_key>` ב-query או `body.token` | `decodeToken()` בתוך ה-route |

**JWT payload** של user: `{ uid, email, password, role: "user" }`. ה-validator מאמת חתימה, ואז מאמת מול DB ש-email+password+role עדיין מתאימים. אם לא – מחזיר `{ logout: true }` (ה-frontend אמור להתנתק).

**Middleware נוספים:**

- `checkPlan` – מוודא שלמשתמש יש מנוי תקף.
- `checkContactLimit`, `checkNote`, `checkTags` – מגבלות לפי המנוי.
- `checkQrScan` – בודק כמה QR-instances מותרים במנוי.
- `checkLicense` – אימות רישיון מערכת (לשרת עצמו).

**יצירת API Key אישי:** `GET /api/user/generate_api_keys` – מחזיר `api_key` חדש, שומר ב-`user.api_key`. משמש את כל ה-endpoints תחת `/api/v1/*` ואת `/api/qr/rest/send_message`.

---

## 3. התחברות WhatsApp דרך QR

מימוש: [wcrm/helper/addon/qr/index.js](wcrm/helper/addon/qr/index.js)
ספרייה: [`baileys`](https://github.com/WhiskeySockets/Baileys) (Multi-Device, WhatsApp Web protocol)
שמירת credentials: [`mysql-baileys`](https://www.npmjs.com/package/mysql-baileys) – נשמרים בטבלה `auth` במסד.

### 3.1 מושגים

- **`uniqueId`** – מזהה ייחודי של ה-instance (חיבור WhatsApp). בדרך כלל בפורמט `<uid>_<random>`. ה-`uid` של המשתמש נחלץ ממנו ב-[`extractUidFromSessionId()`](wcrm/helper/addon/qr/index.js#L55) על-ידי `split("_")[0]`.
- **`activeConnections`** – `Map` בזיכרון השרת המחזיק את כל ה-`sock` של Baileys לפי `uniqueId`. שורד רק עד restart, ולכן `init()` בעלייה טוען מחדש מ-DB.
- **טבלת `instance`** – שורה אחת לכל חיבור: `uid, title, uniqueId, number, status (GENERATING|ACTIVE|INACTIVE), data (JSON של sock.user), qr (data:image/png;base64,...), other`.

### 3.2 מסלול יצירת חיבור חדש (סריקת QR)

1. **המשתמש לוחץ "הוסף WhatsApp"** ב-frontend.
2. **`POST /api/qr/gen_qr`** עם `{ title, uniqueId }` (ראה [routes/qr.js](wcrm/routes/qr.js#L106)). Middlewares: `validateUser → checkPlan → checkQrScan`.
3. הראוט מבצע `INSERT INTO instance (uid, title, uniqueId, status) VALUES (?,?,?, 'GENERATING')` ואז קורא ל-[`createSession(uniqueId, title)`](wcrm/helper/addon/qr/index.js#L86).
4. `createSession` עושה:
   - טוען `baileys` באופן דינמי (ESM dynamic import).
   - שולף את הגרסה האחרונה: `fetchLatestBaileysVersion()`.
   - יוצר state בטבלה `auth` במסד באמצעות `useMySQLAuthState`.
   - יוצר socket: `makeWASocket({ auth, version, browser: [title, "", ""], printQRInTerminal: false, keepAliveIntervalMs: 30000, ... })`.
   - שומר את ה-sock ב-`activeConnections.set(sessionId, sock)`.
   - רושם listeners: `creds.update`, `messages.upsert`, `messages.update`, `lid-mapping.update`, `connection.update`.
5. **`connection.update` עם `qr`** – Baileys מנפיק QR. הקוד הופך אותו ל-Data URL עם `qrcode.toDataURL(qr)` ושומר ב-`UPDATE instance SET qr = ? WHERE uniqueId = ?`. ה-frontend מבצע polling/socket וקורא:
6. **`GET /api/qr/get_all`** עם `validateUser` – מחזיר את כל ה-instances של המשתמש כולל שדה `qr`. ה-frontend מציג את ה-QR.
7. **המשתמש סורק עם הטלפון.** Baileys משלים handshake; `connection.update` נורה עם `connection: "open"`. אז:
   ```sql
   UPDATE instance SET status='ACTIVE', number=<phone>, data=<JSON sock.user> WHERE uniqueId=?
   ```
   המספר נחלץ עם regex `/^(\d+)(?=:|\@)/` מ-`sock.user.id` (פורמט `5491166...:7@s.whatsapp.net`).

### 3.3 ניתוק וניסיונות חיבור מחדש

ב-`connection.update` כש-`connection: "close"`:

- אם `statusCode === DisconnectReason.loggedOut` (התנתק מהטלפון): מוחק מ-`activeConnections`, קורא `removeCreds()`, ועושה `UPDATE instance SET status='INACTIVE'`.
- אחרת (נפילת רשת/שגיאה): `setTimeout(() => createSession(sessionId, title, options), 5000)` – ניסיון חיבור מחדש אחרי 5 שניות.

ב-startup, [`init()`](wcrm/helper/addon/qr/index.js#L444) קורא `SELECT uniqueId FROM instance WHERE status='ACTIVE'` ופותח מחדש לכל אחד session. ה-credentials נטענים אוטומטית מ-`auth` כי `useMySQLAuthState` משתמש באותו `session: uniqueId`.

### 3.4 פעולות על instance

| Endpoint | תיאור |
|---|---|
| `GET  /api/qr/get_all` | רשימת כל ה-instances של ה-user. עובר על כל instance, אם אין session ב-`activeConnections` מסמן status=INACTIVE. |
| `GET  /api/qr/get_all_agent` | אותו דבר עבור agent (`req.owner.uid`). |
| `POST /api/qr/del_instance` body: `{ uniqueId }` | `session.logout()` → מוחק מ-Map → `DELETE FROM instance`. |
| `POST /api/qr/change_instance_status` body: `{ insId, status }` | קורא `session.sendPresenceUpdate("available"/"unavailable")` ושומר ב-`instance.other`. |

### 3.5 שליחת הודעה דרך REST (חיצונית)

`POST /api/qr/rest/send_message` או `GET /api/qr/rest/send_message` – API ציבורי לאינטגרציה (ראה [processMessageRequest()](wcrm/routes/qr.js#L411)).

**פרמטרים חובה:** `messageType, requestType, token, from, to`.

**זרימה:**
1. בונה `msgContent` לפי `messageType` (text/image/video/audio/document/location).
2. `decodeToken(token)` → מאמת `user.api_key`.
3. בודק שיש `plan` תקף (`getNumberOfDaysFromTimestamp >= 1`) ו-`checkWarmerPlan` (האם המנוי כולל QR REST).
4. שולף instance: `SELECT * FROM instance WHERE uid=? AND number=? AND status='ACTIVE'`.
5. `getSession(instance.uniqueId)` – שולף את ה-`sock` מהזיכרון.
6. `isExists(session, "<to>@s.whatsapp.net")` – בודק שהמספר רשום ב-WhatsApp.
7. `session.sendMessage("<to>@s.whatsapp.net", msgContent)` ומחזיר `messageId` ו-timestamp.

דוגמת בקשה:
```http
POST /api/qr/rest/send_message
Content-Type: application/json

{
  "messageType": "text",
  "requestType": "POST",
  "token": "<API_KEY>",
  "from": "972501234567",
  "to": "972549876543",
  "text": "שלום מהמערכת"
}
```

תמיכה לכל סוגי המדיה:
- `image` – `imageUrl`, `caption?`
- `video` – `videoUrl`, `caption?`
- `audio` – `aacUrl` (נשלח כ-`ptt: true`, `mimetype: "audio/aac"`)
- `document` – `docUrl`, `caption?` (שם קובץ נחלץ מה-URL)
- `location` – `lat`, `long`, `title?`

---

## 4. זרימת הודעה בין שני מספרים

זהו ה-flow המלא מהרגע שהמספר היעד שולח הודעה ל-WhatsApp של המספר המחובר ועד שהיא מופיעה בלקוח, נשמרת, ונענית.

### 4.1 הודעה נכנסת (INCOMING)

```
[טלפון של המספר היעד] 
        │ שולח WhatsApp
        ▼
[שרתי WhatsApp / Meta]
        │ Baileys WebSocket (sock)
        ▼
[helper/addon/qr/index.js – sock.ev.on("messages.upsert")]
        │ סינון: לא @newsletter, לא status@broadcast, type==="notify"
        │ resolve LID (@lid → @s.whatsapp.net) דרך signalRepository.lidMapping
        │ extractUidFromSessionId(sessionId)
        ▼
[processMessage] (helper/inbox/inbox.js)
        │ origin: "qr"
        ▼
[processMessageQr] (helper/addon/qr/processThings.js)
        │ • מזהה את סוג ההודעה (text/image/video/audio/document/location/contact/...)
        │ • אם מדיה: downloadMediaMessage → שמירה ל-./client/public/media/<uid>/<filename>
        │ • בונה אובייקט actualMsg עם:
        │     { type, route: "INCOMING", senderName, senderMobile,
        │       msgContext: { ...payload לפי הסוג }, timestamp, msgId, ... }
        │ • UPDATE/INSERT לטבלת beta_chats:
        │     last_message, sender_name, sender_mobile, unread_count++,
        │     origin="qr", origin_instance_id, profile_image
        │ • INSERT לטבלת beta_chat_logs (היסטוריית השיחה)
        ▼
[helper/inbox/inbox.js → המשך processMessage]
        │ • generateNotificationFromMessage → push notification (FCM)
        │ • sendToUid(uid, payload, "new_message") דרך Socket.IO
        │ • אם לאחד הסוכנים יש שיוך לצ'אט – שולח גם להם
        ▼
[chatbot/chatFlow/AI handler]
        │ processWebhook → בדיקת keyword/regex
        │ אם match → בונה תגובה אוטומטית
        │ אם הצ'אטבוט מסוג AI → functions/ai.js (OpenAI/Gemini)
        │ אם chatFlow פעיל → הרצת node הבא בזרימה
        ▼
[processAutomation] (automation/automation.js)
        │ הרצת automations מותנים (tags, hooks, time-based)
        ▼
[processWebhook → POST ל-URLs חיצוניים שהוגדרו תחת /api/webhook]
```

### 4.2 הודעה יוצאת (OUTGOING) – נשלחת מהמערכת אל המספר היעד

מסלול שליחה ידנית ע"י משתמש מ-Inbox UI:

1. ה-Frontend קורא לאחד מ:
   - `POST /api/inbox/send_text`  body: `{ chatId, instanceId, text, ... }`
   - `POST /api/inbox/send_image` (multipart או URL)
   - `POST /api/inbox/send_video` / `send_audio` / `send_doc`
   - `POST /api/inbox/send_meta_templet` (תבנית WhatsApp Business)
2. ה-route מבצע `validateUser → checkPlan`.
3. שולף `getSession(instanceId)` (Baileys sock).
4. בונה `msgContent` (כמו ב-3.5).
5. אם זה טקסט – הקוד ב-[`sendMessage()`](wcrm/helper/addon/qr/index.js#L362) מנסה `getUrlInfo()` ליצירת link preview (עם `thumbnailWidth: 1024`, timeout 5s).
6. `await delay(1000)` – throttle של שניה בין הודעות.
7. `session.sendMessage("<to>@s.whatsapp.net", msgContent)` – מחזיר אובייקט עם `key.id`.
8. הקוד שומר את ההודעה ב-`beta_chat_logs` עם `route: "OUTGOING"` ועדכון של `beta_chats.last_message`.
9. `sendToUid(uid, msg, "new_message")` משדר עדכון ל-Socket של הלקוח.

מסלול שליחה אוטומטית ע"י Agent:
- אותם endpoints קיימים תחת `/api/agent/send_text`, `/api/agent/send_image` וכו', עם `validateAgent`. בנוסף `agents.allow_send` חייב להיות true (מתחלף ב-`POST /api/agent/change_status_allow_send`).

מסלול שליחה ע"י Broadcast (קמפיין):
- `POST /api/broadcast/add_new` יוצר רשומת campaign.
- ה-`campaignBeta.js` loop רץ כל X שניות, שולף messages ב-status PENDING ושולח דרך אותו `sendMessage()`.

מסלול שליחה ע"י Chatbot/Webhook:
- `routes/inbox.js` מקבל `POST /api/inbox/webhook/:uid` מ-Meta Cloud API – זה מסלול moderate (לא Baileys).
- עבור QR – ההודעה הנכנסת עוברת דרך `processMessage()` ש-trigger-מפעיל את הצ'אטבוט. אם נמצאה תשובה – נקרא `session.sendMessage()` ישירות מהזרימה.

### 4.3 חיווי קריאה / סטטוסים

`sock.ev.on("messages.update")` נורה כש-המספר היעד פותח/קורא הודעה ששלחנו (`message.update.status` יכול להיות 1=sent, 2=delivered, 3=read, 4=played). הקוד מעביר ל-`processMessage` עם `qrType: "update"`. ב-DB מתעדכן `beta_chat_logs.status`.

### 4.4 LID Mapping

WhatsApp Multi-Device החל לשלוח מזהים מסוג `<id>@lid` במקום `@s.whatsapp.net`. הקוד מטפל ב:
```js
const resolvedJid = await sock.signalRepository?.lidMapping?.getPNForLID(remoteJid);
```
אם יש mapping – ממיר חזרה ל-`@s.whatsapp.net` כדי לשמור עקביות במסד. אירוע `lid-mapping.update` נרשם ל-log.

---

## 5. Socket.IO – אירועים בזמן אמת

מימוש: [wcrm/socket.js](wcrm/socket.js)

### 5.1 התחברות

**URL:** אותו host כמו HTTP server. **Auth:** `socket.handshake.query.token` חייב להיות JWT תקף.

```js
const socket = io("https://your-server.com", { query: { token: jwtToken } });
```

לאחר אימות, השרת שולח `connection_ack`:
```json
{
  "status": "success",
  "socketId": "...",
  "userData": { "uid": "...", "name": "...", "email": "...", "isAgent": false }
}
```

### 5.2 אירועים יוצאים מהשרת

| Event | Payload | מתי |
|---|---|---|
| `connection_ack` | `{ status, socketId, userData }` | מיד אחרי connect |
| `new_message` | `{ chatId, message, type, sessionId, origin }` | הודעה נכנסת/יוצאת חדשה |
| `message_update` | `{ msgId, status, chatId }` | סטטוס הודעה השתנה |
| `qr_update` | `{ uniqueId, qr }` | קוד QR חדש זמין |
| `instance_status` | `{ uniqueId, status }` | חיבור התחבר/התנתק |
| `chat_assigned` | `{ chatId, agentIds }` | צ'אט הוקצה לסוכן |
| `notification` | `{ title, body, ... }` | התראה כללית |

(האירועים נשלחים דרך `sendToUid(uid, data, event)` או `sendToAll`.)

---

## 6. Webhooks יוצאים

מאוקפלים תחת `/api/webhook/*`. ה-user מגדיר URL חיצוני וה-CRM ירשום אליו אירועים.

| Endpoint | תיאור |
|---|---|
| `GET  /api/webhook/get_webhooks` | מחזיר את כל ה-webhooks של ה-user |
| `POST /api/webhook/add_webhook` body: `{ name, url, events: [], secret? }` | יוצר webhook חדש |
| `POST /api/webhook/update_webhook` | עדכון |
| `POST /api/webhook/delete_webhook` | מחיקה |
| `POST /api/webhook/webhook/:webhook_id` | endpoint Inbound לבדיקה (לקבלת payloads) |
| `GET  /api/webhook/get_webhook_logs` | היסטוריית קריאות |
| `POST /api/webhook/delete_webhook_logs` | מחיקת לוגים |

**Payload סטנדרטי** שנשלח ל-URL חיצוני בכל הודעה נכנסת:
```json
{
  "event": "message.received",
  "uid": "<user uid>",
  "chatId": "972549876543@s.whatsapp.net",
  "instanceId": "<uniqueId>",
  "from": "972549876543",
  "to": "972501234567",
  "type": "text",
  "message": { "text": { "body": "..." } },
  "timestamp": 1718000000,
  "msgId": "..."
}
```

### 6.1 אימות חתימה (HMAC SHA-256)

כל webhook יוצא כעת נשלח עם headers נוספים לאימות שלא שונה ב-transit (כשמוגדר `outbound_webhook_secret` למשתמש):

| Header | תיאור |
|---|---|
| `X-Webhook-Signature` | `sha256=<hex>` – HMAC-SHA256 של ה-raw body עם ה-secret |
| `X-Webhook-Event` | שם האירוע (למשל `message.received`) |
| `X-Webhook-Timestamp` | Unix timestamp של השליחה (להגנה מפני replay) |
| `X-Webhook-Secret` | (legacy) ה-secret בטקסט גלוי – נשמר לתאימות לאחור |

**דוגמת אימות ב-Node.js (Express):**
```js
const crypto = require("crypto");

app.post("/wcrm-webhook", express.raw({ type: "application/json" }), (req, res) => {
  const secret = process.env.WCRM_WEBHOOK_SECRET;
  const sig = req.header("X-Webhook-Signature") || "";
  const expected =
    "sha256=" + crypto.createHmac("sha256", secret).update(req.body).digest("hex");

  // Constant-time compare
  const a = Buffer.from(sig);
  const b = Buffer.from(expected);
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.status(401).end();
  }

  const event = JSON.parse(req.body.toString("utf8"));
  // ... handle event
  res.json({ ok: true });
});
```

> חשוב: לאמת על ה-**raw body** ולא על אובייקט JSON שעבר re-serialization, אחרת ה-hash לא יתאים.

---

## 7. תיעוד Endpoints מלא

> כל הפרטים – method, path, middleware, מטרה. ראה את הקבצים עצמם תחת [wcrm/routes/](wcrm/routes/) לפרטי body מדויקים.

### 7.1 `/api/user` – ניהול משתמשים, תשלומים, פרופיל

| Method | Path | Auth | תיאור |
|---|---|---|---|
| POST | `/login` | – | התחברות עם email/password, מחזיר JWT |
| POST | `/signup` | – | רישום משתמש חדש |
| POST | `/login_with_google` | – | OAuth Google |
| POST | `/login_with_facebook` | – | OAuth Facebook |
| POST | `/send_resovery` | – | שליחת מייל איפוס סיסמה |
| GET  | `/modify_password` | user | שינוי סיסמה |
| GET  | `/get_me` | user | פרטי המשתמש המחובר |
| POST | `/update_profile` | user | עדכון פרופיל |
| GET  | `/generate_api_keys` | user | יצירת/החלפת API key |
| GET  | `/fetch_profile` | user | תמונת פרופיל מ-WhatsApp |
| POST | `/return_media_url` | user | העלאת קובץ → URL ב-`/media` |
| POST | `/convert_audio` | user | המרת אודיו ל-OGG/Opus |
| POST | `/update_meta` | user | שמירת META Cloud API tokens |
| GET  | `/get_meta_keys` | user | קבלת מפתחות META |
| POST | `/add_meta_templet` | user+plan | יצירת תבנית WhatsApp Business |
| GET  | `/get_my_meta_templets` | user | רשימת תבניות |
| POST | `/del_meta_templet` | user | מחיקת תבנית |
| POST | `/add_quick_reply` | user | תשובה מהירה |
| GET  | `/get_all_quick_reply` | user | רשימת תשובות מהירות |
| POST | `/del_quick_r` | user | מחיקה |
| POST | `/add_warmer_message` | user | הוספת הודעת חימום |
| GET  | `/get_my_warmer` | user | סטטוס warmer |
| POST | `/change_warmer_status` | user | start/stop warmer |
| GET  | `/get_dashboard` | user | נתוני דשבורד (chats, messages, agents) |
| POST | `/start_free_trial` | user | התחלת trial |
| POST | `/get_plan_details` | user | פרטי המנוי |
| POST | `/create_stripe_session` | user | תשלום Stripe Checkout |
| POST | `/pay_with_rz` | user | Razorpay |
| POST | `/pay_with_paypal` | user | PayPal |
| POST | `/pay_with_paystack` | user | Paystack |
| GET  | `/mercadopago_payment` | – | callback MercadoPago |
| POST | `/mercadopago_webhook` | – | webhook MercadoPago |
| POST | `/add_task_for_agent` | user | יצירת משימה לסוכן |
| GET  | `/get_my_agent_tasks` | user | משימות שיצרתי |
| POST | `/add_widget` / `del_widget` / `get_my_widget` | user | ניהול widget צ'אט באתר |
| GET  | `/widget` | – | endpoint ציבורי – snippet ל-embed |
| POST | `/update_fcm_token` | user | רישום FCM push |
| POST | `/get_agent_report` | user | דוח ביצועים של סוכנים |
| POST | `/send_template_message` | user | שליחת תבנית META לרשימה |
| POST | `/exchange_embed_token` | user | החלפת קוד META Embedded Signup |

### 7.2 `/api/agent` – פאנל סוכן

| Method | Path | Auth | תיאור |
|---|---|---|---|
| POST | `/login` | – | התחברות סוכן (email+password) |
| GET  | `/logout` | agent | יציאה |
| GET  | `/get_me` | agent | פרטי הסוכן |
| POST | `/add_agent` | user+plan | הבעלים מוסיף סוכן |
| GET  | `/get_my_agents` | user | רשימת סוכנים |
| POST | `/change_status_mask` | user | מיסוך מספרי לקוחות |
| POST | `/change_status_allow_send` | user | הרשאת שליחה |
| POST | `/change_agent_activeness` | user | הפעלה/השבתה |
| POST | `/del_agent` | user | מחיקה |
| POST | `/get_agent_chats_owner` | user | צ'אטים שהוקצו |
| POST | `/get_assigned_chat_agent` | user | סוכנים על צ'אט |
| POST | `/update_agent_in_chat` | user | שיוך סוכן לצ'אט |
| POST | `/del_assign_chat_by_owner` | user | ביטול שיוך |
| GET  | `/get_my_assigned_chats` | agent | הצ'אטים של הסוכן |
| POST | `/get_convo` | agent | היסטוריית שיחה |
| POST | `/send_text` | agent+plan | שליחת טקסט |
| POST | `/send_audio` / `send_doc` / `send_video` / `send_image` | agent+plan | שליחת מדיה |
| POST | `/return_media_url` | agent | העלאת קובץ |
| POST | `/convert_audio` | agent | המרת אודיו |
| GET  | `/get_my_task` | agent | משימות שלי |
| POST | `/mark_task_complete` | agent | סימון משימה |
| POST | `/change_chat_ticket_status` | agent | פתיחת/סגירת tag |
| GET  | `/get_all_quick_reply` | agent | תשובות מהירות |
| POST | `/save_contact_agent` | user | שמירת איש קשר |
| POST | `/update_fcm_token` | agent | FCM push |

### 7.3 `/api/qr` – חיבור WhatsApp QR (Baileys)

| Method | Path | Auth | תיאור |
|---|---|---|---|
| GET  | `/create?id=...` | – (בדיקה פנימית) | יוצר session ידנית |
| GET  | `/send` | – (debug) | hardcoded send – לא לשימוש production |
| POST | `/gen_qr` body: `{ title, uniqueId }` | user+plan+QR | יוצר instance + מייצר QR |
| GET  | `/get_all` | user | רשימת instances + שדה `qr` |
| GET  | `/get_all_agent` | agent | אותו דבר עבור סוכן |
| POST | `/del_instance` body: `{ uniqueId }` | user | logout + מחיקה |
| POST | `/change_instance_status` body: `{ insId, status }` | user | available/unavailable |
| POST | `/rest/send_message` body: `{ messageType, requestType, token, from, to, ... }` | API key | **API ציבורי לשליחת הודעה דרך QR** |
| GET  | `/rest/send_message?...` | API key | אותו API דרך GET |
| GET  | `/rest/instance_status?token=...&instanceId=...\|from=...` | API key | סטטוס instance + האם connected בזיכרון |
| GET  | `/rest/get_chats?token=...&limit=&offset=&search=&instanceId=` | API key | רשימת שיחות (מ-`beta_chats`) |
| GET  | `/rest/get_messages?token=...&chatId=...&limit=&before=` | API key | היסטוריית הודעות של שיחה (`beta_conversation`) |
| POST | `/rest/mark_read` body: `{ token, chatId, msgId, instanceId\|from }` | API key | סימון הודעות כנקראו (משדר read receipts ל-WhatsApp ומאפס unread) |

#### סוגי הודעות נתמכים ב-`rest/send_message`

| `messageType` | פרמטרים נוספים |
|---|---|
| `text` | `text` |
| `image` | `imageUrl`, `caption?` |
| `video` | `videoUrl`, `caption?` |
| `audio` | `aacUrl` (PTT, audio/aac) |
| `document` | `docUrl`, `caption?` |
| `location` | `lat`, `long`, `title?` |
| `reaction` | `emoji`, `targetMsgId`, `targetFromMe?` |
| `delete` | `targetMsgId`, `targetFromMe?` |

**שדות אופציונליים נוספים לכל הסוגים (חוץ מ-reaction/delete):**

- `replyTo` + `replyFromMe?` – ציטוט הודעה אחרת (reply).
- `to` יכול להיות מספר רגיל (`972549876543`), JID (`...@s.whatsapp.net`), או מזהה קבוצה (`...@g.us`).

#### Rate Limiting

כל endpoints תחת `/api/qr/rest/*` מוגנים ב-rate-limiter פר API key + endpoint:

- ברירת מחדל: **60 בקשות לדקה**
- ניתן לכוון דרך משתני סביבה: `QR_REST_RL_WINDOW_MS`, `QR_REST_RL_MAX`
- תגובה 429 כש-חורגים, עם headers: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, `Retry-After`

#### Logging

כל בקשה ל-`rest/send_message` נרשמת ב-טבלה `beta_api_logs` עם `uid, msg_id, request, response, status` (sent/failed).

### 7.4 `/api/inbox` – ניהול שיחות

| Method | Path | Auth | תיאור |
|---|---|---|---|
| GET  | `/get_chats` | user | רשימת כל השיחות (with last_message) |
| POST | `/get_convo` body: `{ chatId, page?, limit? }` | user | ההיסטוריה של שיחה |
| POST | `/send_text` | user+plan | שליחת טקסט |
| POST | `/send_image` | user+plan | שליחת תמונה |
| POST | `/send_video` | user+plan | וידאו |
| POST | `/send_doc` | user+plan | מסמך |
| POST | `/send_audio` | user+plan | אודיו |
| POST | `/send_templet` | user+plan | תבנית פנימית |
| POST | `/send_meta_templet` | user+plan | תבנית META |
| POST | `/del_chat` | user | מחיקת שיחה |
| POST | `/merge_chats` | user | מיזוג |
| POST | `/import_convo_from_v3` | user | ייבוא מגרסה ישנה |
| GET  | `/import_chats_from_v3` | user | ייבוא צ'אטים |
| POST | `/webhook/:uid` | – | **Webhook Inbound מ-Meta Cloud API** |
| GET  | `/webhook/:uid` | – | אימות webhook (`hub.challenge`) |
| GET  | `/embed/webhook/:uid` | – | embedded signup callback |
| POST | `/embed/webhook/:uid` | – | embedded signup callback |

### 7.5 `/api/v1` – Public Developer API (Meta Cloud)

> דורש `?token=<api_key>` (לא JWT!). בנוי לאינטגרציה חיצונית עם ה-Meta Cloud API של המשתמש.

| Method | Path | תיאור |
|---|---|---|
| POST | `/send-message` body: `{ messageObject, enableLog? }` | שולח דרך Meta Cloud (POST ל-`https://graph.facebook.com/.../messages`) |
| POST | `/send_templet` body: `{ sendTo, templetName, exampleArr, mediaUri? }` | שליחת תבנית עם משתנים |
| GET  | `/get_logs` (validateUser) | לוגים של שליחות |
| POST | `/delete_logs` (validateUser) body: `{ ids: [] }` | מחיקת לוגים |

### 7.6 `/api/chatbot` – צ'אטבוט מבוסס keywords

| Method | Path | Auth | תיאור |
|---|---|---|---|
| POST | `/add_chatbot` | user+plan | בוט חדש (legacy) |
| POST | `/update_chatbot` | user+plan | עדכון |
| GET  | `/get_chatbot` | user | רשימת בוטים |
| POST | `/change_bot_status` | user+plan | enable/disable |
| POST | `/del_chatbot` | user | מחיקה |
| POST | `/make_request_api` | user+plan | בדיקת בוט עם payload |
| POST | `/add_beta_chatbot` | user+plan | בוט חדש (גרסה beta – משופרת) |
| GET  | `/get_beta_chatbots` | user | רשימה |
| POST | `/change_beta_bot_status` | user | enable/disable |
| POST | `/del_beta_chatbot` | user | מחיקה |

### 7.7 `/api/chat_flow` – ChatFlow ויזואלי

| Method | Path | Auth | תיאור |
|---|---|---|---|
| POST | `/add_new` | user+plan | יצירת flow ריק |
| POST | `/insert_flow_beta` | user+plan | שמירת nodes/edges |
| GET  | `/get_flows_beta` | user+plan | רשימת flows |
| POST | `/del_flow_beta` | user | מחיקה |
| GET  | `/get_mine` | user | flows ישנים |
| POST | `/del_flow` | user | מחיקה ישנה |
| POST | `/get_by_flow_id` | user | קריאה |
| POST | `/get_activity` | user+plan | סטטיסטיקה |
| POST | `/remove_number_from_activity` | user | reset לפי מספר |
| POST | `/session_mine` | user | sessions פעילים |
| POST | `/session_mine_agent` | agent | sessions של סוכן |
| POST | `/enable_chat` / `/disable_chat` | user | הפעלת flow על צ'אט |
| POST | `/enable_chat_agent` / `/disable_chat_agent` | agent | אותו דבר עבור סוכן |
| POST | `/get_beta_flow_sessions` | user | sessions של flow מסוים |
| POST | `/del_flow_sess` / `/del_multiple_flow_sess` | user | ניקוי sessions |
| POST | `/reset_dc_sess` | user | איפוס |
| POST | `/make_request_try_beta` | user | הרצת flow לבדיקה |
| POST | `/try_con` | user | בדיקת חיבור |
| GET  | `/get_origians` | user | רשימת origins מותרים |

### 7.8 `/api/broadcast` – קמפיינים

| Method | Path | Auth | תיאור |
|---|---|---|---|
| POST | `/add_new` | user+plan | יצירת קמפיין |
| GET  | `/get_broadcast` | user | רשימת קמפיינים |
| POST | `/get_broadcast_logs` | user | לוגים |
| POST | `/change_broadcast_status` | user | start/pause |
| POST | `/del_broadcast` | user | מחיקה |
| GET  | `/get_campaigns` | user+plan | קמפיינים פעילים |
| GET  | `/campaigns` | user | רשימה מלאה |
| GET  | `/campaign/:campaignId` | user | פרטי קמפיין |
| GET  | `/dashboard` | user | סטטיסטיקה |
| GET  | `/export/:campaignId` | user | export CSV |
| POST | `/download_csv` | user | הורדת לוגים |
| POST | `/del_campaign` | user | מחיקת קמפיין |

### 7.9 `/api/phonebook` – אנשי קשר

| Method | Path | Auth | תיאור |
|---|---|---|---|
| POST | `/` (add new) | user+contactLimit | יצירת contact list |
| GET  | `/get_by_uid` | user | רשימות שלי |
| PUT  | `/edit_contact` | user | עדכון contact |
| POST | `/del_phonebook` | user | מחיקת רשימה |
| POST | `/...` (import CSV) | user | ייבוא |
| POST | `/...` (add contact) | user+contactLimit | הוספה |
| GET  | `/get_uid_contacts` | user | כל ה-contacts שלי |
| POST | `/del_contacts` | user | מחיקה bulk |

### 7.10 `/api/templet` – תבניות פנימיות

| Method | Path | Auth |
|---|---|---|
| POST | `/add_new` | user+plan |
| GET  | `/get_templets` | user |
| POST | `/del_templets` | user |

### 7.11 `/api/ai` – אינטגרציית AI

| Method | Path | Auth | תיאור |
|---|---|---|---|
| POST | `/...` (configure) | user+plan | הוספת/עדכון מפתח OpenAI/Gemini |
| POST | `/...` (test) | user+plan | בדיקת prompt |

### 7.12 `/api/wa_call` – Voice Calls (WaCall)

| Method | Path | Auth | תיאור |
|---|---|---|---|
| POST | `/insert_flow` | user+plan | שמירת voice flow |
| GET  | `/get_flows` | user+plan | רשימה |
| POST | `/del_flow` | user | מחיקה |
| POST | `/fetch_el_voice` | user | קולות ElevenLabs |
| GET  | `/call_logs` | user | היסטוריית שיחות |
| POST | `/bulk_delete` | user | מחיקת לוגים |
| POST | `/add_in_bot` | user+plan | הוספת בוט קולי |
| GET  | `/get_call_bot` | user+plan | רשימת בוטים |
| POST | `/del_call_bot` | user+plan | מחיקה |
| POST | `/create_broadcast` | user+plan | קמפיין שיחות |
| GET  | `/get_broadcasts` | user+plan | רשימה |
| POST | `/start_calling` | user+plan | התחלת חיוג |
| POST | `/pause_broadcast` | user+plan | השהיה |
| POST | `/delete_broadcast` | user+plan | מחיקה |
| POST | `/update_broadcast_contact` | – | עדכון סטטוס מ-callback |

### 7.13 `/api/telegram` – חיבור Telegram

| Method | Path | Auth | תיאור |
|---|---|---|---|
| POST | `/send_otp` body: `{ phone }` | user | שליחת קוד אימות |
| POST | `/verify_otp` body: `{ phone, code }` | user | אימות וחיבור |
| GET  | `/sessions` | user | רשימת חשבונות מחוברים |
| GET  | `/session_status/:sessionId` | user | סטטוס |
| POST | `/reconnect` | user | חיבור מחדש |
| POST | `/disconnect` | user | ניתוק |
| GET  | `/session/:sessionId` | user | פרטי session |
| GET  | `/delete_session/:sessionId` | user | מחיקה |
| POST | `/send_message` body: `{ sessionId, to, text }` | user | שליחת הודעה |
| GET  | `/chats/:sessionId` | user | רשימת צ'אטים |
| POST | `/get_chats` | user | בקשה עם פילטרים |
| GET  | `/check_status/:sessionId` | user | בדיקת חיבור |
| POST | `/check_multiple_status` | user | bulk |
| GET  | `/health` | – | health check |

### 7.14 `/api/admin` – ניהול מערכת (אדמין בלבד)

עיקרי: `login`, `add_plan`, `get_plans`, `update_plan`, `del_plan`, `get_users`, `update_user`, `del_user`, `auto_login`, `add_brand_image`, `get_brands`, `add_faq/del_faq/get_faq`, `add_page/del_page/get_pages/get_page_slug`, `update_terms`, `update_privacy_policy`, `get_smtp/update_smtp/send_test_email`, `get_dashboard_for_user`, `get_admin/update-admin`, `send_resovery`, `modify_password`, `get_payment_gateway_admin/update_pay_gateway`, `add_testimonial/del_testi/get_testi`, `get_orders/del_order`, `get_contact_leads/del_cotact_entry`, `get_qr_set/update_qr_set`, `get_mobile_app_dt/update_mb`, `update_embed_config`, `get_tele_config/update_tele_config`, `add_flow_template/get_flow_templates/delete_flow_template`, `get_fcm_data/update_fcm_data/get_fcm_subs/send_fcm_manul`, `update_social_login/get_social_login`, `update_rtl`.

### 7.15 `/api/web` – נתונים פומביים + תרגומים + רישוי

| Method | Path | Auth | תיאור |
|---|---|---|---|
| GET  | `/get_all` | – | מידע כללי על ה-frontend |
| GET  | `/return_module` | – | רשימת מודולים זמינים |
| GET  | `/get-one-translation` | – | קובץ תרגום שפה |
| GET  | `/get-all-translation-name` | – | רשימת שפות |
| POST | `/update-one-translation` | admin | עדכון |
| POST | `/add-new-translation` | admin | הוספת שפה |
| POST | `/del-one-translation` | admin | מחיקת שפה |
| POST | `/submit_contact_form` | – | טופס יצירת קשר באתר |
| POST | `/update_web_config` | admin | עדכון הגדרות site |
| GET  | `/check_install` | – | בדיקה אם הותקן |
| GET  | `/get_app_version` | – | גרסה נוכחית |
| POST | `/install_app` | – | wizard התקנה ראשונית |
| POST | `/update_app` | – | עדכון מערכת |
| GET  | `/update_to_be_shown` | – | flag לעדכון זמין |
| GET  | `/get_web_public` | – | כל הנתונים הפומביים (header, footer, brands, ...) |
| GET  | `/get_web_pvt` | – | נתונים פנימיים |
| GET  | `/get_theme` | license | תצורת theme |
| POST | `/save_theme` | admin | שמירת theme |
| POST | `/verify_license` | – | אימות מפתח רישיון |
| POST | `/gen_wa_link` | – | יצירת קישור wa.me |
| POST | `/exchange-token` | – | החלפת token של ספק זהויות |

### 7.16 `/api/theme` – ניהול ערכות עיצוב

ראה [routes/theme.js](wcrm/routes/theme.js) – ~25 endpoints: `list-themes`, `active-theme`, `set-active-theme`, `get-theme/:id`, `create-theme`, `update-theme/:id`, `delete-theme/:id`, `duplicate-theme/:id`, `rename-theme/:id`, `import-theme`, `export-theme/:id`, `reset-to-default`, `get-theme-config`, `update-theme-config`, `update-theme-partial`, `get-theme-section/:section`, `update-theme-section/:section`, `reset-theme-config`, `get-all-theme-sections`, `update-brand-colors`, `update-typography`, `import-theme-legacy`, `get-theme-backups`, `restore-theme-backup/:filename`, `get-theme-metadata`.

### 7.17 `/api/webhook` – Outbound webhooks

ראה סעיף 6.

---

## 8. סכמת בסיס הנתונים

טבלאות מרכזיות (לפי שאילתות בקוד):

| טבלה | שדות עיקריים | שימוש |
|---|---|---|
| `user` | `uid, email, password, name, role, plan, plan_expire, api_key, fcm_data, ...` | משתמשי המערכת |
| `agents` | `id, uid, owner_uid, email, password, allow_send, mask, fcm_data, ...` | סוכנים |
| `instance` | `id, uid, title, uniqueId, number, status, qr, data, other` | חיבורי WhatsApp QR |
| `auth` | `session, id, value` | Baileys creds (mysql-baileys) |
| `meta_api` | `uid, access_token, business_phone_number_id, ...` | מפתחות META Cloud |
| `beta_chats` | `chat_id, uid, sender_name, sender_mobile, last_message, unread_count, origin, origin_instance_id, profile_image, profile, assigned_agent, ticket_status, ...` | צ'אטים פעילים |
| `beta_chat_logs` | `id, uid, chat_id, msg_id, route (INCOMING/OUTGOING), type, msgContext, status, created_at` | היסטוריית הודעות |
| `beta_api_logs` | `id, uid, msg_id, request, response, status` | לוגים של API ציבורי |
| `chatbot` / `beta_chatbot` | flows ישן/חדש | בוטים |
| `chat_flow` / `beta_flow_sessions` | nodes JSON, sessions פעילים | ChatFlow ויזואלי |
| `phonebook` / `phonebook_contacts` | רשימות + אנשי קשר | אנשי קשר |
| `templet` / `meta_templet` | תבניות | תבניות הודעה |
| `broadcast` / `broadcast_logs` / `campaign` | קמפיינים | שליחה המונית |
| `webhook` / `webhook_logs` | URLs + לוגים | Webhooks יוצאים |
| `quick_reply` | תשובות מהירות | קיצורי דרך |
| `warmer_msg` / `warmer` | חימום מספרים | Anti-ban warmup |

---

## 9. קודי שגיאה

המערכת מחזירה תמיד `200 OK` עם `{ success: false, msg: "..." }` במקרי כשל ולא HTTP error codes (חוץ מ-`/api/qr/rest/send_message` שמחזיר `400/401/403/404/500`).

תבנית סטנדרטית:
```json
{ "success": true,  "data": {...}, "msg": "OK" }
{ "success": false, "msg": "Invalid token", "logout": true }
```

`logout: true` מסמן ל-frontend להוציא את המשתמש מהמערכת.

---

## נספח – דוגמת קוד מינימלית לאינטגרציה

### יצירת חיבור WhatsApp + שליחת הודעה דרך REST

```bash
# 1. login
TOKEN=$(curl -s https://api.example.com/api/user/login \
  -H "Content-Type: application/json" \
  -d '{"email":"me@x.com","password":"123"}' | jq -r .token)

# 2. צור instance ובקש QR
curl -s https://api.example.com/api/qr/gen_qr \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title":"My WA","uniqueId":"<uid>_main"}'

# 3. שלוף את ה-QR (data:image/png;base64,...)
curl -s https://api.example.com/api/qr/get_all \
  -H "Authorization: Bearer $TOKEN" | jq '.data[0].qr'

# סרוק עם הטלפון

# 4. צור API key לשליחה חיצונית
API_KEY=$(curl -s https://api.example.com/api/user/generate_api_keys \
  -H "Authorization: Bearer $TOKEN" | jq -r .api_key)

# 5. שלח הודעה
curl -X POST https://api.example.com/api/qr/rest/send_message \
  -H "Content-Type: application/json" \
  -d "{
    \"messageType\":\"text\",
    \"requestType\":\"POST\",
    \"token\":\"$API_KEY\",
    \"from\":\"972501234567\",
    \"to\":\"972549876543\",
    \"text\":\"שלום!\"
  }"
```

### חיבור Socket.IO לקבלת הודעות בזמן אמת

```js
import { io } from "socket.io-client";

const socket = io("https://api.example.com", {
  query: { token: jwtToken }
});

socket.on("connection_ack", (ack) => console.log("Connected", ack));
socket.on("new_message", (msg) => {
  console.log(`New ${msg.type} from ${msg.message?.senderMobile}:`, msg);
});
socket.on("qr_update", ({ uniqueId, qr }) => updateQrUi(uniqueId, qr));
```

---

**קבצי מקור עיקריים לעיון נוסף:**

- [wcrm/app.js](wcrm/app.js) – mount של routes ו-bootstrap
- [wcrm/socket.js](wcrm/socket.js) – Socket.IO
- [wcrm/helper/addon/qr/index.js](wcrm/helper/addon/qr/index.js) – Baileys integration (createSession, sendMessage, init)
- [wcrm/helper/addon/qr/processThings.js](wcrm/helper/addon/qr/processThings.js) – פירוק הודעה נכנסת
- [wcrm/helper/inbox/inbox.js](wcrm/helper/inbox/inbox.js) – `processMessage()` וסיווג סוגי הודעות
- [wcrm/middlewares/user.js](wcrm/middlewares/user.js) – validateUser
- [wcrm/routes/qr.js](wcrm/routes/qr.js) – endpoints QR
- [wcrm/routes/apiv2.js](wcrm/routes/apiv2.js) – Public API
