AutomationFlowsSocial Media › Ai Assistant Tax Workflow

Ai Assistant Tax Workflow

Ai Assistant Tax Workflow. Uses supabase, httpRequest. Webhook trigger; 9 nodes.

Webhook trigger★★★★☆ complexity9 nodesSupabaseHttp Request
Social Media Trigger: Webhook Nodes: 9 Complexity: ★★★★☆

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
{
  "nodes": [
    {
      "parameters": {
        "jsCode": "const webhookData = $input.first().json;\nconst messages = webhookData.body?.messages || [];\nconst lastUserMsg = messages.filter(m => m.role === 'user').pop()?.content || '';\nconst text = lastUserMsg.toLowerCase();\n\n// Intent: Tax Calculation\nconst taxKeywords = ['ganancias', 'sueldo', 'bruto', 'neto', 'impuesto', 'calculadora', 'deducciones', 'pago de'];\nconst isTaxQuery = taxKeywords.some(kw => text.includes(kw));\n\n// Intent: Short/Greeting\nconst isShort = text.length < 60;\nconst hasQuestion = text.includes('?');\nconst isJustEmail = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/.test(text) && text.length < 80;\n\nlet requires_rag = true;\nlet intent = 'general';\n\nif (isTaxQuery) {\n    intent = 'tax';\n    requires_rag = false;\n} else if ((isShort && !hasQuestion && isJustEmail) || ['hola', 'hi', 'hello', 'buenos d\u00edas', 'gracias', 'thanks'].includes(text.trim())) {\n    requires_rag = false;\n    intent = 'greeting';\n}\n\nreturn [{\n    json: {\n        ...webhookData,\n        requires_rag,\n        intent\n    }\n}];"
      },
      "id": "1eff4a18-b4d0-4cb2-882a-4b95c4b6bd35",
      "name": "Analyze Intent",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -2272,
        1104
      ]
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 1
                },
                "conditions": [
                  {
                    "id": "cond1",
                    "leftValue": "={{ $json.intent }}",
                    "rightValue": "tax",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "tax"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 1
                },
                "conditions": [
                  {
                    "id": "cond2",
                    "leftValue": "={{ $json.requires_rag }}",
                    "rightValue": "={{ true }}",
                    "operator": {
                      "type": "boolean",
                      "operation": "true"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "rag"
            }
          ]
        },
        "fallbackOutput": 2
      },
      "id": "15d55ac8-06ae-4d22-8d1a-8f0d40e4e43f",
      "name": "Router by Intent",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3,
      "position": [
        -2048,
        1104
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "mga-ai-agent",
        "responseMode": "responseNode",
        "options": {
          "allowedOrigins": "https://www.mgatc.com,https://mgatc.pages.dev,http://localhost:3000,http://localhost:3001,https://mgatc.com"
        }
      },
      "id": "ed1c921c-da89-452e-8411-0797d3a29ea6",
      "name": "Webhook Trigger1",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        -2496,
        1104
      ]
    },
    {
      "parameters": {
        "jsCode": "const response = $input.first().json;\nconst error = response.error;\nconst assistantMessage = error ? '' : (response.choices?.[0]?.message?.content || '');\n\nconst webhookBody = $('Webhook Trigger1').first().json.body;\nconst allMessages = webhookBody.messages || [];\nconst conversationStr = JSON.stringify(allMessages);\n\nlet email = webhookBody.user_info?.email || null;\nlet name = webhookBody.user_info?.name || null;\n\nif (!email) {\n    const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g;\n    const emails = conversationStr.match(emailRegex) || [];\n    email = emails[0] || null;\n}\n\nconst systemMsg = allMessages.find(m => m.role === 'system');\nconst sourceLang = systemMsg?.content?.includes('currently set to English') ? 'en' : 'es';\n\nlet chatHtml = \"\";\ntry {\n    const chatMessages = allMessages.filter(m => m.role !== 'system').slice(-6);\n    if (chatMessages.length > 0) {\n        chatHtml = chatMessages.map(msg => {\n            const isUser = msg.role === 'user';\n            const label = isUser ? 'Usuario' : 'Asistente';\n            const bgColor = isUser ? '#f8fafc' : '#ffffff';\n            const borderColor = isUser ? '#2563eb' : '#10b981';\n            return `<div style=\"margin-bottom: 12px; padding: 12px; border-radius: 8px; background-color: ${bgColor}; border-left: 4px solid ${borderColor}; font-family: sans-serif;\"><strong style=\"display: block; font-size: 11px; color: #64748b; text-transform: uppercase; margin-bottom: 4px;\">${label}</strong><div style=\"font-size: 14px; color: #1e293b; line-height: 1.5;\">${msg.content}</div></div>`;\n        }).join('');\n    } else {\n        chatHtml = \"<p>No hay mensajes recientes.</p>\";\n    }\n} catch (e) { chatHtml = \"<p>Error.</p>\"; }\n\nreturn [{\n    json: {\n        error,\n        assistantMessage,\n        email,\n        name,\n        sourceLang,\n        hasLead: !!(email || name),\n        chatHtml,\n        conversation: allMessages.slice(-6)\n    }\n}];"
      },
      "id": "e3476c48-cc05-42ab-bf9e-bef99b7db74c",
      "name": "Extract Lead Data1",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1152,
        1104
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ { content: $json.assistantMessage || ($json.error ? 'Error: ' + $json.error.message : 'No se pudo obtener respuesta de la IA.') } }}",
        "options": {}
      },
      "id": "8930e3b4-b2a2-4bf5-a652-53e8a699fede",
      "name": "Respond to Frontend2",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        -928,
        1008
      ]
    },
    {
      "parameters": {
        "operation": "select",
        "schema": "public",
        "tableId": "tax_calculator_params",
        "filtersUi": {
          "conditions": [
            {
              "id": "[ID]",
              "leftValue": "id",
              "rightValue": 1,
              "operator": "eq"
            }
          ]
        }
      },
      "id": "tax-p-1",
      "name": "Get Tax Params",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        -1900,
        800
      ],
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "select",
        "schema": "public",
        "tableId": "tax_scale_brackets",
        "additionalOptions": {
          "sort": [
            {
              "column": "bracket_order",
              "direction": "asc"
            }
          ]
        }
      },
      "id": "tax-b-1",
      "name": "Get Tax Brackets",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        -1900,
        950
      ],
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const params = $('Get Tax Params').first().json;\nconst brackets = $('Get Tax Brackets').all().map(i => i.json);\nconst webhookData = $('Webhook Trigger1').first().json;\nconst lastMsg = webhookData.body.messages.filter(m => m.role === 'user').pop().content;\n\n// Extraer monto\nconst amountMatch = lastMsg.match(/(\\d+[\\d\\.,]*)/);\nif (!amountMatch) return { json: { ...webhookData, tax_calc: null, needs_data: true } };\n\nlet brutoMensual = parseFloat(amountMatch[0].replace(/\\./g, '').replace(',', '.'));\nif (brutoMensual < 100) brutoMensual *= 1000000;\nelse if (brutoMensual < 10000) brutoMensual *= 1000;\n\n// Deducciones Familiares (Regex simple)\nlet hijos = 0;\nconst hMatch = lastMsg.match(/(\\d+)\\s*hijo/i);\nif (hMatch) hijos = parseInt(hMatch[1]);\nelse if (lastMsg.includes('un hijo')) hijos = 1;\n\nconst conyuge = lastMsg.toLowerCase().includes('conyuge') || lastMsg.toLowerCase().includes('pareja');\n\n// C\u00e1lculo\nconst gni = parseFloat(params.gni);\nconst especial = parseFloat(params.deduccion_especial);\nconst aportesPct = parseFloat(params.aportes_pct);\n\nconst netoAnual = (brutoMensual * 13) * (1 - aportesPct);\nlet deducciones = gni + especial;\ndeducciones += hijos * parseFloat(params.hijo);\nif (conyuge) deducciones += parseFloat(params.conyuge);\n\nconst base = Math.max(0, netoAnual - deducciones);\n\nlet impuestoAnual = 0;\nif (base > 0) {\n    let bracket = brackets[0];\n    for (const b of brackets) {\n        if (base > parseFloat(b.limit_amount)) bracket = b;\n        else break;\n    }\n    const exceso = base - parseFloat(bracket.limit_amount);\n    impuestoAnual = parseFloat(bracket.fixed_amount) + (exceso * parseFloat(bracket.pct));\n}\n\nreturn [{\n    json: {\n        ...webhookData,\n        tax_calc: {\n            bruto: brutoMensual,\n            netoMensual: (netoAnual - impuestoAnual) / 13,\n            impuestoMensual: impuestoAnual / 12,\n            base,\n            hijos,\n            conyuge\n        }\n    }\n}];"
      },
      "id": "tax-c-1",
      "name": "Tax Calculator Logic",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1600,
        900
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://router.huggingface.co/v1/chat/completions",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ \n  JSON.stringify({ \n    messages: (function() { \n      const data = $input.first().json; \n      const history = data.body.messages; \n      const calc = data.tax_calc; \n      const systemMsg = history.find(m => m.role === 'system'); \n      const otherMsgs = history.filter(m => m.role !== 'system'); \n      let taxContext = \"\";\n      if (calc) {\n          taxContext = `\\n\\nCALCULATION RESULT (Usa esto para responder de forma amigable):\\nSueldo Bruto Mensual: $${Math.round(calc.bruto).toLocaleString()}\\nNeto Estimado (con Ganancias y Aportes): $${Math.round(calc.netoMensual).toLocaleString()}\\nRetenci\u00f3n mensual Ganancias: $${Math.round(calc.impuestoMensual).toLocaleString()}\\nDeducciones aplicadas: ${calc.hijos} hijos, ${calc.conyuge ? 'C\u00f3nyuge' : 'Sin c\u00f3nyuge'}.\\nExplica que es un c\u00e1lculo de Ganancias 2026 y ofrece contactar a Mariano para optimizaci\u00f3n financiera [ACTION:CONTACT].`;\n      } else {\n          taxContext = \"\\n\\nINSTRUCCI\u00d3N: El usuario quiere calcular su sueldo pero no proporcion\u00f3 el monto bruto. P\u00eddele amablemente el sueldo bruto mensual y si tiene hijos o c\u00f3nyuge para ser exacto.\";\n      }\n      return [ \n        { role: 'system', content: (systemMsg?.content || '') + taxContext }, \n        ...otherMsgs \n      ]; \n    })(), \n    model: 'zai-org/GLM-4.7-Flash:novita', \n    stream: false \n  }) \n}}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        -1376,
        900
      ],
      "id": "tax-h-1",
      "name": "Call HF Tax Response",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Webhook Trigger1": {
      "main": [
        [
          {
            "node": "Analyze Intent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyze Intent": {
      "main": [
        [
          {
            "node": "Router by Intent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Router by Intent": {
      "main": [
        [
          {
            "node": "Get Tax Params",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Hugging Face Embedding Model",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Call Hugging Face General",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Tax Params": {
      "main": [
        [
          {
            "node": "Get Tax Brackets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Tax Brackets": {
      "main": [
        [
          {
            "node": "Tax Calculator Logic",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tax Calculator Logic": {
      "main": [
        [
          {
            "node": "Call HF Tax Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Call HF Tax Response": {
      "main": [
        [
          {
            "node": "Extract Lead Data1",
            "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.

About this workflow

Ai Assistant Tax Workflow. Uses supabase, httpRequest. Webhook trigger; 9 nodes.

Source: https://github.com/Mgobeaalcoba/Mgobeaalcoba.github.io/blob/main/docs/automations/ai_assistant_tax_workflow.json — original creator credit. Request a take-down →

More Social Media workflows → · Browse all categories →