# OnSMS - Plataforma Operacional

## Ambiente atual (Claude Code / servidor dedicado)
- Host: 69.6.215.231 (SSH porta 22022, usuário onsmscom)
- Domínio: portal.onsms.com.br
- **Esta pasta é DESENVOLVIMENTO:** /home/onsmscom/portal.onsms.com.br/dev/portalonsms2
  Processo PM2: `app-dev`
- **PRODUÇÃO (não mexer a partir daqui):** /home/onsmscom/portal.onsms.com.br/portalonsms2
  Processo PM2: `onsms`
- Repositório: GitHub texugodomel/portalonsms2 (branch main = produção)

## Regras importantes
- Você tem acesso TOTAL (ler, editar, rodar comandos) apenas em:
  /home/onsmscom/portal.onsms.com.br/dev/portalonsms2
- Você pode LER (nunca editar/apagar/reiniciar processo) em produção:
  /home/onsmscom/portal.onsms.com.br/portalonsms2 (processo PM2: onsms)
- Você NÃO tem acesso a: portalonsms (versão antiga), public_html, onsms.com.br,
  ivr.onsms.com.br, ou qualquer arquivo .sql na home. Se precisar de algo nessas
  áreas, pare e avise o usuário em vez de tentar acessar.
- Toda mudança de código só acontece na pasta de dev. Produção só recebe
  atualização via GitHub (PR + merge + deploy), nunca editada diretamente.

## Comandos úteis
- `npm run dev` — sobe o servidor de desenvolvimento
- `pm2 restart app-dev` — reinicia o processo de dev
- `pm2 list` — confere status dos processos (dev e prod)
- `npm test` — roda os testes
- `git status` / `git log --oneline -10` — estado do repositório

---

## Overview
OnSMS é uma plataforma operacional que faz a intermediação entre o sistema principal OnSMS e ferramentas externas de mensageria WhatsApp. Funções principais: gerenciar e executar campanhas de WhatsApp, gerar pacotes operacionais para operadores, processar e normalizar relatórios de operadores, retornar resultados agregados à plataforma principal via API. Além das campanhas de saída, a plataforma valida números WhatsApp antes do disparo, provisiona novos remetentes (chips), e responde autonomamente mensagens WhatsApp recebidas via Agente de IA. Inclui módulo SaaS para gestão de usuários, serviços, contas e créditos.

## Preferências do usuário
- Comunicação clara e concisa
- Código modular e de fácil manutenção
- Desenvolvimento iterativo, com atualizações regulares de progresso
- Pedir confirmação antes de mudanças arquiteturais grandes
- Tema visual verde/teal (inspirado no WhatsApp)
- Idioma da aplicação: português brasileiro (pt-BR)
- Fonte principal: Inter

## Arquitetura

### Tecnologias principais
- **Frontend**: React, TypeScript, Vite, shadcn/ui, Tailwind CSS, TanStack Query
- **Backend**: Express.js com Node.js
- **Banco de dados**: PostgreSQL com Drizzle ORM
- **Provedores de IA**: abstração plugável (`server/aiProvider.ts`) suportando múltiplos vendors de LLM (OpenAI, Anthropic, etc.) pro Agente de IA

### Modelos de dados e módulo SaaS
Módulo SaaS gerencia usuários com papéis hierárquicos (ADMIN, REVENDA, CLIENTE, OPERADOR, USUARIO), tipos de serviço (manual/API com regras de cobrança distintas), vínculos usuário-serviço, saldos de conta e transações de crédito. Campanhas têm UUIDs internos e identificadores externos. Mídia armazenada de forma eficiente.

### Gestão de workflow
Gerencia todo o ciclo de vida da campanha: geração de pacotes ZIP operacionais, atribuição de campanhas, upload de relatórios, processamento com normalização de status (`enviado`/`nao_enviado`), estatísticas finais. Campanhas também podem ser agendadas para execução futura.

### Tipos de serviço (`tipoWhatsapp`)
- **`WA OFICIAL` (`wa_oficial`)**: API oficial WhatsApp Business via Infobip, templates aprovados pela Meta. Roda como `api` (disparo automático direto) ou `manual` (geração de ZIP). Arquitetura multi-sender com seleção de pool por qualidade (Alta/Média/Baixa) e limites diários.
- **`WA OFF` (`wa_off`)**: Ferramentas de terceiros/não-oficiais. Sempre `manual` — sistema gera pacote ZIP (contatos, mensagem, mídia, perfil) pro operador; cobrança finalizada após upload do relatório.
- **`WA HUB` (`wa_hub`)**: Modo híbrido usando templates WABA oficiais disparados manualmente via ZIP. CSV do ZIP carrega múltiplas colunas (`phone, var1, var2...`) casando com variáveis do template.
- **`WA MULTI-BM` (`wa_multibm`)**: Disparo em alta escala entre ~100 Business Managers sob uma única conta Infobip compartilhada. Disparo `api` direto com throttler por segundo embutido. Ver seção dedicada abaixo.
- **`RCS` (`rcs`)**: RCS Business Messaging via MKOM/MKChannels. Disparo `api` com subtipos `basic` (texto), `midia` (mídia única) e `carrossel` (rich cards).
- **`Verificador WA` (`wa_filter`)**: Serviço standalone pra verificar se números têm conta WhatsApp ativa via Checknumber.ai. Ver seção dedicada abaixo.
- **Provisionamento de chip (`sender`, `chip_f2`, `chip_f3`)**: Provisionamento/ativação de remetentes WhatsApp ("chips") via GlobalSIM (F1/F3) e TotalSMS (F2), usado pra gerar novos números remetentes.

### Integração HubSpot
Suporta OAuth e Private App, ação customizada de workflow para envio de WhatsApp, pipeline de disparo compartilhado com tratamento de erro e fila, publica Timeline Events, cobrança de créditos, proteção contra disparo duplicado, alertas de e-mail configuráveis. Status terminais do WhatsApp (DELIVERED/SEEN/FAILED) via webhook Infobip são repassados ao HubSpot Timeline (idempotência por `{status}:{messageId}`, traduzido pra PT-BR). Mudanças no Event Type Template exigem re-registro via `/api/admin/hubspot/timeline/register-template`. Ações de workflow HubSpot podem ser vinculadas a um preset de template no nível da conta (ver Presets de Template).

### API Token por Serviço (API de entrada)
API REST permite que sistemas externos criem campanhas usando tokens vinculados a serviço, com endpoints de listagem de templates e criação de campanhas com variáveis por contato. Campanhas de entrada tipo API exigem template; disparo automático condicionado a `campaign.templateId`. O gate `permitirMarketing` e presets de template também se aplicam ao fluxo da API de entrada.

### Integração Infobip WA OFICIAL CRM
Gerencia credenciais Infobip, múltiplos remetentes, aprovação de templates. Webhook processa eventos de entrega. Templates suportam variáveis `{{N}}`. Checagem de saldo Infobip. Conteúdo de campanha registrado (snapshot) para auditoria. Job automatizado sincroniza templates. Mídia de header no GCS. Ciclo de vida: `pending → processing → completed`, fallback de 24h pra campanhas travadas. Botões de template (URL + QUICK_REPLY), validação de contagem de placeholders. Registro de webhook automatizado via API.

### Serviço WA Multi-BM
`wa_multibm` orquestra disparo entre ~100 Business Managers sob uma única conta Infobip compartilhada por serviço. Credenciais (`apiKey`, `baseUrl`, `messagesPerSecond` opcional) ficam em `multibm_configs`; BMs individuais guardam só seus próprios identificadores (WABA/senders), nunca credenciais. Webhook central (`/webhooks/infobip`), token global (`api_settings.infobip_webhook_token`) — roteamento por `messageId`. `multibm_configs.webhookSubscriptionId` evita duplicação de subscrição. Rotas admin: `GET /api/admin/multibm/webhook/:serviceId/status`, `POST /api/admin/multibm/webhook/:serviceId/register`.

#### Atribuição por sender (não por BM)
Atribuição cliente↔recursos é **por sender** (`bm_sender_assignments`, único `sender_id+user_id`). Admin escolhe por BM quais senders cada cliente pode usar. Mesma BM reutilizável entre clientes com subconjuntos disjuntos de senders. Disparo (`buildSenderPool`) usa só senders atribuídos ao dono da campanha (`getBmSendersForUser`). Substituição de atribuições escopada por serviço (`setBmSenderAssignmentsForUserInService`). Tabela legada `bm_client_assignments` mantida só como fonte de migração. Rotas admin: `GET/PUT /api/admin/multibm/clients/:userId/sender-assignments`.

#### Template Cluster (preencher variáveis uma única vez)
Modelo Multi-BM é **cluster-only** — sem template principal/alternativo por cliente. Templates físicos vêm só da importação Infobip por BM. Admin cria **Template Base** manualmente (assinatura/prévia mestra) e agrupa submissões compatíveis sob esse Base.

`multibm_templates` = Template Base (esqueleto puro): assinatura (`varCount` + `buttonUrlPrefix` + `headerType`) + prévia mestra. Nunca persiste rodapé/header real. Conteúdo físico de cada BM vive em `multibm_template_submissions`. Cliente preenche variáveis uma única vez, disparo resolve conteúdo físico de cada BM. BM incompatível com a assinatura é excluída do pool (log `campaign_multibm_signature_incompatible`). Rotas admin: coverage, group, ungroup. Helpers em `server/multibmSignature.ts`.

##### Mídia de cabeçalho na campanha
Quando o Base marca cabeçalho de mídia (imagem/vídeo/documento), o `headerType` faz parte da assinatura do cluster. Cliente envia uma mídia por campanha (imagem JPG/PNG ≤5MB, vídeo MP4 ≤16MB, documento PDF ≤100MB) via `POST /api/multibm/campaigns/upload-media` (Object Storage/CDN). URL persistida em `campaign.headerImageUrl`, injetada no disparo pra todas as BMs elegíveis.

### Agente de IA (Respostas Automáticas a Mensagens Recebidas / MO)
Orquestrador autônomo de IA (`server/aiAgent.ts`) responde mensagens WhatsApp recebidas (MO — mobile originated).
- **Entrada e fluxo**: eventos MO chegam pelo webhook compartilhado `POST /webhooks/infobip` e são despachados fire-and-forget (`setImmediate`) pra `handleInboundMessage`. Orquestrador roda gates sequenciais (Global → Serviço → Cliente → Guard) antes de gerar resposta e enviar de volta via Infobip.
- **Sessão "sticky"**: respostas enviadas pelo mesmo número remetente que recebeu o MO, preservando a janela de serviço de 24h do WhatsApp.
- **Persona e Base de Conhecimento (RAG)**: usa base de conhecimento específica do cliente com busca textual (não-embedding) pra injetar contexto relevante no system prompt. Tabelas: `ai_conversations`, `conversation_messages`, `knowledge_base`, `ai_agent_configs`.
- **Campanhas em duas etapas**: suporta mensagem principal (texto/mídia) enviada no primeiro contato antes da IA assumir.
- **Modo manual (operador assume)**: quando um operador humano assume a conversa, a IA para de responder automaticamente.
- **Provedores de IA**: model-agnostic via `server/aiProvider.ts` (OpenAI, Anthropic, etc.).
- **Atribuição do dono (owner mapping)**: atribuição "sticky" baseada na última interação de saída. Em um MO de entrada, o sistema busca o disparo mais recente pra esse contato daquele número remetente em `waba_messages` (WABA: owner direto) e `multibm_messages` (Multi-BM: owner resolvido via campanha). Owner normalizado pro nível pai/cliente quando é sub-usuário (operador).
- **Deduplicação (idempotência)**: trata retransmissões de webhook via `infobipMessageId` do vendor como chave única, checagem atômica de existência antes de processar, índice parcial único em `conversation_messages.infobip_message_id` (ids vazios armazenados como null).
- **Guardas de segurança (`server/messageGuard.ts`)**: guard "anti-double-message" compatível com Meta — nunca envia duas mensagens automáticas de formato livre seguidas; nova mensagem `USER` de entrada reseta `lastAiMessageAt`. Verifica janela de serviço configurável (padrão 24h) e aplica limite de respostas por contato (`maxRepliesPerContact`); no limite, envia fallback padrão único e marca conversa `LIMIT_REACHED`. Todo envio automático de formato livre passa pelo guard único compartilhado `canSendAutomaticFreeForm`.
- **Isolation contract (não relaxar)**: o system prompt do agente é composto por EXATAMENTE duas fontes escopadas por conta — Base de Conhecimento do cliente e Company Profile (abaixo). Nenhum outro dado da conta (credenciais de login, CPF/CNPJ, saldos) é lido pro prompt. Ver `.agents/memory/ai-agent-isolation-contract.md`.

### Company Profile
`client_business_profiles` (1:1 por `userId`) guarda o que o CLIENTE preenche sobre o próprio negócio: segmento, produtos/serviços, diferenciais, público-alvo, horário, endereço, redes sociais, formas de pagamento, políticas e tom de voz — mais `aiUseProfile` pra ligar/desligar o uso pelo Agente de IA. `buildProfileSection()` (`server/aiAgent.ts`) monta o bloco injetado no prompt entre as instruções base e a Base de Conhecimento; retorna `null` se `aiUseProfile=false` ou nada preenchido (tom de voz/instruções extras contam como conteúdo válido mesmo sozinhos). Rotas: `GET/PUT /api/profile/business`. Detalhe completo: `.agents/memory/company-business-profile.md`.

### Templates Validados (Multi-BM)
Fluxo exclusivo do Multi-BM para composição manual completa de um template (estilo Meta, com exemplos por variável), isolado em `multibm_validated_templates` — nunca reaproveita o esqueleto de `multibm_templates`. Máquina de estados `RASCUNHO → EM_VALIDACAO → VALIDADO/REJEITADO`: submete primeiro numa BM principal pra validação da Meta; só com status `VALIDADO` libera o rollout em massa (sequencial, respeitando rate-limit Infobip, pulando BMs já submetidas ou sem sender ativo). Rotas: `POST .../validated-templates/:id/validate`, `.../sync-status`, `.../rollout`. Regras completas: `.agents/memory/multibm-validated-templates.md`.

### Verificação de Números WhatsApp (Checknumber.ai)
Validação de número WhatsApp integrada com a API Checknumber.ai pra verificar se números têm conta WhatsApp ativa. Fluxo assíncrono em três etapas: submeter lista texto puro (`+CódigoPaísNúmero`) via `multipart/form-data` e receber `task_id`; consultar status até estado `exported` com `result_url`; baixar ZIP (S3/CloudFront), extrair `all.csv`, parsear `yes`/`no` por número. Downloads de resultado restritos a hosts confiáveis (`checknumber.ai`, `amazonaws.com`) pra prevenir SSRF. Tarefas Checknumber não expiram após conclusão; tratamento de 404 é defensivo pra IDs inválidos, não pra tarefas concluídas. Dois contextos:
- **Filtro integrado à campanha**: limpa lista de contatos de uma campanha. Endpoints: `POST /api/campaigns/:id/wa-filter/start`, `GET /api/campaigns/:id/wa-filter/status` (auto-importa resultados e atualiza flag `whatsapp_active` em `contacts`), `POST /api/campaigns/:id/wa-filter/force-check`. Disparo então mira só contatos com `whatsapp_active = true`.
- **Serviço standalone `wa_filter`**: processamento em lote pra Admins/Revendas. Endpoints: `POST /api/wa-filter-service/batches` (upload `.txt`/`.csv`), `GET /api/wa-filter-service/batches/:id/status`, `POST /api/wa-filter-service/batches/:id/verify`, `GET /api/wa-filter-service/batches/:id/download` (só números válidos). Cobra 1 crédito por número submetido e reembolsa automaticamente créditos de números sem WhatsApp (`total_submitted - valid_whatsapp`). Resultados em `wa_filter_service_batches`.
- **Config global**: `GET /api/settings/checknumber-configured` reporta se a API key global está definida. Cada etapa registrada em `activity_logs`.

### Presets de Template
Presets pré-preenchem variáveis de template (`{{1}}, {{2}}...`) e mídia de header obrigatória, pra operadores/integrações não digitarem de novo:
- **Presets de serviço**: Admin define valores fixos no nível do serviço.
- **Presets de conta**: usuários criam múltiplos presets nomeados por template. Usados por ações de workflow HubSpot, que mapeiam um preset específico pra uma ação. Presets também se aplicam a campanhas criadas via API de entrada.

### Agendamento de Campanhas
Campanhas suportam horário de execução futuro via `scheduledAt` (ISO 8601). Sistema trata o atraso antes de disparar pro provedor ou disponibilizar o ZIP pro operador. Formatação de data feita via helpers do client com ErrorBoundary global e normalização backend `normalizeScheduledAt`.

### Diagnósticos operacionais
Endpoints administrativos pra gestão de token de webhook Infobip, diagnóstico de status comparando dados locais com Infobip Logs API, força conclusão de campanhas WA OFICIAL travadas (janela de segurança de 60min). Logs de atividade por contato pra mensagens Infobip falhadas, logs detalhados de requisições de envio e eventos de webhook.

### Gestão de remetentes e limites diários
Interface admin centraliza configuração de remetentes Infobip, incluindo `dailyLimit` e status `preferred`. Pipeline de disparo respeita limites, enfileirando mensagens quando excede capacidade, worker processa mensagens pendentes com mecanismo de retry. Sender Infobip preferido exigido só pra serviços `tipo="api"`; serviços manuais nunca precisam.

### Gestão de serviços
Interface admin com CRUD completo pra serviços: tipo, detalhes de integração, regras de cobrança, filtro de template (`templateFiltro`, restringe templates WABA visíveis por substring de nome), status ativo, flag `modoTeste` pra serviços tipo API.

### Configuração de API
Interface web pra configurar callback URLs, API Keys, timeout e retry pra integração com a plataforma principal OnSMS.

### Segurança
Autenticação por sessão, hash bcrypt de senhas, controle de acesso por papel (ADMIN, REVENDA, CLIENTE, OPERADOR). Bloqueio de conta, rate limiting, rotas de API protegidas. Upload de relatórios validado por tipo e tamanho de arquivo. Fetches server-side de URLs fornecidas pelo usuário resolvem DNS e bloqueiam faixas de IP privado (proteção SSRF), rotas públicas de mídia protegidas contra path traversal.

### Processamento de relatórios
Processa relatórios CSV/TXT, lidando com separadores e formatos de hora variados. Normaliza números de telefone e padroniza status, preservando detalhes originais. Duplicatas tratadas priorizando a última ocorrência.

### Armazenamento de mídia
Mídia de campanha (banners, fotos de perfil, vídeos) gerenciada por `server/mediaStorage.ts`, com API unificada. Padroniza em caminho de objeto normalizado (`imageObjectPath`, ex: `/objects/campaign-media/...`), servido via `/api/object-media/*`, persistindo em diretório local durável configurado via `MEDIA_UPLOAD_DIR` (fallback pra `./uploads`). Mídia de header de campanha Multi-BM fica em Object Storage/CDN porque a URL precisa ser acessível pelos servidores Meta/WhatsApp.

## Dependências externas
- **PostgreSQL**: banco relacional principal
- **WhatsApp Business API**: pra tipo de serviço "WA OFICIAL"
- **Ferramentas de terceiros WhatsApp**: pra "WA OFF"
- **Plataforma OnSMS**: sistema principal, recebe resultados via callback API
- **HubSpot API**: integração CRM e automação de workflow
- **Infobip API**: mensageria WhatsApp Business API e gestão de templates
- **Checknumber.ai API**: validação/filtragem de número WhatsApp
- **MKOM / MKChannels**: RCS Business Messaging (tipo de serviço `rcs`)
- **GlobalSIM / TotalSMS**: provisionamento de remetente WhatsApp ("chip") (`sender`, `chip_f2`, `chip_f3`)
- **Provedores de IA/LLM** (OpenAI, Anthropic, etc.): pra auto-resposta de entrada do Agente de IA

## graphify

This project has a knowledge graph at graphify-out/ with god nodes, community structure, and cross-file relationships.

Rules:
- For codebase questions, first run `graphify query "<question>"` when graphify-out/graph.json exists. Use `graphify path "<A>" "<B>"` for relationships and `graphify explain "<concept>"` for focused concepts. These return a scoped subgraph, usually much smaller than GRAPH_REPORT.md or raw grep output.
- If graphify-out/wiki/index.md exists, use it for broad navigation instead of raw source browsing.
- Read graphify-out/GRAPH_REPORT.md only for broad architecture review or when query/path/explain do not surface enough context.
- After modifying code, run `graphify update .` to keep the graph current (AST-only, no API cost).
