This workflow follows the HTTP Request → OpenAI recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "Follow-up Automatizado",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "hours",
"minutesInterval": 1,
"triggerAtHour": 10
}
]
}
},
"name": "Agendador Follow-up",
"type": "n8n-nodes-base.cron",
"typeVersion": 1,
"position": [
0,
0
],
"id": "a0800e83-1100-4cff-b137-84781f2c9979"
},
{
"parameters": {
"operation": "get",
"tableId": "clientes_finais",
"filters": {
"conditions": [
{
"keyName": "modo",
"keyValue": "em aberto"
}
]
}
},
"name": "Buscar Clientes Pendentes",
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
200,
0
],
"id": "ce5a0278-eba1-40c4-979a-b152a51e31a9",
"alwaysOutputData": true,
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT me.* FROM mensagens_enviadas me\nJOIN clientes_finais cf ON me.cliente_final_id = cf.id\nWHERE cf.modo = 'em aberto'\nAND me.timestamp = (\n SELECT MAX(timestamp) FROM mensagens_enviadas\n WHERE cliente_final_id = cf.id\n)"
},
"name": "Buscar Ultima Mensagem",
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
400,
0
],
"id": "ce5a0278-eba1-40c4-979a-b152a51e31b1",
"alwaysOutputData": true,
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Filtrar clientes que precisam de follow-up\n// Ajustar para o fuso hor\u00e1rio brasileiro (GMT-3)\nconst agora = new Date();\n\n// Fun\u00e7\u00e3o para ajustar timestamp para fuso hor\u00e1rio brasileiro\nfunction ajustarParaFusoBrasil(data) {\n // Criar uma string de data no formato ISO com o fuso hor\u00e1rio de S\u00e3o Paulo\n const dataString = data.toLocaleString('en-US', { timeZone: 'America/Sao_Paulo' });\n // Converter de volta para objeto Date\n return new Date(dataString);\n}\n\n// Ajustar a data atual para o fuso hor\u00e1rio brasileiro\nconst agoraBrasil = ajustarParaFusoBrasil(agora);\nconsole.log('Data atual (fuso BR):', agoraBrasil);\n\nconst clientesParaFollowUp = [];\n\n// Obter todas as \u00faltimas mensagens\nconst ultimasMensagens = $node[\"Buscar Ultima Mensagem\"].json;\n\nfor (const item of $input.all()) {\n const cliente = item.json;\n \n // Encontrar a \u00faltima mensagem deste cliente\n const ultimaMensagem = ultimasMensagens.find(m => m.cliente_final_id === cliente.id);\n \n // Verificar se tem \u00faltima mensagem\n if (!ultimaMensagem || !ultimaMensagem.timestamp) continue;\n \n // Converter para data e ajustar para fuso hor\u00e1rio brasileiro\n const dataUltimaMensagem = new Date(ultimaMensagem.timestamp);\n const dataUltimaMensagemBR = ajustarParaFusoBrasil(dataUltimaMensagem);\n \n console.log(`Cliente ${cliente.id} - \u00daltima mensagem:`, dataUltimaMensagemBR);\n \n // Calcular diferen\u00e7a em dias (usando datas no mesmo fuso)\n const diffTime = Math.abs(agoraBrasil - dataUltimaMensagemBR);\n const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));\n \n console.log(`Cliente ${cliente.id} - Dias desde \u00faltima mensagem:`, diffDays);\n \n // Adicionar tipo de follow-up baseado no tempo\n if (diffDays === 1) {\n clientesParaFollowUp.push({\n ...cliente,\n tipoFollowUp: 'lembrete_leve',\n diasEspera: diffDays,\n ultimaMensagemData: ultimaMensagem.timestamp\n });\n } else if (diffDays === 3) {\n clientesParaFollowUp.push({\n ...cliente,\n tipoFollowUp: 'reforcar_beneficio',\n diasEspera: diffDays,\n ultimaMensagemData: ultimaMensagem.timestamp\n });\n } else if (diffDays === 7) {\n clientesParaFollowUp.push({\n ...cliente,\n tipoFollowUp: 'encerramento',\n diasEspera: diffDays,\n ultimaMensagemData: ultimaMensagem.timestamp\n });\n }\n}\n\n// Retornar apenas clientes que precisam de follow-up\nreturn clientesParaFollowUp.map(cliente => ({ json: cliente }));"
},
"name": "Filtrar Follow-ups",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
600,
0
],
"id": "37b14782-e647-489a-852e-ea6148f70d27"
},
{
"parameters": {
"options": {}
},
"name": "Loop Clientes",
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"position": [
800,
0
],
"id": "ed981d1b-4a0a-4075-9672-133a7eaecab3"
},
{
"parameters": {
"authentication": "oAuth2",
"resource": "chat",
"prompt": {
"messages": [
{
"role": "system",
"content": "Voc\u00ea \u00e9 um assistente especializado em follow-up de vendas. Crie mensagens curtas, amig\u00e1veis e naturais para WhatsApp."
},
{
"role": "user",
"content": "=Crie uma mensagem de follow-up para um cliente chamado {{$json.nome}}. Passaram-se {{$json.diasEspera}} dias desde a \u00faltima intera\u00e7\u00e3o.\n\nTipo de follow-up: {{$json.tipoFollowUp}}\n\nSe o tipo for 'lembrete_leve', envie uma mensagem simp\u00e1tica perguntando se o cliente viu as informa\u00e7\u00f5es enviadas anteriormente.\n\nSe o tipo for 'reforcar_beneficio', reforce os benef\u00edcios do servi\u00e7o e pergunte se o cliente tem alguma d\u00favida espec\u00edfica.\n\nSe o tipo for 'encerramento', informe que o atendimento ser\u00e1 encerrado, mas que o cliente pode entrar em contato quando quiser.\n\nA mensagem deve ser curta (m\u00e1ximo 3 linhas), amig\u00e1vel e natural, como se fosse enviada por uma pessoa real. N\u00e3o mencione que \u00e9 um follow-up automatizado."
}
]
},
"options": {
"model": "gpt-3.5-turbo",
"temperature": 0.7,
"maxTokens": 150,
"additionalProperties": {}
},
"simplifyOutput": true
},
"name": "Gerar Mensagem Follow-up",
"type": "@n8n/n8n-nodes-langchain.openAi",
"typeVersion": 1.8,
"position": [
1000,
0
],
"id": "73c21b6d-6374-4eec-8d62-d63121c0757a",
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Depurar a sa\u00edda do OpenAI\nconst openAIOutput = $input.first().json;\nconsole.log('Sa\u00edda do OpenAI:', JSON.stringify(openAIOutput, null, 2));\n\n// Tentar extrair a mensagem de diferentes formatos poss\u00edveis\nlet mensagem = '';\nif (typeof openAIOutput === 'string') {\n mensagem = openAIOutput;\n} else if (openAIOutput.text) {\n mensagem = openAIOutput.text;\n} else if (openAIOutput.content) {\n mensagem = openAIOutput.content;\n} else if (openAIOutput.choices && openAIOutput.choices[0]) {\n mensagem = openAIOutput.choices[0].message?.content || '';\n}\n\nconsole.log('Mensagem extra\u00edda:', mensagem);\n\n// Garantir que temos uma mensagem padr\u00e3o se nada for encontrado\nif (!mensagem) {\n if ($node[\"Loop Clientes\"].json.tipoFollowUp === 'lembrete_leve') {\n mensagem = `Ol\u00e1 ${$node[\"Loop Clientes\"].json.nome}! S\u00f3 passando para saber se voc\u00ea chegou a ver as informa\u00e7\u00f5es que te enviei. Estou \u00e0 disposi\u00e7\u00e3o para qualquer d\u00favida! \ud83d\ude0a`;\n } else if ($node[\"Loop Clientes\"].json.tipoFollowUp === 'reforcar_beneficio') {\n mensagem = `Oi ${$node[\"Loop Clientes\"].json.nome}! Lembra que conversamos sobre nossos planos? Eles oferecem o melhor custo-benef\u00edcio do mercado. Posso te ajudar com alguma d\u00favida espec\u00edfica?`;\n } else {\n mensagem = `Ol\u00e1 ${$node[\"Loop Clientes\"].json.nome}, estou encerrando seu atendimento por aqui, mas fique \u00e0 vontade para me chamar quando precisar! Estamos sempre \u00e0 disposi\u00e7\u00e3o. \ud83d\ude4c`;\n }\n}\n\nreturn [{\n json: {\n mensagemGerada: mensagem\n }\n}];"
},
"name": "Debug OpenAI",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1100,
0
],
"id": "7f42255f-8548-424d-b4c3-438f05afd5a6"
},
{
"parameters": {
"jsCode": "// Obter o n\u00famero do Twilio para envio\n// Normalmente seria armazenado em uma configura\u00e7\u00e3o ou obtido de algum lugar\n\nreturn [{\n json: {\n numeroRemetente: \"+14155238886\", // Substitua pelo n\u00famero do Twilio real\n mensagemParaEnviar: $node[\"Debug OpenAI\"].json.mensagemGerada\n }\n}];"
},
"name": "Buscar N\u00famero Twilio",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1200,
0
],
"id": "7f42255f-8548-424d-b4c3-438f05afd5a5"
},
{
"parameters": {
"from": "={{$node[\"Buscar N\u00famero Twilio\"].json.numeroRemetente}}",
"to": "={{$node[\"Loop Clientes\"].json.whatsapp}}",
"toWhatsapp": true,
"message": "={{$node[\"Buscar N\u00famero Twilio\"].json.mensagemParaEnviar}}",
"options": {}
},
"name": "Enviar WhatsApp",
"type": "n8n-nodes-base.twilio",
"typeVersion": 1,
"position": [
1400,
0
],
"id": "a91534de-6b85-4394-b82b-ebadc0cb4cea",
"credentials": {
"twilioApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "POST",
"url": "=http://supabase-server:3001/api/registrar-mensagem",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "whatsappNumero",
"value": "={{$node[\"Buscar N\u00famero Twilio\"].json.numeroRemetente}}"
},
{
"name": "pergunta",
"value": "follow-up autom\u00e1tico"
},
{
"name": "resposta",
"value": "={{$node[\"Buscar N\u00famero Twilio\"].json.mensagemParaEnviar}}"
},
{
"name": "numeroDestino",
"value": "={{$node[\"Loop Clientes\"].json.whatsapp}}"
},
{
"name": "nome",
"value": "={{$node[\"Loop Clientes\"].json.nome}}"
}
]
},
"options": {}
},
"name": "Registrar Mensagem",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 3,
"position": [
1600,
0
],
"id": "1a1bf06c-8f9d-4f0a-88bb-d5fb241d3b0c"
},
{
"parameters": {
"operation": "update",
"tableId": "clientes_finais",
"id": "={{$node[\"Loop Clientes\"].json.id}}",
"updateAllMatching": false,
"data": {
"ultimo_followup": "={{$today}}",
"modo": "={{$node[\"Loop Clientes\"].json.tipoFollowUp === 'encerramento' ? 'encerrado' : 'em aberto'}}",
"tentativas_followup": "={{$node[\"Loop Clientes\"].json.tentativas_followup ? $node[\"Loop Clientes\"].json.tentativas_followup + 1 : 1}}"
}
},
"name": "Atualizar Status Cliente",
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
1800,
0
],
"id": "ce5a0278-eba1-40c4-979a-b152a51e31b0",
"alwaysOutputData": true,
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
}
],
"connections": {
"Agendador Follow-up": {
"main": [
[
{
"node": "Buscar Clientes Pendentes",
"type": "main",
"index": 0
}
]
]
},
"Buscar Clientes Pendentes": {
"main": [
[
{
"node": "Buscar Ultima Mensagem",
"type": "main",
"index": 0
}
]
]
},
"Buscar Ultima Mensagem": {
"main": [
[
{
"node": "Filtrar Follow-ups",
"type": "main",
"index": 0
}
]
]
},
"Filtrar Follow-ups": {
"main": [
[
{
"node": "Loop Clientes",
"type": "main",
"index": 0
}
]
]
},
"Loop Clientes": {
"main": [
[
{
"node": "Gerar Mensagem Follow-up",
"type": "main",
"index": 0
}
]
]
},
"Gerar Mensagem Follow-up": {
"main": [
[
{
"node": "Debug OpenAI",
"type": "main",
"index": 0
}
]
]
},
"Debug OpenAI": {
"main": [
[
{
"node": "Buscar N\u00famero Twilio",
"type": "main",
"index": 0
}
]
]
},
"Buscar N\u00famero Twilio": {
"main": [
[
{
"node": "Enviar WhatsApp",
"type": "main",
"index": 0
}
]
]
},
"Enviar WhatsApp": {
"main": [
[
{
"node": "Registrar Mensagem",
"type": "main",
"index": 0
}
]
]
},
"Registrar Mensagem": {
"main": [
[
{
"node": "Atualizar Status Cliente",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
openAiApisupabaseApitwilioApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Follow-up Automatizado. Uses supabase, openAi, twilio, httpRequest. Scheduled trigger; 11 nodes.
Source: https://github.com/1thays4/painel-clientes-ia-ts/blob/main/workflows/n8n-follow-up-workflow.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Follow-up Automatizado. Uses supabase, openAi, twilio, httpRequest. Scheduled trigger; 11 nodes.
Sales Team V2. Uses supabase, httpRequest, crypto, openAi. Scheduled trigger; 25 nodes.
This workflow automatically fetches active deals from Zoho CRM, retrieves real-time market signals, calculates AI-enhanced forecast metrics, evaluates deal-market alignment, stores data in a database,
Feliz-Aniversario. Uses httpRequest, openAi, whatsApp, supabase. Scheduled trigger; 16 nodes.
Fetch campaign & members from Salesforce. GPT‑4 auto‑writes a channel‑appropriate, personalised outbound message. Switch node sends via Twilio (SMS/WhatsApp), SMTP (Email). Mark each member as process