# 019SMS WhatsApp Gateway — Integration Guide

This document describes what we need to add a **new WhatsApp gateway** powered by [019SMS](https://docs.019sms.co.il/whatsapp/) alongside the existing Wizzo (`whatsapp-web`) and Meta Cloud API (`whatsapp`) integrations.

Base URL: `https://019sms.co.il/whatsapp-api`
Auth: `Authorization: Bearer <API_TOKEN>` + `Content-Type: application/json`

---

## 1. Capability Overview

| Feature | Supported |
|---|---|
| Send free-text message (within 24h window) | ✅ |
| Send template (first contact / after 24h) | ✅ |
| Send buttons (up to 3, with optional media header) | ✅ |
| Send list (1–10 items, only as reply) | ✅ |
| Incoming webhook (text/image/audio/ptt/video/document/location/button/list/reaction/order) | ✅ |
| List verified sender phones | ✅ |
| Inbound media delivered as **link** (must be downloaded; deleted after 7 days) | ⚠️ |
| Interactive buttons natively (vs Wizzo text fallback) | ✅ |

**Key rule:** free-text / list / buttons require an **active 24h conversation window** (customer messaged us in last 24h). Otherwise must start with a **template**.

---

## 2. Endpoints

### 2.1 Get verified phones
```
GET /get-whatsapp-phones
→ { success: true, phones: ["972771234567", ...] }
```

### 2.2 Get templates
```
GET /get-phone-templates/:phone/:templateId?
```

### 2.3 Send text message
```
POST /send-whatsapp-message
{
  "source": "972555555555",
  "destination": "972550000000",
  "message": "hello \\n *bold* _italic_ ~strike~",
  "replyTo": ""   // optional: quote an inbound unique id
}
```

### 2.4 Send template
```
POST /send-whatsapp-template
// bodyVariable (1–5), headerLink for image/video/document header
```

### 2.5 Send buttons
```
POST /send-buttons
{
  "source": "...", "destination": "...",
  "header": "1|2|3|4",         // none | image | video | document
  "headerURL": "https://...",  // jpg/png ≤5MB, mp4 ≤16MB, pdf ≤100MB
  "body": "Choose:",
  "footer": "optional",
  "button1": "...", "button2": "...", "button3": "...",
  "buttonExternal": "https://...",  // optional external URL on button1
  "replyTo": ""
}
```

### 2.6 Send list (reply-only, within 24h)
```
POST /send-list
{ source, destination, header?, message, footer?, buttonText,
  itemTitle1..10, itemDescription1..10, replyTo? }
```

### 2.7 Send campaign
```
POST /create-campaign-wa
```

---

## 3. Send Message Response

```json
{
  "success": true,
  "ans": {
    "status": "OK",
    "unique": "HBgMOTcy...==",
    "timestamp": 1735648695,
    "from": 972555555555,
    "to": "972550000000",
    "body": "...",
    "templateTimeLeft": 0,
    "conversationTimeLeft": 0,
    "reason": 1
  }
}
```

### Reason codes
| Code | Meaning |
|---|---|
| 1 | Success |
| 2 | Invalid `from` / JSON syntax |
| 3 | General error |
| 4 | Invalid/empty `to` or non-WhatsApp number |
| 5 | Empty body |
| 6 | WhatsApp service error |
| 7 | >24h since last inbound — must use template |
| 8 | First contact — must use template |
| 9 | Rate limit exceeded |

→ Service must map **reason 7/8** to "fall back to template" logic, and **reason 9** to retry with backoff.

---

## 4. Incoming Webhook (Push API)

Register a public URL in the 019SMS web app. All inbound events arrive as `POST application/json`.

### Payload
```json
{
  "status": "OK",
  "hook": "new",
  "unique": "92E004B950CCEA3C386FCFA2AAF8558A",
  "from": "972552222222",
  "to": "972553333333",
  "senderName": "Niv",
  "type": "text",               // text|image|audio|ptt|video|document|location|button|list|reaction|contacts|order
  "body": "hello",              // text OR media URL OR lat,lng
  "caption": "optional",
  "media": false,
  "isForwarded": false,
  "quote": false,
  "quoteUnique": "",
  "conversationTimeLeft": 1440,
  "referral": { /* FB/IG ad source */ },
  "timestamp": "1600517209",
  "timestampMS": 123
}
```

⚠️ **Media**: `body` is a **link**. Files are **deleted after 7 days** — we must download and persist to GCS immediately on receipt (same pattern as existing `chat-whatsapp.service.ts` does for Wizzo base64).

---

## 5. Implementation Plan in `bina_plus`

Mirror the existing `whatsapp-web` module structure.

### 5.1 New module: `backend/src/modules/whatsapp-019/`
```
whatsapp-019/
├── whatsapp-019.module.ts
├── services/
│   ├── whatsapp-019.service.ts            # HTTP client (send text/template/buttons/list, get-phones)
│   ├── whatsapp-019-messaging.service.ts  # Unified sendText/sendImage/sendDocument/sendButtons
│   └── whatsapp-019-webhook.service.ts    # Parse inbound → ChatWhatsappService
├── controllers/
│   └── whatsapp-019.controller.ts         # POST /whatsapp-019/webhook
├── interfaces/
│   ├── whatsapp-019-request.interface.ts
│   └── whatsapp-019-webhook.interface.ts
└── utils/
    └── phone-normalizer.util.ts           # already exists in whatsapp-web — reuse
```

### 5.2 Env vars (`backend/src/common/config/envConsts.ts`)
```
WHATSAPP_019_API_URL=https://019sms.co.il/whatsapp-api
WHATSAPP_019_API_TOKEN=...
WHATSAPP_019_SOURCE_NUMBER=972...
WHATSAPP_019_WEBHOOK_SECRET=...        # optional, for path obfuscation
```

### 5.3 Webhook endpoint
- Route: `POST /api/whatsapp-019/webhook`
- Validates `hook === "new"` and `status === "OK"`
- For `type` in `[image, video, audio, ptt, document]`:
  1. `GET body` URL → buffer
  2. Upload to GCS (reuse `chat-whatsapp.service.ts:477-540` helper)
  3. For `audio/ptt` → OpenAI transcription (reuse `413-449`)
- Delegates to `ChatWhatsappService.processWhatsappMessage()` — same signature as Wizzo path, so **no change required** in `chat-history`.

### 5.4 Response adapter
Reuse `WhatsappResponseAdapter`, but inject a new messaging service:
- Text → `POST /send-whatsapp-message`
- Image + caption → `POST /send-buttons` with `header=2, headerURL=<gcs_url>` **or** template with media header if outside 24h window
- Blocked-reason / retry → native `send-buttons` (3 buttons) — **upgrade** over Wizzo's text fallback

### 5.5 Gateway selection
Add a per-user or global config flag (new `appConfig` entry or migration v9):
```
whatsappGateway: 'wizzo' | 'meta' | '019sms'
```
`ChatWhatsappService` picks the messaging service based on this flag. Number normalization (`@c.us` → `+972…`) already handled.

### 5.6 24h-window + template fallback
New helper `send019WithFallback(to, message)`:
1. Try `send-whatsapp-message`.
2. If `reason === 7 || reason === 8` → call `send-whatsapp-template` with a pre-approved template id from config.
3. Persist `unique` + `conversationTimeLeft` on the chat document so we can pre-check before sending.

### 5.7 Migration
`migration_v9.ts` (do **not** bump `CURRENT_MIGRATION_VERSION`):
- Add `whatsappGateway` default to appConfig.
- Add `whatsapp019TemplateId` (fallback template) to config.

### 5.8 Testing checklist
- [ ] `get-whatsapp-phones` returns configured source number.
- [ ] Webhook receives text, image, audio, document from a real device.
- [ ] Media link downloaded + stored in GCS within seconds of receipt.
- [ ] Audio transcription path works (matches Wizzo behavior).
- [ ] Outgoing text within 24h window succeeds.
- [ ] Outgoing after window → auto-fallback to template.
- [ ] Buttons rendered natively.
- [ ] Rate-limit (reason 9) triggers backoff/retry.
- [ ] Abort handling: pending message abort works (same pattern as `abortPendingMessages()`).

---

## 6. Differences vs existing gateways

| Concern | Wizzo (whatsapp-web) | Meta Cloud (whatsapp) | **019SMS (new)** |
|---|---|---|---|
| Provider type | 3rd-party whatsapp-web.js | Official Meta API | Israeli reseller of Official |
| Media inbound | base64 in payload | download via media id | **URL link (7-day TTL)** |
| Buttons | text fallback | native | **native** |
| 24h rule enforcement | transparent | explicit | explicit (`reason 7/8`) |
| Templates | n/a | yes | **yes** |
| Auth | bearer (hardcoded) | token + phone_number_id | bearer + `source` per request |
| Webhook verification | path only | `hub.challenge` GET | path only (add secret in URL) |

---

## 7. Open Questions

1. Which WhatsApp number(s) will be provisioned under our 019SMS account? (affects `source`)
2. Template list — do we need Hebrew-approved templates? Meta review can take days.
3. Do we keep Wizzo as primary and use 019SMS as backup, or migrate fully?
4. Billing per-conversation (24h) vs per-message — impacts campaign strategy.
5. Webhook URL must be HTTPS and publicly reachable — confirm Heroku domain is acceptable to 019SMS dashboard.
