Files
KLINGO-CLINICAL-SYSLIFE-GCHAT/docs/n8n-workflows/agendamento-gchat-zpro-clinica-exemplo.json
T
GustavoandClaude Sonnet 5 257afe98c0 Snapshot: gti-scheduling-mcp (Z-PRO + Klingo adapters) e workflow n8n de agendamento funcionando de ponta a ponta
- gti-scheduling-mcp: MCP server com ZProAdapter e KlingoAdapter (agendar() completo pros dois)
- Workflow n8n 'Agendamento - GChat - Z-PRO': webhook -> AI Agent -> MCP -> Z-PRO, testado com sucesso real
- Documentacao: specs, plano, runbook de problemas reais e export do workflow

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 17:23:10 -03:00

189 lines
8.3 KiB
JSON

{
"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"
}
}