{
  "name": "Chatbot SDR WhatsApp v2 - Z-API (so Postgres, F2A)",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "whatsapp-zapi",
        "options": {}
      },
      "id": "ef8c7234-da1e-4f02-b49f-d319760354e9",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        0,
        400
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "e623a4d6-4bcd-442d-927c-dba3b61522ab",
              "operator": {
                "type": "boolean",
                "operation": "false",
                "singleValue": true
              },
              "leftValue": "={{ $json.body.isGroup }}",
              "rightValue": ""
            },
            {
              "id": "75d99ed3-f93b-44bf-a715-f4ec862b90f9",
              "operator": {
                "type": "boolean",
                "operation": "false",
                "singleValue": true
              },
              "leftValue": "={{ $json.body.fromMe }}",
              "rightValue": ""
            }
          ]
        },
        "options": {}
      },
      "id": "a3cbd2e3-acdf-4329-8a32-5f5f9bead5a5",
      "name": "So conversa individual (anti-grupo)",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        220,
        400
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "5466814b-8048-4a94-b2d1-6406309e96d2",
              "name": "sessionId",
              "type": "string",
              "value": "={{ $json.body.phone }}"
            },
            {
              "id": "8e8e9ee1-458b-4191-83db-2baa338b6e82",
              "name": "texto",
              "type": "string",
              "value": "={{ $json.body.text.message }}"
            },
            {
              "id": "8628e44d-fada-4473-a158-937ee32bd7e2",
              "name": "msgId",
              "type": "string",
              "value": "={{ $json.body.messageId }}"
            }
          ]
        },
        "options": {}
      },
      "id": "f194e9d9-e016-4d72-832e-5e0c992faf32",
      "name": "Extrai dados",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        440,
        320
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "CREATE TABLE IF NOT EXISTS msg_buffer (\n  id BIGSERIAL PRIMARY KEY,\n  session_id VARCHAR(30) NOT NULL,\n  texto TEXT NOT NULL,\n  msg_id VARCHAR(255) NOT NULL,\n  created_at TIMESTAMPTZ NOT NULL DEFAULT now()\n);\nCREATE INDEX IF NOT EXISTS idx_msg_buffer_session ON msg_buffer (session_id, created_at DESC);",
        "options": {}
      },
      "id": "4037adbc-5959-4237-b00e-22811a0e0a86",
      "name": "Cria tabela buffer",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        640,
        320
      ],
      "credentials": {
        "postgres": {
          "id": "SUBSTITUIR_POSTGRES",
          "name": "Postgres"
        }
      }
    },
    {
      "parameters": {
        "schema": {
          "__rl": true,
          "mode": "list",
          "value": "public"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "msg_buffer"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "session_id": "={{ $('Extrai dados').item.json.sessionId }}",
            "texto": "={{ $('Extrai dados').item.json.texto }}",
            "msg_id": "={{ $('Extrai dados').item.json.msgId }}"
          },
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": true,
          "schema": [
            {
              "id": "id",
              "type": "number",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "id",
              "canBeUsedToMatch": true
            },
            {
              "id": "session_id",
              "type": "string",
              "display": true,
              "required": true,
              "displayName": "session_id",
              "canBeUsedToMatch": true
            },
            {
              "id": "texto",
              "type": "string",
              "display": true,
              "required": true,
              "displayName": "texto",
              "canBeUsedToMatch": true
            },
            {
              "id": "msg_id",
              "type": "string",
              "display": true,
              "required": true,
              "displayName": "msg_id",
              "canBeUsedToMatch": true
            },
            {
              "id": "created_at",
              "type": "dateTime",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "created_at",
              "canBeUsedToMatch": true
            }
          ]
        },
        "options": {}
      },
      "id": "8e8c812c-b845-4bb0-a1f5-bcfb279f2f62",
      "name": "Empilha no buffer",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        840,
        320
      ],
      "credentials": {
        "postgres": {
          "id": "SUBSTITUIR_POSTGRES",
          "name": "Postgres"
        }
      }
    },
    {
      "parameters": {
        "amount": 8,
        "unit": "seconds"
      },
      "id": "949c3837-70bd-492b-a5b3-8e0194ef7042",
      "name": "Espera (debounce)",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        1040,
        320
      ],
      "webhookId": "53a29de7-f768-4884-ada3-3275a0fbdf19"
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT msg_id FROM msg_buffer WHERE session_id = '{{ $('Extrai dados').item.json.sessionId }}' ORDER BY created_at DESC LIMIT 1;",
        "options": {}
      },
      "id": "1b869503-df19-4475-8594-18f0c733f573",
      "name": "Pega ultima do buffer",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        1240,
        320
      ],
      "credentials": {
        "postgres": {
          "id": "SUBSTITUIR_POSTGRES",
          "name": "Postgres"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "f49c0179-9b5f-4e55-bcb8-6059816dbb6c",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.msg_id }}",
              "rightValue": "={{ $('Extrai dados').item.json.msgId }}"
            }
          ]
        },
        "options": {}
      },
      "id": "2bfd5cdc-b47d-41d9-aaae-d075cbf99e78",
      "name": "Sou a ultima?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1440,
        320
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT string_agg(texto, ' ' ORDER BY created_at) AS mensagem FROM msg_buffer WHERE session_id = '{{ $('Extrai dados').item.json.sessionId }}';",
        "options": {}
      },
      "id": "a93051e6-67f9-4474-a571-b8c7b1ae1fd4",
      "name": "Junta mensagens",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        1640,
        240
      ],
      "credentials": {
        "postgres": {
          "id": "SUBSTITUIR_POSTGRES",
          "name": "Postgres"
        }
      }
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "DELETE FROM msg_buffer WHERE session_id = '{{ $('Extrai dados').item.json.sessionId }}';",
        "options": {}
      },
      "id": "c03ec389-1791-4cc7-be13-f41238b36e0a",
      "name": "Limpa buffer",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        1840,
        240
      ],
      "credentials": {
        "postgres": {
          "id": "SUBSTITUIR_POSTGRES",
          "name": "Postgres"
        }
      }
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "={{ $('Junta mensagens').item.json.mensagem }}",
        "options": {
          "systemMessage": "# IDENTIDADE\n\nVocê é o assistente de pré-vendas (SDR) e recuperação de vendas da **[SUA EMPRESA]** (descreva seu negócio numa linha). Sua função no WhatsApp é:\n\n1. **Atender leads novos** (que vieram de criativos, indicação, formulário)\n2. **Recuperar leads inativos** (que viram a oferta mas não fecharam, ou abriram conversa e sumiram)\n3. **Qualificar e agendar** call com humano quando o lead estiver pronto\n\n# TOM DE VOZ (CRÍTICO)\n\nVocê fala **EXATAMENTE como um SDR humano da [SUA EMPRESA] falaria no WhatsApp**:\n\n- **Português brasileiro coloquial**. Mistura \"tu\" e \"você\" naturalmente.\n- **Frases curtas**. Quebra em várias mensagens quando faz sentido.\n- **Marcadores humanos**: \"pô\", \"tipo\", \"basicamente\", \"enfim\", \"na real\", \"inclusive\", \"sacou?\", \"tá ligado?\"\n- **Áudio do WhatsApp em texto**: leia em voz alta — se soar como apresentação corporativa, está errado.\n- **NUNCA usa**: \"amigão\", \"imagina só\", \"e se eu te dissesse\", \"deixa eu ser honesto\", travessões (—), reticências de IA, \"literalmente\" em excesso, \"incrível\", \"transforme sua vida\".\n- **Reticências** são amigas — uso natural pra dar respiração.\n- **Áudios de áudio**: se o lead manda áudio, responde com paciência tipo \"ó, escutei aqui, então...\"\n- **Vocativos**: \"cara\", \"mano\", \"meu querido\"/\"minha querida\", \"galera\". Adapta pelo gênero/idade aparente do lead.\n\n# FLUXO SDR\n\n## Lead NOVO chegou (primeira msg)\n1. **Cumprimenta** breve + apresenta-se (\"Fala, [nome]! Aqui é o [seu_nome] da [SUA EMPRESA]. Tudo bem?\")\n2. **Identifica origem** sem ser invasivo (\"Vi que tu deixou contato no [criativo/site] — me conta rapidão o que tu tá buscando?\")\n3. **Escuta a dor** sem empurrar venda\n4. **Qualifica** com perguntas BANT light (já tem agência? já roda tráfego pago? qual investimento médio mensal? quando quer começar?)\n5. **Agenda call** se tem fit (oferecer 2-3 horários, não 5+; deixar a decisão fácil)\n\n## Lead INATIVO retornando (já conversou antes)\n1. **Resgata contexto** (\"Oi, [nome]! Lembra que tu tinha conversado comigo sobre [tema]?\")\n2. **Reabre com curiosidade**, não com pressão (\"Apareceu uma coisa que vai te interessar / o que tu achou da proposta?\")\n3. **Identifica objeção residual** (preço? timing? cônjuge? confiança?)\n4. **Trata objeção com case real ou prova** (sem inventar)\n5. **Reencaminha pra call ou material** dependendo do nível\n\n# LIMITES (CRÍTICOS)\n\n- **NÃO inventa números, cases, garantias ou prazos** que não foram informados no contexto.\n- **NÃO promete resultado específico** (\"vai dobrar seu faturamento em 30 dias\").\n- **NÃO desce o preço** sem autorização — quando perguntarem valor, fala que depende do escopo e leva pra call.\n- **NÃO discute concorrente** de forma negativa.\n- Quando o lead estiver pronto pra fechar ou pedir algo fora do teu escopo, **passa pra humano**: \"Show, vou alinhar com [seu nome/closer] aqui e ele te chama em até X horas.\"\n\n# HISTÓRICO\n\nVocê tem memória automática desta conversa (as últimas mensagens deste mesmo número). Use pra dar continuidade, não repetir perguntas e retomar pontos abertos.\n\n# OUTPUT\n\nResponda APENAS o texto que vai pro lead. Sem prefixo, sem aspas, sem markdown — fala como pessoa fala no WhatsApp.",
          "enableStreaming": false,
          "maxIterations": 10
        }
      },
      "id": "ccfe1e02-47d3-40d0-83a3-8bf526a3b055",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 3.1,
      "position": [
        2060,
        240
      ]
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "mimo-v2.5-pro",
          "cachedResultName": "mimo-v2.5-pro"
        },
        "responsesApiEnabled": false,
        "options": {
          "baseURL": "https://api.xiaomimimo.com/v1",
          "temperature": 0
        }
      },
      "id": "9d6cef33-aec6-48c6-b1c0-5c65c5f9cf09",
      "name": "MiMo v2.5-pro",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.3,
      "position": [
        2000,
        460
      ],
      "credentials": {
        "openAiApi": {
          "id": "SUBSTITUIR_LLM",
          "name": "MiMo v2.5-pro (OpenAI-compat)"
        }
      }
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "={{ $('Extrai dados').item.json.sessionId }}",
        "tableName": "n8n_chat_histories",
        "contextWindowLength": 15
      },
      "id": "dc7f7aaf-d1a0-4c28-aba2-3df3656b2247",
      "name": "Memoria (Postgres)",
      "type": "@n8n/n8n-nodes-langchain.memoryPostgresChat",
      "typeVersion": 1.4,
      "position": [
        2200,
        460
      ],
      "credentials": {
        "postgres": {
          "id": "SUBSTITUIR_POSTGRES",
          "name": "Postgres"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.z-api.io/instances/SEU_INSTANCE_ID/token/SEU_TOKEN/send-text",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Client-Token",
              "value": "SEU_CLIENT_TOKEN"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ phone: $('Extrai dados').item.json.sessionId, message: $('AI Agent').item.json.output }) }}",
        "options": {}
      },
      "id": "760af530-0b1d-4f43-bc5f-b357ba7f3dd8",
      "name": "Envia (Z-API)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2280,
        240
      ]
    },
    {
      "parameters": {},
      "id": "ccf8ddeb-2707-4e89-8053-670693ae628e",
      "name": "Ignora (grupo/eco/duplicada)",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        840,
        560
      ]
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "So conversa individual (anti-grupo)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "So conversa individual (anti-grupo)": {
      "main": [
        [
          {
            "node": "Extrai dados",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Ignora (grupo/eco/duplicada)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extrai dados": {
      "main": [
        [
          {
            "node": "Cria tabela buffer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Cria tabela buffer": {
      "main": [
        [
          {
            "node": "Empilha no buffer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Empilha no buffer": {
      "main": [
        [
          {
            "node": "Espera (debounce)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Espera (debounce)": {
      "main": [
        [
          {
            "node": "Pega ultima do buffer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pega ultima do buffer": {
      "main": [
        [
          {
            "node": "Sou a ultima?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sou a ultima?": {
      "main": [
        [
          {
            "node": "Junta mensagens",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Ignora (grupo/eco/duplicada)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Junta mensagens": {
      "main": [
        [
          {
            "node": "Limpa buffer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Limpa buffer": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "Envia (Z-API)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "MiMo v2.5-pro": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Memoria (Postgres)": {
      "ai_memory": [
        [
          {
            "node": "AI Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {}
}