Skip to main content
Cada pipeline do GHL pode ter um fluxo: um grafo em que cada nó é um estágio, cada aresta tem uma condição em linguagem natural e cada nó tem ações ao entrar e ao sair. O estágio da oportunidade no GHL manda: o nó da conversa é sempre o do estágio em que o card está. Tudo abaixo usa a chave admin (X-Admin-Key), em /admin/tenants/{id}.

Quem decide

O ZapMeow não escreve a resposta nem avalia a condição: ele pergunta ao decisor e executa. O decisor é configurado na seção flow do tenant:
  • kind: "http": o ZapMeow faz POST na url com o turno e espera a decisão. Se existir o segredo flow_decider_token no cofre do tenant, vai em Authorization: Bearer.
  • kind: "anthropic": chama a API da Anthropic com o segredo anthropic_api_key do tenant (modelo em decider.model, padrão claude-sonnet-5-5).
Entrada do decisor (DecideIn): mode (shadow, simulate ou live), nodeKey, nodeTitle, instructions, options[] (to, title, when), collect[] (campos a extrair), messages[] (from: lead ou ia), contactId, opportunityId. Saída (DecideOut): reply (texto ao lead ou vazio), next (chave de um nó de options, ou vazio para ficar), handoff (passar para uma pessoa), fields (campo → valor, só dos pedidos em collect), reason. Passagem para um nó que não é saída do atual e campo não pedido são descartados, com nota no evento.

Desenhar

Nós e arestas guardam tenantId e ghlPipelineId. Regras: o pipeline e os estágios precisam existir no espelho do GHL (sincronize antes: 409 se não); um nó por estágio; kind é stage (precisa de ghlStageId) ou end (fim; não sai); até 100 nós, 300 arestas, 10 ações por lista. O pipeline de um fluxo não muda depois de criado. Um fluxo em sombra ou ao vivo por pipeline.

Catálogo de ações

As tools respeitam o modo do tenant (config/tools): em pede_ok (padrão) o fluxo propõe e uma pessoa aprova.

Sombra antes do ao vivo

  • draft: só desenho; não processa conversa.
  • shadow: observa as conversas reais do pipeline e grava em zm_flow_events o que faria (ações ao sair, passagem, ações ao entrar). Não envia, não mexe no GHL e não chama nenhuma tool. O mundo segue como está; quando o card muda de estágio, o evento compare diz se bateu com a previsão (match).
  • live: executa. Só se chega aqui pelo shadow: draft → live dá 409, e shadow → live confere o relatório (GET /flows/{fid}/shadow): dias em sombra, conversas reais e eventos mínimos (padrão 7 dias, 3 conversas, 10 eventos; configuráveis na seção flow, com piso de 1 conversa e 1 evento). Conversa de teste (simulação), eventos de erro e skipped não contam.
  • Editar o desenho de um fluxo ao vivo o devolve para a sombra, na versão seguinte (a contagem recomeça).
  • live → shadow é o botão de reversão.

Conversa de teste (simulação)

POST /flows/{fid}/simulate com {"chat": "teste-1", "message": "tenho interesse", "startNode": "novo"} roda um turno de uma conversa que não existe no WhatsApp: o histórico fica guardado, o decisor responde e o nó segue as passagens escolhidas. Nada é enviado e o GHL não é tocado. Funciona em qualquer estado, inclusive rascunho. Responde {event, run}.

Acompanhar

Como a conversa encontra o fluxo

A mensagem do lead (chat individual) procura o contato do GHL pelo telefone (/contacts/search/duplicate, vínculo guardado em zm_flow_links), pega a oportunidade aberta mais recente num pipeline com fluxo em sombra ou ao vivo e começa no nó do estágio dela. Lead sem contato no GHL ou em estágio fora do desenho não é acompanhado (o evento skipped explica). Várias mensagens seguidas geram uma resposta só, sobre a conversa inteira.

O humano manda no card

  • Pessoa move o card (webhook do GHL, ou o espelho ao começar o turno): o nó reposiciona sem executar ações (evento reposition; na sombra, compare). Oportunidade ganha, perdida ou abandonada encerra o acompanhamento.
  • Passagem em pede_ok: o nó só muda depois do OK do pedido; aprovado, o evento confirmed roda as ações ao entrar; recusado, a conversa fica onde estava (rejected).
  • Handoff pausa só este motor. Respostas que outro sistema (ex.: o Convex do tenant Glow) manda pela fila outbox não são bloqueadas pela pausa.

Limites conhecidos

  • O motor guarda estado no Postgres, mas as travas por conversa são em memória: mantenha 1 réplica.
  • Condições são avaliadas pelo decisor, não pelo ZapMeow: a qualidade vem do decisor.
  • Mensagem de áudio do lead entra no histórico como [audio] (sem transcrição).