This workflow follows the HTTP Request → Postgres 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": "n8n-supabase-ai-pipeline",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "food-log",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
272,
0
],
"id": "a3e61f01-fab8-4203-98d7-06cbdf25a3b0",
"name": "Webhook"
},
{
"parameters": {
"jsCode": "const rawContent = $json.choices[0].message.content;\nconst aiData = JSON.parse(rawContent);\nconst originalInput = $(\"Webhook\").first().json.body;\n\nconst cleanIntent = aiData.intent ? aiData.intent.trim() : 'update_preferences';\nconst data = aiData.data || {};\n\nlet sqlQuery = \"\";\nlet tableName = \"\";\n\nif (cleanIntent === 'feedback') {\n sqlQuery = `INSERT INTO ai_learning (user_id, bad_experience, topic) VALUES ('${originalInput.user_id}', '${data.feedback_content || \"Feedback gen\u00e9rico\"}', 'Feedback Negativo') RETURNING id;`;\n tableName = \"ai_learning\";\n\n} else if (cleanIntent === 'planning') {\n sqlQuery = `INSERT INTO workout_plans (user_id, plan_structure, active) VALUES ('${originalInput.user_id}', '${JSON.stringify(data)}', true) RETURNING id;`;\n tableName = \"workout_plans\";\n\n} else {\n const equipment = JSON.stringify(data.available_equipment || [\"Peso do corpo\"]);\n const dislikedEx = JSON.stringify(data.disliked_exercises || []);\n const dislikedFood = JSON.stringify(data.disliked_foods || []);\n \n sqlQuery = `\n INSERT INTO user_preferences (user_id, goal, injuries, available_equipment, disliked_exercises, disliked_foods) \n VALUES ('${originalInput.user_id}', '${data.goal}', '${data.injuries}', '${equipment}', '${dislikedEx}', '${dislikedFood}')\n ON CONFLICT (user_id) \n DO UPDATE SET \n goal = EXCLUDED.goal,\n injuries = EXCLUDED.injuries,\n available_equipment = EXCLUDED.available_equipment,\n disliked_exercises = EXCLUDED.disliked_exercises,\n disliked_foods = EXCLUDED.disliked_foods;\n `;\n tableName = \"user_preferences\";\n}\n\nreturn [\n {\n json: {\n user_id: originalInput.user_id,\n intent: cleanIntent, \n reply_message: aiData.reply_message, \n generated_sql: sqlQuery,\n target_table: tableName,\n data: data,\n calories: data.calories || 0,\n macros: {\n protein: data.protein || 0,\n carbs: data.carbs || 0,\n fat: data.fat || 0\n }\n }\n }\n];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2384,
0
],
"id": "f3c422ef-ed6c-451b-88fe-139edad56323",
"name": "Code in JavaScript"
},
{
"parameters": {
"tableId": "food_logs",
"fieldsUi": {
"fieldValues": [
{
"fieldId": "user_id",
"fieldValue": "={{ $node[\"Webhook\"].json[\"body\"][\"user_id\"] }}"
},
{
"fieldId": "raw_text",
"fieldValue": "={{$node[\"Webhook\"].json[\"body\"][\"message\"]}}"
},
{
"fieldId": "calories",
"fieldValue": "={{ $json[\"calories\"] }}"
},
{
"fieldId": "protein",
"fieldValue": "={{ Math.round($json[\"macros\"][\"protein\"]) }}"
},
{
"fieldId": "carbs",
"fieldValue": "={{ Math.round($json[\"macros\"][\"carbs\"]) }}"
},
{
"fieldId": "fat",
"fieldValue": "={{ Math.round($json[\"macros\"][\"fat\"]) }}"
},
{
"fieldId": "processed_json",
"fieldValue": "={{ $json.processed_json }}"
}
]
}
},
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
2896,
-80
],
"id": "eec8cb2c-cd92-47fb-9b46-fa89f2d8337b",
"name": "Create a row",
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{\n {\n \"message\": $(\"Code in JavaScript\").first().json.reply_message,\n \"saved_log\": $json\n }\n}}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
3104,
-80
],
"id": "ca68a1c2-74d7-494b-b751-beca63c088eb",
"name": "Respond to Webhook"
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"leftValue": "={{ $json.intent }}",
"rightValue": "nutrition",
"operator": {
"type": "string",
"operation": "equals"
},
"id": "d2edb468-0626-4cda-8528-8ec8cc97e63c"
}
],
"combinator": "and"
}
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "3f587364-6965-4b6c-a5bf-70835940156a",
"leftValue": "={{ $json.intent }}",
"rightValue": "workout",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
}
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "5531c4fe-c22a-4057-8bf4-47cec6fc752d",
"leftValue": "={{ $json.intent }}",
"rightValue": "update_preferences|planning|feedback",
"operator": {
"type": "string",
"operation": "regex"
}
}
],
"combinator": "and"
}
}
]
},
"options": {}
},
"type": "n8n-nodes-base.switch",
"typeVersion": 3.4,
"position": [
2576,
-16
],
"id": "86c6a698-9eee-4b8c-90d0-6a61b4b33588",
"name": "Switch"
},
{
"parameters": {
"tableId": "workout_logs",
"fieldsUi": {
"fieldValues": [
{
"fieldId": "user_id",
"fieldValue": "={{ $json.user_id }}"
},
{
"fieldId": "workout_day",
"fieldValue": "={{ $json.workout_day }}"
},
{
"fieldId": "exercises_performed",
"fieldValue": "={{ $json.exercises }}"
},
{
"fieldId": "feedback",
"fieldValue": "={{ $json.modifications }}"
},
{
"fieldId": "calories_burned",
"fieldValue": "={{ $json.calories_burned }}"
}
]
}
},
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
2896,
96
],
"id": "e6215f62-5ecb-4b05-a9d4-026896832f24",
"name": "Create a row1",
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{\n {\n \"message\": $(\"Code in JavaScript\").first().json.reply_message,\n \"saved_log\": $json\n }\n}}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
3104,
96
],
"id": "25842728-9842-4027-b24c-97f69995054b",
"name": "Respond to Webhook1"
},
{
"parameters": {
"method": "POST",
"url": "https://api.groq.com/openai/v1/chat/completions",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ $json.payload }}",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
2224,
0
],
"id": "ad95f633-1f62-40be-94f6-6430a37c32ee",
"name": "HTTP Request",
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{\n {\n \"message\": $(\"Code in JavaScript\").first().json.reply_message,\n \"status\": \"Prefer\u00eancias atualizadas com sucesso\",\n \"data_saved\": $json\n }\n}}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
3104,
288
],
"id": "19bcd80c-5e8f-4b64-9db2-9e310d2927f3",
"name": "Respond to Webhook2"
},
{
"parameters": {
"operation": "executeQuery",
"query": "{{ $json.generated_sql }}",
"options": {
"queryReplacement": "={{ [ \n $json.user_id, \n $json.goal || \"N\u00e3o definido\", \n $json.injuries || \"\", \n JSON.stringify($json.disliked_foods || []), \n JSON.stringify($json.disliked_exercises || []) \n] }}"
}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
2896,
288
],
"id": "9e3431ce-3238-4430-a695-4db72fbb0ba1",
"name": "Execute a SQL query",
"alwaysOutputData": true,
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"leftValue": "={{ $json.choices[0].message.content }}",
"rightValue": "EMERGENCY",
"operator": {
"type": "string",
"operation": "contains"
},
"id": "e69f4f23-744e-4c0a-9a93-238d23cbaef9"
}
],
"combinator": "and"
}
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "7993ae64-879c-401e-aa93-9ccdfd5d931a",
"leftValue": "={{ $json.choices[0].message.content }}",
"rightValue": "NORMAL",
"operator": {
"type": "string",
"operation": "contains"
}
}
],
"combinator": "and"
}
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "d712b985-0523-46ca-9a71-cd4617977410",
"leftValue": "={{ $json.choices[0].message.content }}",
"rightValue": "PLANNING",
"operator": {
"type": "string",
"operation": "contains"
}
}
],
"combinator": "and"
}
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "3166e0ec-6f77-42ab-8fd3-5b39a7bff043",
"leftValue": "={{ $json.choices[0].message.content }}",
"rightValue": "FEEDBACK",
"operator": {
"type": "string",
"operation": "contains"
}
}
],
"combinator": "and"
}
}
]
},
"options": {}
},
"type": "n8n-nodes-base.switch",
"typeVersion": 3.4,
"position": [
928,
-16
],
"id": "4bebc2a1-9cc4-47b0-9eb0-2b504dbba071",
"name": "Switch1"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "{\n \"message\": \"\u26a0\ufe0f ALERTA: Detectamos um poss\u00edvel caso de emerg\u00eancia m\u00e9dica. Como uma IA, n\u00e3o posso ajudar. Por favor, dirija-se a um hospital ou ligue para o 192 imediatamente.\"\n}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
1872,
-160
],
"id": "55df04b3-3128-45ce-b466-1221d83db38b",
"name": "Respond to Webhook3"
},
{
"parameters": {
"method": "POST",
"url": "https://api.groq.com/openai/v1/chat/completions",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ $json.payload }}",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
2144,
272
],
"id": "31f21fa9-a018-4940-b826-24e5e49e4b2a",
"name": "Agente Planner",
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const prefsNode = $(\"Get Preferences1\").first(); \nconst prefs = prefsNode ? prefsNode.json : {};\nconst userMsg = $(\"Webhook\").first().json.body.message;\nconst roteadorOutput = $(\"Prepara Roteador\").first().json || {}; \nconst roteadorData = roteadorOutput.payload || {}; \n\nconst plannerPrompt = `Voc\u00ea \u00e9 um Agente Planejador de Sa\u00fade (Planner).\nDiferente de um personal comum, voc\u00ea n\u00e3o prescreve treinos soltos. Voc\u00ea cria ESTRAT\u00c9GIAS de longo prazo.\n\nSUA MISS\u00c3O:\n1. Analise o Estado Inicial ($start) e o Objetivo Final ($goal).\n2. Use \"Chain of Thought\": Descreva mentalmente os passos necess\u00e1rios para reduzir a dist\u00e2ncia entre eles a zero.\n3. Gere um plano estruturado em fases/semanas.\n\nCONTEXTO DO USU\u00c1RIO:\n- Objetivo: ${prefs.goal || \"Melhorar sa\u00fade\"}\n- Les\u00f5es: ${prefs.injuries || \"Nenhuma\"}\n- Equipamentos: ${JSON.stringify(prefs.available_equipment || [])}\n\nSCHEMA DE RESPOSTA (JSON):\n{\n \"chain_of_thought\": \"Explique seu racioc\u00ednio aqui...\",\n \"project_name\": \"Nome criativo do projeto (ex: Projeto Ver\u00e3o 30 Dias)\",\n \"steps\": [\n {\n \"phase\": \"Semana 1\",\n \"focus\": \"Adapta\u00e7\u00e3o\",\n \"actions\": [\"...\"]\n }\n ]\n}`;\n\nreturn { json: { payload: {\n model: \"llama-3.3-70b-versatile\",\n messages: [\n { role: \"system\", content: plannerPrompt },\n { role: \"user\", content: `Crie o plano para: \"${userMsg}\"` }\n ],\n response_format: { type: \"json_object\" }\n} } };"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1920,
256
],
"id": "5d5866a5-6c92-4759-8e60-2aa6cad53c78",
"name": "Prepara Planner"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{\n (() => {\n // 1. Pega o texto cru que veio da IA\n const rawContent = $json.choices[0].message.content;\n \n // 2. Converte o texto em Objeto JavaScript (o \"unboxing\")\n const data = JSON.parse(rawContent);\n\n // 3. Monta a mensagem final formatada\n return {\n \"message\": \"\ud83d\uddfa\ufe0f Planejei sua jornada! Aqui est\u00e1 o \" + data.project_name + \":\\n\\n\" + \n \"\ud83e\udde0 Racioc\u00ednio: \" + data.chain_of_thought + \"\\n\\n\" +\n data.steps.map(s => \"\ud83d\udcc5 \" + s.phase + \" (\" + s.focus + \"):\\n\" + s.actions.map(a => \"- \" + a).join(\"\\n\")).join(\"\\n\\n\")\n }\n })()\n}}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
2352,
304
],
"id": "6c1e0d8e-0959-4a3d-b479-ab578bd86e47",
"name": "Respond to Webhook4"
},
{
"parameters": {
"operation": "executeQuery",
"query": "INSERT INTO ai_learning (user_id, bad_experience)\nVALUES ($1, $2);",
"options": {
"queryReplacement": "={{ [\n $node[\"Webhook\"].json.body.user_id, \n $node[\"Webhook\"].json.body.message \n] }}"
}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
1888,
496
],
"id": "ac1718fc-35ca-4052-9f60-8ff12ae2d3cb",
"name": "Execute a SQL query1",
"alwaysOutputData": true,
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{\n {\n \"message\": \"\ud83e\udee1 Entendido! Salvei essa informa\u00e7\u00e3o ('\" + $node[\"Webhook\"].json.body.message + \"') na minha mem\u00f3ria. N\u00e3o vou sugerir isso novamente nos pr\u00f3ximos treinos.\",\n \"status\": \"learned\"\n }\n}}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
2112,
496
],
"id": "34fa60b7-7837-4ace-b193-c0b3bd5f8d45",
"name": "Respond to Webhook5"
},
{
"parameters": {
"fileSelector": "/home/node/prompt/system_prompt.txt",
"options": {
"dataPropertyName": "prompt_content"
}
},
"type": "n8n-nodes-base.readWriteFile",
"typeVersion": 1.1,
"position": [
1904,
0
],
"id": "1b8ff289-9a51-45f6-a022-218f9e639a6f",
"name": "Read/Write Files from Disk"
},
{
"parameters": {
"operation": "getAll",
"tableId": "user_preferences",
"limit": 1,
"filters": {
"conditions": [
{
"keyName": "user_id",
"condition": "eq",
"keyValue": "={{ $node[\"Webhook\"].json.body.user_id }}"
}
]
}
},
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
1424,
0
],
"id": "16441ea9-8f10-4440-b7f8-5ac91d564661",
"name": "Get Preferences",
"alwaysOutputData": true,
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "getAll",
"tableId": "workout_plans",
"limit": 1,
"filters": {
"conditions": [
{
"keyName": "user_id",
"condition": "eq",
"keyValue": "={{ $node[\"Webhook\"].json.body.user_id }}"
}
]
}
},
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
1600,
0
],
"id": "e1b0d8e1-bd3b-4fff-8229-2fcd657d7da5",
"name": "Get Workout Plan",
"alwaysOutputData": true,
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "function getSafeJson(nodeName) {\n try {\n const node = $(nodeName).first();\n return (node && node.json) ? node.json : {};\n } catch (e) { return {}; }\n}\n\nconst prefs = getSafeJson(\"Get Preferences\");\nconst plan = getSafeJson(\"Get Workout Plan\"); \nconst userMsg = $(\"Webhook\").first().json.body.message;\n\nlet memories = [];\ntry {\n const learningNode = $(\"Get Learning\").first();\n if (learningNode && learningNode.json) {\n const rawMemories = $(\"Get Learning\").all().map(item => item.json.bad_experience).filter(Boolean);\n memories = [...new Set(rawMemories)]; \n }\n} catch (error) { memories = []; }\n\nconst systemPrompt = `Voc\u00ea \u00e9 a IA Principal do Health AI (Nutricionista + Personal Trainer).\nO usu\u00e1rio j\u00e1 passou por uma triagem de seguran\u00e7a inicial. Sua fun\u00e7\u00e3o \u00e9 ATENDER e ADAPTAR.\n\nOBJETIVO:\nResponder tecnicamente cruzando TREINO ATUAL, PERFIL e MEM\u00d3RIAS.\n\n---\n\ud83e\udde0 INTEN\u00c7\u00d5ES DO AGENTE (Apenas estas existem no fluxo):\n\n1. \ud83e\udd57 NUTRITION:\n - Relatos de comida (\"comi frango\", \"almo\u00e7o\").\n - A\u00c7\u00c3O: Calcular macros estimados.\n\n2. \ud83c\udf93 FEEDBACK (Prioridade M\u00e1xima):\n - Dor, desconforto, \"n\u00e3o gostei\" ou reclama\u00e7\u00f5es.\n - SEGURAN\u00c7A: Se algo grave passou pelo filtro (ex: \"dor no peito\"), use esta inten\u00e7\u00e3o para recusar educadamente e mandar para o m\u00e9dico.\n - A\u00c7\u00c3O: Registre o fato para aprendizado.\n\n3. \ud83c\udfcb\ufe0f WORKOUT (Padr\u00e3o):\n - Pedidos de treino, d\u00favidas t\u00e9cnicas ou adapta\u00e7\u00f5es.\n - Ex: \"Qual o treino A?\", \"Troca esse exerc\u00edcio\".\n\n4. \ud83d\udd04 UPDATE_PREFERENCES:\n - Mudan\u00e7a de objetivo ou perfil (\"agora quero emagrecer\").\n\n---\n\ud83d\udee1\ufe0f MEM\u00d3RIA DE DOR (LISTA NEGRA):\n[ ${memories.length > 0 ? JSON.stringify(memories) : \"Nenhum hist\u00f3rico de dor\"} ]\n\nREGRA DE ADAPTA\u00c7\u00c3O:\nSe o exerc\u00edcio pedido estiver na Lista Negra -> BLOQUEIE e SUBSTITUA.\n\n---\n\ud83d\udcdd EXEMPLOS (FEW-SHOT):\n\nEx 1 (Adapta\u00e7\u00e3o por Dor):\nContexto: Lista Negra tem \"Supino\".\nInput: \"Quero treinar peito\"\nOutput: {\n \"intent\": \"workout\",\n \"reply_message\": \"Montei seu treino! Troquei o Supino por Flex\u00f5es para poupar seu ombro.\",\n \"data\": { \"workout_day\": \"A\", \"exercises\": [\"Flex\u00e3o\", \"Crucifixo\"] }\n}\n\nEx 2 (Nutri\u00e7\u00e3o):\nInput: \"Comi uma ma\u00e7\u00e3\"\nOutput: {\n \"intent\": \"nutrition\",\n \"reply_message\": \"Registrado! Aprox 52kcal.\",\n \"data\": { \"calories\": 52, \"protein\": 0, \"carbs\": 14, \"fat\": 0 }\n}\n\n---\nCONTEXTO DO USU\u00c1RIO:\n- Objetivo: ${prefs.goal || \"Geral\"}\n- Les\u00f5es: ${prefs.injuries || \"Nenhuma\"}\n- Equipamentos: ${JSON.stringify(prefs.available_equipment || [\"Peso do corpo\"])}\n- PLANO ATUAL: ${plan.plan_structure ? JSON.stringify(plan.plan_structure) : \"Nenhum plano ativo.\"}\n\n---\nSCHEMA OBRIGAT\u00d3RIO (JSON):\n{\n \"intent\": \"nutrition\" | \"workout\" | \"feedback\" | \"update_preferences\",\n \"reply_message\": \"Texto formatado (Markdown).\",\n \"data\": {\n \"goal\": \"...\", \"injuries\": \"...\",\n \"disliked_exercises\": [],\n \"feedback_content\": \"...\",\n \"calories\": 0, \"protein\": 0, \"carbs\": 0, \"fat\": 0 // S\u00f3 p/ nutrition\n }\n}`;\n\nconst promptContexto = `INPUT DO USU\u00c1RIO: \"${userMsg}\"`;\n\nreturn { json: { payload: { \n model: \"llama-3.3-70b-versatile\", \n messages: [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: promptContexto }\n ], \n response_format: { type: \"json_object\" } \n} } };"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2064,
0
],
"id": "2b3003af-4c88-4d83-96ba-244b5012a4f1",
"name": "Preparar Prompt"
},
{
"parameters": {
"method": "POST",
"url": "https://api.groq.com/openai/v1/chat/completions",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ $json.payload }}",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
688,
0
],
"id": "04e818b6-9bca-47fd-91a3-1bd3c423fe42",
"name": "Roteador IA",
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "getAll",
"tableId": "user_preferences",
"limit": 1,
"filters": {
"conditions": [
{
"keyName": "user_id",
"condition": "eq",
"keyValue": "={{ $node[\"Webhook\"].json.body.user_id }}"
}
]
}
},
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
1648,
208
],
"id": "69af46d9-ebe5-4e45-ad27-8ed4543dd0cc",
"name": "Get Preferences1",
"alwaysOutputData": true,
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const userMsg = $(\"Webhook\").first().json.body.message;\nconst systemPrompt = `Voc\u00ea \u00e9 o Roteador Central (Dispatcher) do sistema Health AI.\nSua \u00daNICA fun\u00e7\u00e3o \u00e9 classificar a mensagem do usu\u00e1rio em uma de 3 rotas de processamento.\n\n---\nDIRETRIZES DE CLASSIFICA\u00c7\u00c3O:\n\n1. EMERGENCY (Rota de Abortar):\n - APENAS para risco iminente \u00e0 vida: Infarto, desmaio, falta de ar grave, dor no peito aguda (cora\u00e7\u00e3o).\n - IMPORTANTE: Dor muscular, dor articular ou \"me machuquei no treino\" N\u00c3O \u00c9 EMERGENCY. Isso deve ir para NORMAL.\n\n2. PLANNING (Rota do Agente Planner):\n - Pedidos de cria\u00e7\u00e3o de cronogramas, projetos de longo prazo ou desafios.\n - Ex: \"Crie um plano de 30 dias\", \"Projeto Ver\u00e3o\", \"Quero uma periodiza\u00e7\u00e3o mensal\".\n\n3. NORMAL (Rota do Agente Principal):\n - TODO O RESTO. Inclui:\n - Nutri\u00e7\u00e3o (\"comi frango\").\n - Treino (\"qual o treino A?\", \"quero treinar perna\").\n - Feedback de Dor/Treino (\"meu ombro doeu\", \"n\u00e3o gostei desse exerc\u00edcio\") -> O Agente Principal tratar\u00e1 isso.\n - Mudan\u00e7a de Perfil (\"quero ganhar massa\").\n\n---\nEXEMPLOS (FEW-SHOT):\n- \"Estou infartando\" -> EMERGENCY\n- \"Senti uma pontada no peito correndo\" -> EMERGENCY\n- \"Meu joelho doeu no agachamento\" -> NORMAL (Isso \u00e9 feedback t\u00e9cnico)\n- \"Quero um plano de 4 semanas\" -> PLANNING\n- \"Comi arroz\" -> NORMAL\n\nSA\u00cdDA ESPERADA:\nResponda APENAS a palavra da categoria (EMERGENCY, PLANNING ou NORMAL).`;\n\nreturn {\n json: {\n payload: {\n model: \"llama-3.3-70b-versatile\",\n messages: [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: userMsg }\n ],\n temperature: 0 \n }\n }\n};"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
480,
0
],
"id": "c420b4f5-a4b8-46eb-9a4d-ae3308bfed6b",
"name": "Prepara Roteador"
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT bad_experience FROM ai_learning WHERE user_id = $1",
"options": {
"queryReplacement": "={{ [ $node[\"Webhook\"].json.body.user_id ] }}"
}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
1760,
0
],
"id": "4e12ea98-80f6-4c41-b0db-f46562f6c98c",
"name": "Get Learning",
"alwaysOutputData": true,
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
}
],
"connections": {
"Webhook": {
"main": [
[
{
"node": "Prepara Roteador",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript": {
"main": [
[
{
"node": "Switch",
"type": "main",
"index": 0
}
]
]
},
"Create a row": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
},
"Switch": {
"main": [
[
{
"node": "Create a row",
"type": "main",
"index": 0
}
],
[
{
"node": "Create a row1",
"type": "main",
"index": 0
}
],
[
{
"node": "Execute a SQL query",
"type": "main",
"index": 0
}
]
]
},
"Create a row1": {
"main": [
[
{
"node": "Respond to Webhook1",
"type": "main",
"index": 0
}
]
]
},
"HTTP Request": {
"main": [
[
{
"node": "Code in JavaScript",
"type": "main",
"index": 0
}
]
]
},
"Execute a SQL query": {
"main": [
[
{
"node": "Respond to Webhook2",
"type": "main",
"index": 0
}
]
]
},
"Switch1": {
"main": [
[
{
"node": "Respond to Webhook3",
"type": "main",
"index": 0
}
],
[
{
"node": "Get Preferences",
"type": "main",
"index": 0
}
],
[
{
"node": "Get Preferences1",
"type": "main",
"index": 0
}
],
[
{
"node": "Execute a SQL query1",
"type": "main",
"index": 0
}
]
]
},
"Prepara Planner": {
"main": [
[
{
"node": "Agente Planner",
"type": "main",
"index": 0
}
]
]
},
"Agente Planner": {
"main": [
[
{
"node": "Respond to Webhook4",
"type": "main",
"index": 0
}
]
]
},
"Execute a SQL query1": {
"main": [
[
{
"node": "Respond to Webhook5",
"type": "main",
"index": 0
}
]
]
},
"Read/Write Files from Disk": {
"main": [
[
{
"node": "Preparar Prompt",
"type": "main",
"index": 0
}
]
]
},
"Get Preferences": {
"main": [
[
{
"node": "Get Workout Plan",
"type": "main",
"index": 0
}
]
]
},
"Get Workout Plan": {
"main": [
[
{
"node": "Get Learning",
"type": "main",
"index": 0
}
]
]
},
"Preparar Prompt": {
"main": [
[
{
"node": "HTTP Request",
"type": "main",
"index": 0
}
]
]
},
"Roteador IA": {
"main": [
[
{
"node": "Switch1",
"type": "main",
"index": 0
}
]
]
},
"Get Preferences1": {
"main": [
[
{
"node": "Prepara Planner",
"type": "main",
"index": 0
}
]
]
},
"Prepara Roteador": {
"main": [
[
{
"node": "Roteador IA",
"type": "main",
"index": 0
}
]
]
},
"Get Learning": {
"main": [
[
{
"node": "Read/Write Files from Disk",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1",
"availableInMCP": false
},
"versionId": "c1926d6a-f35f-4a40-aa61-5bac4687db2f",
"meta": {
"templateCredsSetupCompleted": true
},
"id": "D9oE2I7dzl8SVah3Ud-sh",
"tags": []
}
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.
httpHeaderAuthpostgressupabaseApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
n8n-supabase-ai-pipeline. Uses supabase, httpRequest, postgres, readWriteFile. Webhook trigger; 25 nodes.
Source: https://github.com/clivissonjose/saude-ia-backend/blob/bcb41d1c0466de0dc747714095dfc340e1613d8a/workflows/main_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.
Postgres. Uses openAi, postgres, postgresTool, httpRequest. Webhook trigger; 19 nodes.
CMM. Uses httpRequest, postgres, redis. Webhook trigger; 90 nodes.
Scraping. Uses httpRequest, postgres, @apify/n8n-nodes-apify, respondToWebhook. Webhook trigger; 61 nodes.
Workflow B — AI Listing Engine. Uses httpRequest, postgres, errorTrigger. Webhook trigger; 47 nodes.
LogSentinel Workflow. Uses postgres, emailSend, httpRequest. Webhook trigger; 44 nodes.