{"id":"repo/waha-integration","source":"repo","sourceLabel":"repository root","sourcePath":"WAHA_INTEGRATION.md","routePath":"/fastapp/public-docs/page/repo/waha-integration","rawPath":"/fastapp/public-docs/raw/repo/waha-integration","slug":["repo","waha-integration"],"slugPath":"repo/waha-integration","title":"📱 WAHA Integration — Manual Tecnico","description":"Camada de infraestrutura para envio/recebimento de mensagens WhatsApp via WAHA (https://waha.devlike.pro/). Vive em server/utils/waha.ts + server/api/waha/ — fora do dominio de metadados (App.objects).","headings":["📱 WAHA Integration — Manual Tecnico","1. Posicionamento Arquitetural","2. Configuracao de Ambiente","Subir o servidor WAHA (Docker, dev)","3. Camadas","Funcoes do wrapper (server/utils/waha.ts)","4. Endpoints Publicos (proxy)","Sessions","Mensagens","Webhook","5. Ciclo de Pareamento (primeira vez)","6. Exemplo de Envio (uso futuro pelo Painel de Comunicados)","7. Como o \"Painel de Comunicados\" vai usar isso (segunda fase)","8. Eventos do Webhook (referencia)","9. Limitacoes Conhecidas"],"wordCount":560,"markdown":"# 📱 WAHA Integration — Manual Tecnico\r\n\r\n> Camada de infraestrutura para envio/recebimento de mensagens WhatsApp via WAHA (https://waha.devlike.pro/).\r\n> Vive em `server/utils/waha.ts` + `server/api/waha/*` — fora do dominio de metadados (App.objects).\r\n\r\n---\r\n\r\n## 1. Posicionamento Arquitetural\r\n\r\nWAHA e **Infrastructure Layer**, nao **Metadata Layer**. Segue o mesmo padrao de Prisma, Socket.IO e do `fastapp-agent`:\r\n\r\n```mermaid\r\ngraph TB\r\n    subgraph \"Metadata (App JSON)\"\r\n        OBJ[\"app.objects[]<br/>(comunicado, lead, ...)\"]\r\n        WF[\"app.workflows[]\"]\r\n    end\r\n    subgraph \"Engine\"\r\n        DB[\"dynamic-db.ts\"]\r\n        FADAP[\"FADAP Protocol\"]\r\n    end\r\n    subgraph \"Infrastructure\"\r\n        WAHA[\"server/utils/waha.ts<br/>+ server/api/waha/*\"]\r\n        AGENT[\"fastapp-agent.post.ts\"]\r\n        SOCKET[\"socket.ts\"]\r\n    end\r\n    OBJ --> DB\r\n    WF -->|trigger| WAHA\r\n    OBJ -.referencia.-> WAHA\r\n\r\n    style WAHA fill:#25D366,color:#000\r\n    style OBJ fill:#0891b2,color:#fff\r\n```\r\n\r\nA futura feature de \"Painel de Comunicados\" sera um Object normal em `app.objects[]` (ex: `comunicado` com properties `mensagem`, `target_object`, `scheduled_at`). Quando um comunicado for disparado, o handler chama os endpoints `/api/waha/messages/*` desta camada.\r\n\r\n---\r\n\r\n## 2. Configuracao de Ambiente\r\n\r\n`.env`:\r\n\r\n```env\r\nWAHA_BASE_URL=http://localhost:3000\r\nWAHA_API_KEY=\r\nWAHA_WEBHOOK_TOKEN=\r\n```\r\n\r\n| Variavel | Obrigatorio | Descricao |\r\n|---|---|---|\r\n| `WAHA_BASE_URL` | Sim | URL do servidor WAHA. Self-hosted Docker default `http://localhost:3000`. |\r\n| `WAHA_API_KEY` | Recomendado | Header `X-Api-Key` enviado em toda chamada. Configure tambem no servidor WAHA via `WHATSAPP_API_KEY`. |\r\n| `WAHA_WEBHOOK_TOKEN` | Recomendado | Token compartilhado para validar webhooks recebidos. Configure no painel WAHA ao definir o hook URL. |\r\n\r\nMapeado em `nuxt.config.ts` como `runtimeConfig.waha.*` — **server-only**, nunca exposto ao cliente.\r\n\r\n### Subir o servidor WAHA (Docker, dev)\r\n\r\n```bash\r\ndocker run -it --rm \\\r\n  -p 3000:3000/tcp \\\r\n  -e WHATSAPP_API_KEY=sua-chave-aqui \\\r\n  --name waha \\\r\n  devlikeapro/waha\r\n```\r\n\r\nAcesse `http://localhost:3000/dashboard` para criar a primeira session.\r\n\r\n---\r\n\r\n## 3. Camadas\r\n\r\n```\r\nserver/\r\n├── utils/\r\n│   └── waha.ts                              # Wrapper HTTP + tipos + helpers\r\n└── api/\r\n    └── waha/\r\n        ├── sessions/\r\n        │   ├── index.get.ts                 # GET    listar sessions\r\n        │   ├── index.post.ts                # POST   criar/iniciar session\r\n        │   ├── [name].get.ts                # GET    status detalhado\r\n        │   ├── [name].delete.ts             # DELETE logout (apaga creds)\r\n        │   └── [name]/\r\n        │       ├── stop.post.ts             # POST   para session\r\n        │       └── qr.get.ts                # GET    QR code para parear\r\n        ├── messages/\r\n        │   ├── text.post.ts                 # POST   enviar texto\r\n        │   └── image.post.ts                # POST   enviar imagem\r\n        ├── check-number.get.ts              # GET    validar se numero existe no WhatsApp\r\n        └── webhook.post.ts                  # POST   recebe eventos do servidor WAHA\r\n```\r\n\r\n### Funcoes do wrapper (`server/utils/waha.ts`)\r\n\r\n| Funcao | Descricao |\r\n|---|---|\r\n| `waha.listSessions()` | Lista todas as sessions do servidor |\r\n| `waha.getSession(name)` | Status detalhado de uma session |\r\n| `waha.startSession(name, config?)` | Cria/inicia session (proximo passo: ler QR) |\r\n| `waha.stopSession(name)` | Para session (mantem credenciais) |\r\n| `waha.logoutSession(name)` | Logout completo (apaga credenciais) |\r\n| `waha.getQr(name, format?)` | QR code para parear (`image` base64 PNG ou `raw`) |\r\n| `waha.sendText(session, payload)` | Envia texto |\r\n| `waha.sendImage(session, payload)` | Envia imagem (URL ou base64) |\r\n| `waha.sendFile(session, payload)` | Envia arquivo generico |\r\n| `waha.checkNumber(session, phone)` | Valida se numero tem WhatsApp |\r\n| `phoneToChatId(phone, cc?)` | Helper: `\"11999999999\"` -> `\"5511999999999@c.us\"` |\r\n| `isWahaConfigured()` | Verifica se ambiente esta minimamente setado |\r\n\r\n---\r\n\r\n## 4. Endpoints Publicos (proxy)\r\n\r\nTodos os endpoints sao **server-side proxies** — a API key WAHA nunca sai do servidor.\r\n\r\n### Sessions\r\n\r\n| Rota | Metodo | Body / Query | Retorno |\r\n|---|---|---|---|\r\n| `/api/waha/sessions` | GET | — | `WahaSession[]` |\r\n| `/api/waha/sessions` | POST | `{ name, config? }` | `WahaSession` (status inicial) |\r\n| `/api/waha/sessions/:name` | GET | — | `WahaSession` |\r\n| `/api/waha/sessions/:name` | DELETE | — | logout |\r\n| `/api/waha/sessions/:name/stop` | POST | — | para session |\r\n| `/api/waha/sessions/:name/qr` | GET | `?format=image\\|raw` | QR code |\r\n\r\n### Mensagens\r\n\r\n| Rota | Metodo | Body | Notas |\r\n|---|---|---|---|\r\n| `/api/waha/messages/text` | POST | `{ session, chatId? \\| phone?, text, linkPreview?, reply_to? }` | Pelo menos um entre `chatId` e `phone` |\r\n| `/api/waha/messages/image` | POST | `{ session, chatId? \\| phone?, file: { url } \\| { mimetype, filename, data }, caption? }` | Aceita URL publica ou base64 |\r\n| `/api/waha/check-number` | GET | `?session=&phone=` | Util para limpar listas antes de campanhas |\r\n\r\n### Webhook\r\n\r\n| Rota | Metodo | Descricao |\r\n|---|---|---|\r\n| `/api/waha/webhook` | POST | Receptor de eventos (mensagens recebidas, ack, status). Validado via `?token=` ou header `X-Webhook-Token`. |\r\n\r\n---\r\n\r\n## 5. Ciclo de Pareamento (primeira vez)\r\n\r\n```mermaid\r\nsequenceDiagram\r\n    participant Admin\r\n    participant API as /api/waha\r\n    participant WAHA as Servidor WAHA\r\n    participant WhatsApp\r\n\r\n    Admin->>API: POST /sessions { name: \"default\" }\r\n    API->>WAHA: POST /api/sessions\r\n    WAHA-->>API: { status: \"STARTING\" }\r\n    API-->>Admin: status\r\n\r\n    loop ate WORKING\r\n        Admin->>API: GET /sessions/default/qr\r\n        API->>WAHA: GET /api/default/auth/qr\r\n        WAHA-->>API: PNG base64\r\n        API-->>Admin: QR code\r\n        Admin->>WhatsApp: escaneia\r\n        WhatsApp->>WAHA: pareado\r\n        Admin->>API: GET /sessions/default\r\n        API-->>Admin: { status: \"WORKING\" }\r\n    end\r\n```\r\n\r\n---\r\n\r\n## 6. Exemplo de Envio (uso futuro pelo Painel de Comunicados)\r\n\r\n```ts\r\n// Server-side handler do \"comunicado\", em segunda fase:\r\nawait $fetch('/api/waha/messages/text', {\r\n    method: 'POST',\r\n    body: {\r\n        session: 'default',\r\n        phone: lead.whatsapp,           // sera normalizado via phoneToChatId\r\n        text: comunicado.mensagem\r\n    }\r\n});\r\n```\r\n\r\n---\r\n\r\n## 7. Como o \"Painel de Comunicados\" vai usar isso (segunda fase)\r\n\r\nA feature sera construida **dentro** do padrao Fastapp:\r\n\r\n1. **Object** `comunicado` em `app.objects[]` com properties:\r\n   - `titulo` (string), `mensagem` (string, multiline)\r\n   - `target_object` (string — qual entity recebe, ex: \"lead\")\r\n   - `target_filter` (json — filtros tipo `{ status: \"ATIVO\" }`)\r\n   - `channel` (enum: `whatsapp` | `email`) — preparado para multi-canal\r\n   - `waha_session` (string — qual session usar)\r\n   - `scheduled_at` (datetime)\r\n   - `status` (enum: `draft` | `scheduled` | `sending` | `sent` | `failed`)\r\n\r\n2. **Page** `comunicados` em `app.pages[]` (CRUD dinamico, gerado pelo motor)\r\n\r\n3. **Workflow** opcional para aprovacao multi-step\r\n\r\n4. **Disparador** (server handler novo, ex: `/api/comunicado/[id]/dispatch.post.ts`):\r\n   - Le o objeto `comunicado`\r\n   - Resolve a lista de `lead`s pelo `target_filter`\r\n   - Para cada lead: `waha.sendText(session, { chatId: phoneToChatId(lead.whatsapp), text: comunicado.mensagem })`\r\n   - Atualiza `status` e gera log por destinatario\r\n\r\nA camada WAHA atual **nao precisa mudar** para isso ser feito — ela ja expoe tudo que e necessario.\r\n\r\n---\r\n\r\n## 8. Eventos do Webhook (referencia)\r\n\r\nWAHA empurra eventos como:\r\n\r\n| Evento | Disparo |\r\n|---|---|\r\n| `message` | Mensagem recebida (texto, midia, etc) |\r\n| `message.ack` | Ack de entrega/leitura |\r\n| `session.status` | Mudanca de estado da session |\r\n| `state.change` | Mudanca de estado WhatsApp |\r\n| `group.join` / `group.leave` | Participacao em grupo |\r\n\r\nConfigure no WAHA via env vars do container:\r\n\r\n```env\r\nWHATSAPP_HOOK_URL=https://creator.fastapp.cloud/api/waha/webhook?token=SEU_WEBHOOK_TOKEN\r\nWHATSAPP_HOOK_EVENTS=message,message.ack,session.status\r\n```\r\n\r\nA logica de roteamento de eventos e um TODO da segunda fase — o handler atual valida o token e loga.\r\n\r\n---\r\n\r\n## 9. Limitacoes Conhecidas\r\n\r\n- Esta camada **nao** persiste mensagens enviadas/recebidas. A persistencia (historico, threads) sera feita pelo Object `comunicado` na segunda fase.\r\n- Sem rate limiting proprio — confie no rate limit do servidor WAHA + cuidado com WhatsApp ban (recomendado: ~1 msg/segundo por session).\r\n- `phoneToChatId` assume Brasil (`+55`) por padrao — passe o segundo arg para outro DDI.\r\n\r\n---\r\n\r\n**Status**: Infraestrutura pronta. Painel de Comunicados (segunda fase) ainda nao implementado.\r\n**Branch**: `feat/Waha`\r\n"}