AutomationFlowsData & Sheets › N8n Supabase AI Pipeline

N8n Supabase AI Pipeline

n8n-supabase-ai-pipeline. Uses supabase, httpRequest, postgres, readWriteFile. Webhook trigger; 25 nodes.

Webhook trigger★★★★☆ complexity25 nodesSupabaseHTTP RequestPostgresRead Write File
Data & Sheets Trigger: Webhook Nodes: 25 Complexity: ★★★★☆ Added:

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 →

Download .json
{
  "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.

Pro

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 →

More Data & Sheets workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Data & Sheets

Postgres. Uses openAi, postgres, postgresTool, httpRequest. Webhook trigger; 19 nodes.

OpenAI, Postgres, Postgres Tool +2
Data & Sheets

CMM. Uses httpRequest, postgres, redis. Webhook trigger; 90 nodes.

HTTP Request, Postgres, Redis
Data & Sheets

Scraping. Uses httpRequest, postgres, @apify/n8n-nodes-apify, respondToWebhook. Webhook trigger; 61 nodes.

HTTP Request, Postgres, @Apify/N8N Nodes Apify
Data & Sheets

Workflow B — AI Listing Engine. Uses httpRequest, postgres, errorTrigger. Webhook trigger; 47 nodes.

HTTP Request, Postgres, Error Trigger
Data & Sheets

LogSentinel Workflow. Uses postgres, emailSend, httpRequest. Webhook trigger; 44 nodes.

Postgres, Email Send, HTTP Request