{ "name": "Agendamento - GChat - Z-PRO (clínica exemplo)", "nodes": [ { "parameters": { "httpMethod": "POST", "path": "agendamento-zpro-clinica-exemplo", "responseMode": "responseNode", "options": {} }, "id": "webhook-zpro", "name": "Webhook Z-PRO", "type": "n8n-nodes-base.webhook", "typeVersion": 2.1, "position": [-208, -176] }, { "parameters": { "respondWith": "text", "responseBody": "={{ $json.output }}", "options": {} }, "id": "respond-stub", "name": "Respond to Webhook", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.5, "position": [224, 48] }, { "parameters": { "mode": "runOnceForEachItem", "jsCode": "const body = $input.item.json.body || {};\n\n// Configuração fixa desta cópia do workflow. Ajustar por clínica ao\n// duplicar este workflow para uma nova instância.\nconst clinicConfig = {\n provider: \"zpro\",\n clinicId: \"clinica-exemplo\",\n};\n\nif (body.step !== \"process\") {\n return {\n json: {\n earlyReply: true,\n sessionId: body.protocol || \"\",\n mensagem: \"\",\n nomeContato: \"\",\n telefone: \"\",\n provider: clinicConfig.provider,\n clinicId: clinicConfig.clinicId,\n },\n };\n}\n\nreturn {\n json: {\n earlyReply: false,\n sessionId: body.protocol || \"\",\n mensagem: String(body.entrada || \"\").trim(),\n nomeContato: body.contactName || \"\",\n telefone: body.number || \"\",\n provider: clinicConfig.provider,\n clinicId: clinicConfig.clinicId,\n },\n};" }, "id": "normalizar-entrada", "name": "Normalizar entrada", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [-16, -176], "onError": "continueErrorOutput" }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 2 }, "combinator": "and", "conditions": [ { "id": "cond-early-reply", "leftValue": "={{ $json.earlyReply }}", "rightValue": true, "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ] }, "options": {} }, "id": "if-early-reply", "name": "É step=process?", "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [240, -336] }, { "parameters": { "model": { "mode": "id", "value": "gpt-4o-mini" }, "builtInTools": {}, "options": {} }, "id": "openai-chat-model", "name": "OpenAI Chat Model", "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi", "typeVersion": 1.3, "position": [512, -64], "credentials": { "openAiApi": { "id": "PRECISA_RECRIAR_NO_SERVIDOR_NOVO", "name": "GPT" } } }, { "parameters": { "sessionIdType": "customKey", "sessionKey": "={{ $json.sessionId }}", "contextWindowLength": 20 }, "id": "memoria-sessao", "name": "Memória da Sessão", "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow", "typeVersion": 1.4, "position": [672, -96] }, { "parameters": { "promptType": "define", "text": "={{ $json.mensagem }}", "options": { "systemMessage": "=Você é o assistente de agendamento da clínica, conversando por WhatsApp. A data de hoje é {{ $now.setZone('America/Sao_Paulo').toFormat('yyyy-MM-dd (cccc)') }}, fuso America/Sao_Paulo — use isso pra calcular qualquer data relativa (\"amanhã\", \"sexta que vem\", etc.), nunca assuma outro ano. clinicId e telefone do paciente já são preenchidos automaticamente pelo sistema — você só precisa decidir data/hora e confirmar o nome completo.\n\nRegras:\n1. O nome de contato do WhatsApp é \"{{ $('Normalizar entrada').item.json.nomeContato }}\" e o telefone é \"{{ $('Normalizar entrada').item.json.telefone }}\" — mas SEMPRE confirme o nome completo do paciente com o cliente antes de agendar, mesmo que já tenha o nome de contato.\n2. Pergunte a data e o horário desejados para a consulta, e opcionalmente o motivo. O horário que o cliente disser (ex.: \"10h\") é sempre horário local de Brasília (America/Sao_Paulo, UTC-3) — use esse mesmo número de hora ao montar startAt/endAt, com offset -03:00 (nunca converta pra UTC/Z, nunca some ou subtraia horas).\n3. Nunca invente informação que o cliente não deu.\n4. Responda sempre em português, em tom cordial e objetivo, adequado para WhatsApp (mensagens curtas).\n5. Quando tiver o nome completo confirmado e a data/hora desejada, você pode agendar de verdade.\n6. NUNCA agende mais de uma vez na mesma conversa — se o cliente já confirmou um agendamento nesta conversa, não agende de novo; informe que o agendamento já foi feito.\n7. Se o agendamento falhar, explique o problema ao cliente em linguagem simples e pergunte se ele quer tentar de novo com outros dados, ou avise que vai encaminhar para um atendente." } }, "id": "agente-agendamento", "name": "AI Agent", "type": "@n8n/n8n-nodes-langchain.agent", "typeVersion": 3.1, "position": [560, -320], "onError": "continueErrorOutput" }, { "parameters": { "operation": "executeTool", "toolName": "agendar", "toolParameters": "={{ JSON.stringify({ clinicId: $('Normalizar entrada').item.json.clinicId, startAt: $fromAI('startAt', 'Data e hora de inicio da consulta, no horario local pedido pelo cliente, em ISO 8601 com offset -03:00 (America/Sao_Paulo) -- NUNCA use Z/UTC. Exemplo: cliente pediu 10h -> 2026-08-14T10:00:00-03:00', 'string'), endAt: $fromAI('endAt', 'Data e hora de termino da consulta, mesmo formato do startAt (offset -03:00, nunca Z/UTC), geralmente 30 minutos apos o startAt', 'string'), patient: { name: $fromAI('patientName', 'Nome completo do paciente, ja confirmado com o cliente na conversa', 'string'), phone: $('Normalizar entrada').item.json.telefone } }) }}" }, "id": "mcp-tool-agendar", "name": "MCP - gti-scheduling-mcp", "type": "n8n-nodes-mcp.mcpClientTool", "typeVersion": 1, "position": [1024, -128], "credentials": { "mcpClientApi": { "id": "PRECISA_RECRIAR_NO_SERVIDOR_NOVO", "name": "Client (STDIO) account" } } }, { "parameters": { "assignments": { "assignments": [ { "id": "erro-output", "name": "output", "type": "string", "value": "Desculpe, tive um problema técnico agora e não consegui continuar. Pode tentar de novo em alguns minutos, ou aguardar que alguém da nossa equipe vai te atender." } ] }, "options": {} }, "id": "erro-resposta-padrao", "name": "Erro - Resposta padrão", "type": "n8n-nodes-base.set", "typeVersion": 3.5, "position": [240, -176] } ], "connections": { "Webhook Z-PRO": { "main": [[{ "node": "Normalizar entrada", "type": "main", "index": 0 }]] }, "Normalizar entrada": { "main": [ [{ "node": "É step=process?", "type": "main", "index": 0 }], [{ "node": "Erro - Resposta padrão", "type": "main", "index": 0 }] ] }, "OpenAI Chat Model": { "ai_languageModel": [[{ "node": "AI Agent", "type": "ai_languageModel", "index": 0 }]] }, "Memória da Sessão": { "ai_memory": [[{ "node": "AI Agent", "type": "ai_memory", "index": 0 }]] }, "É step=process?": { "main": [ [], [{ "node": "AI Agent", "type": "main", "index": 0 }] ] }, "AI Agent": { "main": [ [{ "node": "Respond to Webhook", "type": "main", "index": 0 }], [{ "node": "Erro - Resposta padrão", "type": "main", "index": 0 }] ] }, "MCP - gti-scheduling-mcp": { "ai_tool": [[{ "node": "AI Agent", "type": "ai_tool", "index": 0 }]] }, "Erro - Resposta padrão": { "main": [[{ "node": "Respond to Webhook", "type": "main", "index": 0 }]] } }, "settings": { "executionOrder": "v1" } }