Skip to content

007 — WhatsApp channel via Baileys (no Cloud API fallback)

Status: Aceita (2026-06-03)

  • BR é whats-first. Casal abre whats 50x/dia; web app de finanças disputa atenção com tudo. WhatsApp não tem concorrente direto pra “agente do casal”.
  • Group chat 1:1 entre o casal + bot é UX nativa pra DDD Household (cenário 005). Ambos members no mesmo chat_id — o canal já modela a granularidade do aggregate.
  • Upload de fatura PDF (cenário 009) já é hábito BR — casal manda extrato por whats hoje. Web upload é fricção; whats upload é gesto natural.
  • CEO doc decisão #9: K-factor 1.5 viral depende de habit diário. WhatsApp tem; web não. Casal não volta diariamente num site novo.
  • Domain (cenários 008/009/012/013) já é stateless e modal-neutro: AgentChat.ask({messages, resourceId, threadId}) com content multimodal opaco (file parts via 009). Só falta adapter de canal pra plugar.
  • ADR 006 (anonymous-first) ancora resourceId em cookie/localStorage no contexto web. Whats-first muda a fonte do resourceId — não muda o contract opaco.

Baileys é o único adapter de canal conversacional em produção. Sem Cloud API (Meta) como fallback, sem Telegram, sem WebSocket próprio.

  • Baileys (@whiskeysockets/baileys) — multi-device, TypeScript, MIT, sem auth oficial Meta. Entra na lista adopt do ADR 001 (atualizar quando o primeiro consumer instalar).
  • Port WhatsAppGateway em src/contexts/agent/application/ — interface limpa: receber eventos de mensagem, enviar mensagem, baixar mídia decriptada.
  • Adapter BaileysAdapter em src/contexts/agent/infrastructure/ — wrapper sobre Baileys; encapsula pair multi-device, reconexão, decrypt de mídia, store de sessão.
  • Test fake MockWhatsAppGateway colocated em src/contexts/agent/infrastructure/ — segue padrão de InMemoryBudgetRepository (ADR 002, gotcha “test fake é adapter legítimo”).
  • Port HouseholdLookup em src/contexts/agent/application/ — mapeia chatId → Household. Bootstrap via bootstrapFromChat({chatId, senderId, referralToken?}) (entry direta whats, ADR 010); composition root resolve.
  • VO WhatsAppMessageEvent em src/contexts/agent/domain/whatsapp/ — normaliza payload Baileys pro shape que AgentChat.ask consome (text + file parts + metadata).
  • resourceId = Household.id. threadId = chatId (DM ou group id whats).
  • Onboarding cria Household no primeiro evento de chatId desconhecido — chat-as-onboarding (ADR 006), versão whats. Sem signup screen.
  • Identidade do member via waid (WhatsApp ID) do remetente. Member.create({name, waid}) quando o agent extrair nome via conversa natural. waid mora no member como identifier externo; Member.id UUID continua sendo a identidade interna (gotcha “Member promovido VO → Entity”).
  • Mapping waid ↔ Member.id fica no caller (composition root), agent continua sem conhecer waid (gotcha “resourceId opaco no contract, mapping fica no caller”).
  1. Baileys emite evento de mensagem → BaileysAdapter normaliza pra WhatsAppMessageEvent.
  2. Composition root resolve chatId → Household.id via HouseholdLookup (ou cria Household se desconhecido).
  3. Composition root chama AgentChat.ask({messages, resourceId: Household.id, threadId: chatId}).
  4. AgentChat retorna resposta (texto + tool-calls opcionais já executados); composition root envia via WhatsAppGateway.send(chatId, text).
  5. Mídia (PDF de fatura, cenário 009) baixada via WhatsAppGateway.downloadMedia(...) antes de empilhar no pendingFiles do AgentChat.ask (gotcha “Multimodal: extrair file bytes per-ask()”).
  • Web (Astro, ADR 005) entrega só: landing PT-BR, share link UX (referral attribution), blog SEO, dashboard read-only futuro.
  • Sem chat UI web em produção. Landing-003 (chat embed teaser) leva pra wa.me/<bot>?text=... em vez de /chat web — ajustar landing-003 quando ADR 007 sair.
  • Chat web do vercel/chatbot starter (ADR 001) fica como scaffold de dev/debug interno, não canal user-facing.
  • Whats specs (wa-001/002/003 planejados) vivem em tier domain (ADR 002) usando MockWhatsAppGateway. Adapter real BaileysAdapter ganha spec colocada em src/contexts/agent/infrastructure/BaileysAdapter.spec.ts sem doc Gherkin (ADR 003), gated por env porque pair multi-device exige QR scan real.
  • Canal nativo do BR — fricção zero pra adoção, alinha com K-factor viral (CEO doc).
  • Group chat 1:1 = Household mapeado pelo canal. Ambos members veem mesma conversa, mesmo state. UX e DDD convergem sem cerimônia.
  • Upload de fatura PDF (009) usa gesto natural do casal. Sem hábito novo pra criar.
  • Domain de agent/ (008–015) intacto. AgentChat stateless já recebe messages/resourceId/threadId opacos — só plugou outro caller.
  • Tools (read 008, write 013), memory (012), multimodal (009) — tudo reaproveitado sem mudança.
  • ADR 006 (anonymous-first) cresce naturalmente: waid substitui cookie UUID como entry identifier; resto do model continua.
  • Custo operacional zero por mensagem (vs Cloud API que cobra por conversa).
  • TOS risk aceito conscientemente: Baileys não é API oficial Meta. Risco real de ban no número do bot. Pair multi-device é instável — QR re-link periódico. Risco de bloqueio em massa se whats apertar.
  • Mitigação única: pair backup + 1-2 números reserva pra rotação manual. Runbook fica em docs/ quando aparecer o primeiro incidente. Sem driver alternativo, sem auto-failover.
  • Cold outreach via whats proibido pelo TOS. Captura de leads passa por landing direct-to-whats CTA (landing-001). Nada de spam pelo número do bot — descumprir = ban garantido.
  • Web some como canal de chat user-facing. Landing-003 muda CTA pra deep-link whats. Aceito: web não tem habit, whats tem.
  • Baileys requer processo Node sempre online segurando socket multi-device. Não é serverless-friendly como Cloud API webhooks. Hosting precisa de container/VM persistente — fricção operacional vs Cloudflare Workers da landing (ADR 005).
  • Decrypt de mídia (PDF) tem custo de CPU + memória no processo do adapter. Aceitável pra volume de fatura mensal por casal.
  • Pair multi-device caduca; recovery exige operador humano scanneando QR. Sem self-service de re-link pra MVP.
  • agent/ core (008–015) não muda. Tools, memory, write-tools, idempotência por operationId (013), forecast (014), alerts (015) — todos intactos.
  • Specs domain existentes continuam verdes. Whats specs entram como wa-* novos, não tocam 008–015.
  • WhatsApp Cloud API oficial (Meta / WhatsApp Business Platform): cobra por conversa (após free tier mensal), exige verificação empresarial (Facebook Business Manager + documentação corporativa), latência maior pra setup inicial, template message approval pra notificações proativas. Custos crescem linearmente com escala de ativações (CEO meta 1k casais ativos em 60d → conversas crescem). Recusado por custo recorrente + setup pesado. Reavaliar se Baileys ban-rate inviabilizar operação.
  • Telegram bot: API oficial, gratuita, polida. Penetração BR ~10-15% vs whats ~98%. Adoção do casal cai dramaticamente — perde a tese “canal onde o casal já está”. Rejeitado.
  • Web app principal (Next.js chat UI como canal primário): friction de “abrir mais um site”. Casal não volta diariamente. iOS Safari sem web push competente (notificação fraca = sem habit). SEO blog ainda vale como funil de waitlist (landing-003), mas chat teaser leva pro whats, não pro web. Rejeitado como canal; mantido como scaffold de dev/debug.
  • iMessage / RCS: fragmentado BR (iMessage só iOS-to-iOS, RCS adoção desigual entre operadoras), sem API estável pra bot. Rejeitado.
  • WebSocket próprio + PWA: reinventar mensageria, sem habit BR, push iOS fraco, install friction. Rejeitado.
  • Baileys — https://github.com/WhiskeySockets/Baileys (multi-device, TS, MIT).
  • ADR 001 — stack adopt/skip (Baileys + unpdf entram; atualizar lista quando primeiro consumer instalar).
  • ADR 002 — test tiers (domain tier pra whats specs com MockWhatsAppGateway).
  • ADR 005 — landing stack (web fica restrita a landing/blog/dashboard read-only).
  • ADR 006 — anonymous-first identity (waid substitui cookie UUID como entry identifier whats).
  • Cenários 008 (chat), 009 (fatura PDF), 012 (memória persistida), 013 (write tools) — domain estável, adapter Baileys consome.
  • Cenários whatsapp wa-001/002/003 (planned, sibling deste ADR — onboarding, fatura, group).
  • docs/CEO.md decisão #9 — landing+whats como primeiro move (ADR 010 supersedes waitlist mechanic); K-factor 1.5 depende de habit; BR é whats-first.