Bot & Automation

AI Assistants

/root/hermes-projects/AI Assistants

docs/WHATSAPP_CONTROL_LAYER_TECHNICAL_FLOW.md text
# WhatsApp Control Layer for Hermes Agent

## Tujuan

WhatsApp berfungsi hanya sebagai remote interface resmi untuk Hermes Agent melalui Meta WhatsApp Cloud API. WhatsApp tidak menggantikan Hermes, tidak menjadi dashboard baru, dan tidak membatasi workflow Hermes. Semua analisis, planning, pemilihan tools, skill, memory, eksekusi command, testing, debugging, refactor, dan validasi tetap berada di Hermes Agent.

Target deploy:

```text
/root/hermes-projects/AI Assistants
```

## Prinsip Arsitektur

- WhatsApp adalah transport layer, bukan business brain.
- Hermes tetap autonomous orchestration layer.
- Webhook server harus tipis: validasi, persist, auth, routing, dan delivery.
- Semua aksi berisiko harus melalui approval gate dari WhatsApp.
- Progress, hasil, error, dan artifact harus bisa dikirim balik ke WhatsApp.
- Sistem harus siap untuk multi-channel di masa depan tanpa menulis ulang core Hermes.

## Arsitektur Sistem

Komponen yang direkomendasikan:

- `whatsapp-webhook-api`: FastAPI service untuk webhook verification, inbound events, outbound send API, approval callbacks, dan healthcheck.
- `channel-orchestrator`: service internal yang memetakan pesan WhatsApp menjadi session, task, approval request, dan outbound updates.
- `hermes-bridge`: adapter yang membuat task Hermes, menulis prompt/context, menjalankan Hermes CLI/API, dan menerima progress log.
- `approval-policy`: rule engine sederhana untuk mendeteksi aksi berisiko seperti deploy, publish, delete, config change, secret access, dan external messaging.
- `postgres`: database utama untuk users, sessions, messages, tasks, approvals, files, dan audit logs.
- `redis`: queue, dedupe, progress fan-out, rate-limit, idempotency cache, dan job retry state.
- `artifact-storage`: local volume atau S3-compatible storage untuk file output yang perlu dikirim ulang atau diunduh.
- `meta-whatsapp-cloud-api`: channel resmi untuk inbound dan outbound komunikasi.
- `hermes-agent-runtime`: Hermes yang tetap bekerja normal di workspace dan project root `/root/hermes-projects/...`.

## Diagram Teks

```text
WhatsApp User
  -> Meta WhatsApp Cloud API
  -> Webhook API
     -> Verify Signature + Verify Token
     -> Auth WhatsApp Number
     -> Persist Raw Event
     -> Normalize Message
     -> Channel Orchestrator
        -> Session Resolver
        -> Task Router
        -> Approval Policy
        -> Hermes Bridge
           -> Hermes Agent
              -> Analyse Requirement
              -> Plan
              -> Choose Tools / Skills / Memory
              -> Execute / Test / Debug / Validate
           -> Progress Stream
        -> Result Formatter
     -> Outbound Sender
        -> Meta WhatsApp Cloud API
        -> WhatsApp User
```

## Batas Tanggung Jawab

### WhatsApp Control Layer

- Verifikasi webhook Meta.
- Validasi signature callback.
- Mapping nomor WhatsApp ke user internal.
- Simpan inbound dan outbound event.
- Buat task Hermes dari pesan user.
- Relay progress, approval request, hasil, dan error.
- Menjaga audit trail dan retry delivery.

### Hermes Agent

- Menganalisis intent dan requirement.
- Menentukan planning dan arsitektur.
- Memilih workflow, tools, skills, memory, dan command.
- Mengedit file, menjalankan check, debugging, refactor, dan validasi.
- Menentukan apakah task butuh approval berdasarkan policy signal.
- Menghasilkan artifact, log, dan final summary.

## Alur Webhook Meta WhatsApp

### 1. Verify Callback URL

Tujuan: menghubungkan endpoint webhook ke Meta App.

Flow:

1. Meta mengirim `GET` ke `WHATSAPP_WEBHOOK_URL`.
2. Server membaca query params:
   - `hub.mode`
   - `hub.verify_token`
   - `hub.challenge`
3. Server membandingkan `hub.verify_token` dengan `META_VERIFY_TOKEN`.
4. Jika cocok dan `hub.mode == subscribe`, server mengembalikan plain text `hub.challenge` dengan status `200`.
5. Jika tidak cocok, server mengembalikan `403`.
6. Event verifikasi dicatat ke audit log tanpa menulis token mentah.

Contoh respons sukses:

```text
200 OK
body: <hub.challenge>
```

### 2. Receive Webhook Event

Tujuan: menerima pesan masuk, status delivery, dan event lain dari Meta.

Flow:

1. Meta mengirim `POST` ke endpoint webhook.
2. Server membaca raw body.
3. Server memvalidasi signature `X-Hub-Signature-256` menggunakan `META_APP_SECRET`.
4. Jika signature tidak valid, event ditolak dan dicatat sebagai security incident ringan.
5. Jika valid, payload disimpan ke `webhook_events` untuk audit dan replay.
6. Event dipilah menjadi:
   - `messages`
   - `statuses`
   - `errors`
   - event lain yang tidak diproses saat MVP
7. Event `messages` dinormalisasi menjadi model internal `InboundMessage`.
8. Event `statuses` dipakai untuk update status outbound message.

## Alur Verifikasi Callback URL dan Verify Token

```text
Meta GET webhook
  -> extract hub.mode, hub.verify_token, hub.challenge
  -> compare hub.verify_token with META_VERIFY_TOKEN
  -> if valid and mode=subscribe: return hub.challenge
  -> else: 403 forbidden
  -> log verification attempt with request id, ip, timestamp
```

Aturan:

- `META_VERIFY_TOKEN` hanya dipakai pada proses subscription handshake.
- Jangan kirim balik nilai verify token ke log, response, atau chat.
- Pisahkan `META_VERIFY_TOKEN` dari `META_ACCESS_TOKEN`.

## Alur Menerima Pesan dari WhatsApp

```text
POST webhook
  -> verify signature
  -> persist raw event
  -> idempotency check by wa_message_id
  -> normalize sender number
  -> resolve or create user/session
  -> store inbound message
  -> classify message type
  -> if approval reply: send to approval processor
  -> else: create Hermes task request
  -> enqueue task for Hermes Bridge
```

Detail MVP:

- Terima minimal `text` message lebih dulu.
- Simpan `wa_message_id`, `from`, `timestamp`, `profile_name`, `phone_number_id`, dan raw payload.
- Terapkan idempotency agar webhook retry dari Meta tidak membuat task ganda.
- Tambahkan rate limit per nomor untuk mencegah flood.
- Jika nomor belum terdaftar, lakukan auto-provision user dengan status `pending` atau `active` sesuai kebijakan allowlist.

## Alur Auth Nomor WhatsApp

Model auth yang direkomendasikan:

- `allowlist-first` untuk MVP.
- Nomor yang boleh mengontrol Hermes disimpan di tabel `users`.
- Semua nomor lain ditolak halus dengan pesan akses belum diizinkan.
- Untuk multi-user nanti, tambahkan role:
  - `owner`
  - `operator`
  - `viewer`

Flow:

1. Normalisasi nomor menjadi format E.164 tanpa simbol tambahan.
2. Cari `users.whatsapp_number`.
3. Jika tidak ada:
   - buat audit log,
   - simpan message sebagai `rejected`,
   - kirim pesan penolakan aman.
4. Jika ada tetapi status nonaktif:
   - tolak dengan pesan yang sama.
5. Jika aktif:
   - lanjutkan ke session resolver dan task router.

## Alur Mengirim Pesan ke WhatsApp

Endpoint outbound standar:

```text
POST https://graph.facebook.com/<GRAPH_API_VERSION>/<META_PHONE_NUMBER_ID>/messages
Authorization: Bearer <META_ACCESS_TOKEN>
Content-Type: application/json
```

Payload minimum untuk text:

```json
{
  "messaging_product": "whatsapp",
  "recipient_type": "individual",
  "to": "62812xxxx",
  "type": "text",
  "text": {
    "preview_url": false,
    "body": "Task diterima. Hermes mulai menganalisis kebutuhan."
  }
}
```

Flow:

1. Ambil `META_PHONE_NUMBER_ID` dan `META_ACCESS_TOKEN` dari env.
2. Bentuk payload outbound sesuai jenis message:
   - text
   - interactive button untuk approval
   - document untuk artifact
   - image atau media jika diperlukan
3. Kirim request ke Graph API.
4. Simpan response Meta, termasuk `messages[].id`.
5. Track status delivery via webhook `statuses`.
6. Jika gagal:
   - retry terbatas untuk error transient,
   - tandai permanent failure untuk error auth atau validation,
   - buat audit log.

Catatan implementasi:

- Gunakan version pin, misalnya `v23.0` atau versi Graph API yang aktif saat implementasi.
- Jangan hardcode version di banyak tempat; simpan di `META_GRAPH_API_VERSION`.
- Jika task menghasilkan file besar, kirim link signed URL atau unggah dokumen bila masih dalam batas ukuran Meta.

## Alur Meneruskan Pesan ke Hermes Agent

Adapter `hermes-bridge` harus tipis dan eksplisit.

Flow:

1. `channel-orchestrator` membuat `task_request`.
2. Context yang dikirim ke Hermes mencakup:
   - user identity internal
   - nomor WhatsApp yang sudah diautentikasi
   - pesan user
   - session summary singkat
   - project scope jika user menyebut project
   - approval policy hint
   - preferred response channel: `whatsapp`
3. `hermes-bridge` membuat folder task dan prompt file seperti pola `Hermes-Agent`.
4. `hermes-bridge` menjalankan Hermes CLI atau Hermes API tanpa mengubah workflow internal Hermes.
5. Output log Hermes di-stream ke task store.
6. Progress event diteruskan ke outbound notifier.

Prompt contract yang disarankan:

```text
Source channel: WhatsApp Cloud API
Response channel: WhatsApp
User identity: <internal_user_id>
Approved capabilities: <role/policy>
Need approval before risky actions: yes
Return:
- short acknowledgement
- progress updates when meaningful
- final result
- artifact references
- approval request if needed
```

## Alur Hermes Menjalankan Task

```text
Hermes Task Created
  -> analyse intent
  -> decide whether chat-only or actionable task
  -> if actionable: create plan
  -> choose tools / skills / memory
  -> run commands / edit files / test / debug / validate
  -> emit progress checkpoints
  -> if risky action detected: pause and request approval
  -> on approval: continue
  -> produce final summary + artifacts
```

Prinsip runtime:

- Chat bebas tetap boleh. Tidak semua pesan harus jadi command kaku.
- Shortcut seperti `status`, `log`, `retry`, `cancel`, `deploy`, atau `zip` boleh ada, tetapi opsional.
- Hermes sendiri yang menentukan apakah input adalah tanya jawab ringan, task baru, follow-up, atau approval reply.

## Alur Mengirim Progress, Hasil, dan Error ke WhatsApp

### Progress

Kirim progress hanya untuk checkpoint penting agar tidak spam:

- task diterima
- analisis dimulai
- plan selesai
- eksekusi sedang berjalan
- menunggu approval
- testing atau build gagal
- task selesai

Contoh format progress:

```text
[Task T-20260618-001]
Status: Running
Step: Running tests
Progress: 70%
Project: AI Assistants
```

### Final Result

Final result minimal mencakup:

- ringkasan singkat
- file yang berubah atau artifact yang dibuat
- command/check penting yang dijalankan
- status akhir: `completed`, `failed`, `cancelled`, atau `waiting_approval`
- next step jika ada

### Error

Error flow:

1. Simpan error mentah di log internal.
2. Kirim error yang sudah disanitasi ke WhatsApp.
3. Jangan kirim stack trace, token, path sensitif, atau environment value ke user chat.
4. Tandai task untuk retry manual jika perlu.

## Alur Approval untuk Aksi Berisiko

### Aksi yang Wajib Approval

- deploy ke server
- publish ke production
- kirim email, WhatsApp, Telegram, atau notifikasi keluar
- hapus file atau data
- ubah `.env`, secret, credential, firewall, reverse proxy, DNS, atau service config
- akses data sensitif
- push git, merge, atau release

### Approval Flow

```text
Hermes detects risky step
  -> create approval record
  -> pause task with status waiting_approval
  -> send WhatsApp interactive approval card
  -> user replies Approve or Reject
  -> verify approval token + task binding + actor identity + expiry
  -> if approved: resume task
  -> if rejected: mark task rejected and stop risky branch
```

Approval message yang disarankan:

```text
Approval dibutuhkan
Task: Deploy AI Assistants ke VPS
Reason: Akan menjalankan perubahan pada environment production
Risk: service restart, perubahan config, potensi downtime
Reply: APPROVE T123 atau REJECT T123
```

Aturan approval:

- Approval harus terikat ke `task_id`, `user_id`, dan `expires_at`.
- Approval sekali pakai memakai nonce.
- Hanya role tertentu yang boleh approve.
- Semua approval dicatat di audit log.
- Timeout approval mengembalikan task ke status `cancelled` atau `waiting_user`.

Untuk MVP, gunakan reply text `APPROVE <task_id>` dan `REJECT <task_id>`.
Untuk fase berikutnya, upgrade ke interactive buttons.

## Struktur Database

Database utama: PostgreSQL.

### `users`

```text
id                  uuid pk
whatsapp_number     varchar unique not null
display_name        varchar null
role                varchar not null
status              varchar not null
created_at          timestamptz not null
updated_at          timestamptz not null
last_seen_at        timestamptz null
```

### `sessions`

```text
id                  uuid pk
user_id             uuid fk users.id
channel             varchar not null          -- whatsapp
channel_chat_id     varchar not null          -- wa phone or conversation key
state               varchar not null          -- active, idle, waiting_approval
last_message_at     timestamptz not null
context_summary     text null
created_at          timestamptz not null
updated_at          timestamptz not null
```

### `messages`

```text
id                  uuid pk
session_id          uuid fk sessions.id
user_id             uuid fk users.id
direction           varchar not null          -- inbound, outbound
message_type        varchar not null          -- text, interactive, document, status
wa_message_id       varchar unique null
reply_to_wa_id      varchar null
body_text           text null
payload_json        jsonb not null
status              varchar not null          -- received, queued, sent, delivered, read, failed
created_at          timestamptz not null
```

### `tasks`

```text
id                  uuid pk
user_id             uuid fk users.id
session_id          uuid fk sessions.id
source_message_id   uuid fk messages.id
project_name        varchar null
channel             varchar not null          -- whatsapp
prompt              text not null
status              varchar not null          -- queued, running, completed, failed, waiting_approval
current_step        varchar null
progress_percent    integer not null default 0
hermes_task_ref     varchar null
result_summary      text null
error_summary       text null
created_at          timestamptz not null
started_at          timestamptz null
finished_at         timestamptz null
updated_at          timestamptz not null
```

### `task_logs`

```text
id                  bigserial pk
task_id             uuid fk tasks.id
level               varchar not null
message             text not null
created_at          timestamptz not null
```

### `files`

```text
id                  uuid pk
task_id             uuid fk tasks.id
kind                varchar not null          -- report, zip, image, document, link
storage_path        text null
public_url          text null
mime_type           varchar null
file_size_bytes     bigint null
checksum_sha256     varchar null
created_at          timestamptz not null
```

### `approvals`

```text
id                  uuid pk
task_id             uuid fk tasks.id
requested_by_user_id uuid fk users.id
approved_by_user_id uuid null fk users.id
approval_type       varchar not null          -- deploy, delete, config_change
reason              text not null
risk_summary        text not null
status              varchar not null          -- pending, approved, rejected, expired
approval_code       varchar not null
nonce_hash          varchar not null
requested_at        timestamptz not null
responded_at        timestamptz null
expires_at          timestamptz not null
```

### `webhook_events`

```text
id                  uuid pk
provider            varchar not null          -- meta_whatsapp
event_type          varchar not null
delivery_key        varchar unique not null
signature_valid     boolean not null
payload_json        jsonb not null
received_at         timestamptz not null
processed_at        timestamptz null
```

### `audit_logs`

```text
id                  bigserial pk
actor_type          varchar not null          -- user, system, hermes, meta
actor_ref           varchar not null
action              varchar not null
target_type         varchar not null
target_ref          varchar not null
metadata_json       jsonb not null
created_at          timestamptz not null
```

## Environment Variables

### Required

```env
META_APP_ID=
META_APP_SECRET=
META_BUSINESS_ID=
META_WABA_ID=
META_PHONE_NUMBER_ID=
META_ACCESS_TOKEN=
META_VERIFY_TOKEN=
WHATSAPP_WEBHOOK_URL=
META_GRAPH_API_VERSION=v23.0
DATABASE_URL=
REDIS_URL=
HERMES_COMMAND=/usr/local/bin/hermes
HERMES_WORKSPACE_ROOT=/root
HERMES_PROJECTS_ROOT=/root/hermes-projects
TASK_ARTIFACTS_DIR=/root/hermes-projects/AI Assistants/data/artifacts
TASK_LOGS_DIR=/root/hermes-projects/AI Assistants/data/tasks
WHATSAPP_ALLOWED_NUMBERS=62812xxxx,62813xxxx
APPROVAL_CODE_TTL_SECONDS=900
WEBHOOK_SIGNATURE_REQUIRED=true
```

### Recommended

```env
APP_ENV=production
APP_PORT=8040
LOG_LEVEL=info
ENCRYPTION_KEY=
OUTBOUND_RATE_LIMIT_PER_MINUTE=20
TASK_PROGRESS_THROTTLE_SECONDS=20
SIGNED_URL_TTL_SECONDS=3600
MAX_INBOUND_TEXT_LENGTH=8000
MAX_OUTBOUND_TEXT_LENGTH=4096
```

### Optional

```env
S3_ENDPOINT=
S3_BUCKET=
S3_ACCESS_KEY=
S3_SECRET_KEY=
SENTRY_DSN=
OPENTELEMETRY_ENDPOINT=
```

## Security Flow

### Secret Handling

- Semua token dan secret hanya di environment variable atau secret manager.
- Jangan commit `.env`.
- Sediakan `.env.example` tanpa nilai nyata.
- Rotasi `META_ACCESS_TOKEN` dan `META_APP_SECRET` bila ada indikasi bocor.

### Webhook Security

- Validasi `X-Hub-Signature-256` pada setiap `POST`.
- Simpan raw event terenkripsi atau minimal batasi akses tabel audit.
- Terapkan idempotency pada `wa_message_id` dan `delivery_key`.
- Rate-limit IP dan nomor pengirim untuk mencegah abuse.

### Data Security

- Enkripsi field sensitif saat at-rest jika menyimpan approval context atau signed URL.
- Mask nomor WhatsApp pada log aplikasi.
- Jangan kirim raw exception, command penuh yang sensitif, atau env dump ke WhatsApp.
- Pisahkan log aplikasi, log audit, dan log Hermes.

### Runtime Security

- Jalankan service dengan user terbatas.
- Batasi akses filesystem ke path proyek yang dibutuhkan.
- Semua aksi berisiko harus pause di approval gate.
- Tambahkan outbound allowlist untuk domain Meta Graph API dan dependency penting.

### Operational Security

- Audit semua approval, deploy, delete, config change, dan file download.
- Tambahkan alert untuk:
  - signature mismatch berulang
  - auth failure Meta berulang
  - nomor tak dikenal mencoba akses
  - approval code brute force

## MVP Step-by-Step

Target MVP: sistem bisa menerima pesan WhatsApp resmi, meneruskannya ke Hermes, lalu mengirim hasil Hermes kembali ke WhatsApp.

### Phase 1. Setup Meta dan Infrastruktur

1. Buat project folder di VPS:
   - `"/root/hermes-projects/AI Assistants"`
2. Siapkan Meta Developer App dan webhook subscription untuk WhatsApp.
3. Isi env:
   - `META_APP_ID`
   - `META_APP_SECRET`
   - `META_BUSINESS_ID`
   - `META_WABA_ID`
   - `META_PHONE_NUMBER_ID`
   - `META_ACCESS_TOKEN`
   - `META_VERIFY_TOKEN`
   - `WHATSAPP_WEBHOOK_URL`
4. Siapkan PostgreSQL dan Redis.
5. Siapkan reverse proxy HTTPS untuk webhook public URL.

### Phase 2. Webhook Server

1. Implement endpoint `GET /webhooks/meta/whatsapp`.
2. Implement endpoint `POST /webhooks/meta/whatsapp`.
3. Tambahkan signature validation.
4. Simpan raw webhook event.
5. Tambahkan healthcheck `GET /health`.

Definition of done:

- Meta callback URL berhasil diverifikasi.
- Test message dari WhatsApp masuk ke database.

### Phase 3. Auth, Session, dan Message Store

1. Buat tabel `users`, `sessions`, `messages`, `webhook_events`, `audit_logs`.
2. Implement allowlist nomor WhatsApp.
3. Buat normalizer inbound text message.
4. Tambahkan idempotency check.

Definition of done:

- Nomor di allowlist bisa lolos.
- Nomor non-allowlist ditolak aman.
- Satu webhook retry tidak membuat pesan ganda.

### Phase 4. Hermes Bridge

1. Buat adapter yang membuat task dari inbound message.
2. Gunakan pola task runner seperti `Hermes-Agent/services/hermes_runner.py`.
3. Simpan `tasks` dan `task_logs`.
4. Kirim acknowledgement awal ke WhatsApp:
   - `Task diterima. Hermes sedang menganalisis kebutuhan.`

Definition of done:

- Pesan WhatsApp membuat task Hermes.
- Log Hermes masuk ke store internal.

### Phase 5. Outbound Result Relay

1. Implement sender ke endpoint `/messages`.
2. Kirim progress throttled.
3. Kirim final result.
4. Track delivery status via webhook `statuses`.

Definition of done:

- User mengirim pesan WA.
- Hermes memproses.
- Hasil akhir kembali ke WA.

### Phase 6. Approval Gate

1. Tambahkan rule deteksi aksi berisiko.
2. Simpan approval request ke tabel `approvals`.
3. Implement parser reply:
   - `APPROVE <task_id>`
   - `REJECT <task_id>`
4. Resume task setelah approval valid.

Definition of done:

- Deploy atau delete tidak berjalan tanpa approval.
- Approval dari nomor yang salah ditolak.

### Phase 7. Artifact Delivery

1. Simpan file output ke artifact storage.
2. Kirim dokumen atau signed URL ke WhatsApp.
3. Audit semua artifact download/send.

Definition of done:

- File hasil Hermes bisa diterima user via WA.

## Struktur Project yang Direkomendasikan

```text
AI Assistants/
  apps/
    whatsapp_control_api/
      src/
        api/
          routes/
            health.ts
            meta-whatsapp.ts
            approvals.ts
        core/
          config.ts
          logging.ts
          security.ts
        services/
          inbound-parser.ts
          outbound-sender.ts
          session-service.ts
          approval-service.ts
          hermes-bridge.ts
          task-service.ts
        workers/
          inbound-worker.ts
          outbound-worker.ts
        models/
          message.ts
          task.ts
          approval.ts
    admin_web/
      ... optional future internal dashboard
  packages/
    database/
    shared/
    queue/
  docs/
    WHATSAPP_CONTROL_LAYER_TECHNICAL_FLOW.md
  ecosystem/
    systemd/
  data/
    tasks/
    artifacts/
```

Jika ingin paling konsisten dengan Hermes-Agent saat ini, backend control layer juga bisa dibuat dengan Python `FastAPI` alih-alih TypeScript. Keputusan terbaik adalah mengikuti runtime Hermes yang sudah paling mudah dipasang, di-debug, dan diberi akses ke CLI Hermes di VPS.

## Risiko dan Keputusan Penting

### Keputusan yang direkomendasikan

- Gunakan webhook server terpisah dari Hermes core, tetapi tetap satu project.
- Gunakan PostgreSQL untuk state penting, bukan file JSON saja.
- Gunakan Redis untuk queue dan retry.
- Gunakan approval gate sebelum aksi berisiko.
- Gunakan allowlist nomor untuk MVP.

### Risiko utama

- Retry webhook tanpa idempotency akan membuat task ganda.
- Error auth Meta bisa membuat outbound terlihat sukses padahal gagal.
- Spam progress akan menurunkan usability WhatsApp.
- Menaruh logic Hermes di webhook layer akan merusak boundary arsitektur.
- Menjalankan aksi berisiko tanpa approval akan berbahaya untuk VPS dan data.

## Referensi Implementasi Lokal yang Relevan

- `Hermes-Agent/apps/hermes_telegram_agent/services/hermes_runner.py`
- `Hermes-Agent/apps/hermes_telegram_agent/services/task_store.py`
- `Activity Reminder Bot/apps/activity_reminder_bot/services/meta_whatsapp.py`

## Referensi Resmi

Dokumen resmi Meta yang perlu dicek ulang saat implementasi:

- https://developers.facebook.com/docs/whatsapp/cloud-api
- https://developers.facebook.com/docs/graph-api/webhooks/getting-started
- https://developers.facebook.com/docs/whatsapp/cloud-api/guides/send-messages

Catatan: link resmi di atas perlu diverifikasi kembali terhadap versi Graph API yang aktif saat development, terutama untuk format payload, event types, dan kebijakan messaging window.