AutomationFlowsAI & RAG › Amicana Chatbot — Cuotas (gemini)

Amicana Chatbot — Cuotas (gemini)

AMICANA Chatbot — Cuotas (Gemini). Uses httpRequest. Webhook trigger; 21 nodes.

Webhook trigger★★★★☆ complexity21 nodesHTTP Request
AI & RAG Trigger: Webhook Nodes: 21 Complexity: ★★★★☆ Added:

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": "AMICANA Chatbot \u2014 Cuotas (Gemini)",
  "nodes": [
    {
      "id": "node-webhook",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        240,
        300
      ],
      "parameters": {
        "httpMethod": "POST",
        "path": "amicana-chatbot",
        "responseMode": "responseNode",
        "options": {}
      }
    },
    {
      "id": "node-get-history",
      "name": "Get Session History",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        400,
        300
      ],
      "parameters": {
        "jsCode": "const body = $input.first().json.body;\nconst sessionId = body.session_id || '';\nconst message   = body.message   || '';\n\nif (!sessionId || !message) {\n  throw new Error('session_id and message are required');\n}\n\nreturn [{ json: { session_id: sessionId, message: message } }];"
      }
    },
    {
      "id": "node-load-session",
      "name": "Load Session API",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        560,
        300
      ],
      "parameters": {
        "method": "GET",
        "url": "=https://proyecto-amicana-20-production.up.railway.app/chatbot/session/{{ $json.session_id }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Chatbot-Key",
              "value": "={{ $env.CHATBOT_INTERNAL_KEY }}"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      }
    },
    {
      "id": "node-hydrate-session",
      "name": "Hydrate Session",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        720,
        300
      ],
      "parameters": {
        "jsCode": "const apiResp = $input.first().json;\nconst body    = $('Get Session History').first().json;\nconst loaded  = (apiResp && apiResp.session) || null;\n\nconst session = loaded || { history: [], alumno_id: null, estado: 'sin_autenticar', intentos_auth: 0 };\n\nreturn [{\n  json: {\n    session_id: body.session_id,\n    message: body.message,\n    history: session.history || [],\n    alumno_id: session.alumno_id || null,\n    session_state: session.estado || 'sin_autenticar',\n    intentos_auth: session.intentos_auth || 0\n  }\n}];"
      }
    },
    {
      "id": "node-gemini-router",
      "name": "Gemini Router",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        880,
        300
      ],
      "parameters": {
        "method": "POST",
        "url": "=https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key={{ $env.GEMINI_API_KEY }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"system_instruction\": {\n    \"parts\": [{\"text\": \"Sos Ianna, la asistente virtual de AMICANA (Instituto Cultural Argentino Norteamericano). Sos amigable, concisa y habl\u00e1s en espa\u00f1ol rioplatense.\\n\\nResponde SIEMPRE con JSON con estas claves exactas: intent, params, response.\\n\\nINTENTS DISPONIBLES:\\nAUTH: el alumno quiere identificarse o pide info personal sin estar autenticado. Si dio DNI ponelo en params.dni, si dio email en params.email.\\nESTADO: quiere ver sus cuotas o deuda. Solo si ya est\u00e1 autenticado.\\nPAGAR: quiere pagar una cuota. Si indic\u00f3 cuota_id ponelo en params.cuota_id.\\nCONFIRMAR: confirma que ya pag\u00f3. params debe tener cuota_id y comprobante.\\nCERRAR: el alumno se despide.\\nFUERA_SCOPE: para CUALQUIER otra consulta (saludos, horarios, cursos, modalidades, ex\u00e1menes, inscripciones, preguntas generales). Respond\u00e9 de forma \u00fatil en el campo response usando esta informaci\u00f3n real:\\n  - Horarios: lunes a viernes 8:30 a 21:00hs, s\u00e1bados 8:30 a 12:30hs\\n  - Modalidades: presencial, virtual e h\u00edbrida\\n  - Cursos: English A1 Beginners, B1 Intermediate, C1 Advanced, Conversation Class\\n  - Ex\u00e1menes internacionales: ECECE (nivel A2), TOEIC Bridge (A2), TOEIC (B1), TOEFL iBT (B2)\\n  - Inscripciones: presencialmente en la sede o consultando por este chat\\n  - Para consultar cuotas o pagar necesit\u00e1s identificarte con DNI o email\\n\\nREGLAS:\\n- Si no est\u00e1 autenticado y pide ESTADO, PAGAR o CONFIRMAR \u2192 us\u00e1 AUTH\\n- Para saludos y preguntas generales \u2192 FUERA_SCOPE con respuesta \u00fatil en response\\n- No inventes datos fuera de lo indicado\\n\\nEstado sesi\u00f3n: {{ $json.session_state }}\\nAlumno ID: {{ $json.alumno_id || 'ninguno' }}\"}]\n  },\n  \"contents\": {{ JSON.stringify($json.history.concat([{\"role\":\"user\",\"parts\":[{\"text\":$json.message}]}])) }},\n  \"generationConfig\": {\n    \"temperature\": 0.1,\n    \"maxOutputTokens\": 512,\n    \"responseMimeType\": \"application/json\"\n  }\n}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      }
    },
    {
      "id": "node-parse-intent",
      "name": "Parse Intent",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1080,
        300
      ],
      "parameters": {
        "jsCode": "const raw = $input.first().json.candidates?.[0]?.content?.parts?.[0]?.text || '{}';\nlet parsed;\ntry {\n  const clean = raw.replace(/```json?\\n?/g, '').replace(/```/g, '').trim();\n  parsed = JSON.parse(clean);\n} catch (e) {\n  parsed = {\n    intent: 'FUERA_SCOPE',\n    params: {},\n    response: 'Perdon, no pude procesar tu consulta. Pod\u00e9s repetirla de otra forma?'\n  };\n}\n\nconst prev = $('Hydrate Session').first().json;\nreturn [{\n  json: {\n    intent: (parsed.intent || 'FUERA_SCOPE').toUpperCase(),\n    params: parsed.params || {},\n    response: parsed.response || '',\n    session_id: prev.session_id,\n    message: prev.message,\n    history: prev.history,\n    alumno_id: prev.alumno_id,\n    session_state: prev.session_state,\n    intentos_auth: prev.intentos_auth\n  }\n}];"
      }
    },
    {
      "id": "node-switch",
      "name": "Intent Switch",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3,
      "position": [
        1280,
        300
      ],
      "parameters": {
        "mode": "rules",
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": false,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "conditions": [
                  {
                    "id": "cond-auth",
                    "leftValue": "={{ $json.intent }}",
                    "rightValue": "AUTH",
                    "operator": {
                      "type": "string",
                      "operation": "equals",
                      "singleValue": true
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "AUTH"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": false,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "conditions": [
                  {
                    "id": "cond-estado",
                    "leftValue": "={{ $json.intent }}",
                    "rightValue": "ESTADO",
                    "operator": {
                      "type": "string",
                      "operation": "equals",
                      "singleValue": true
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "ESTADO"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": false,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "conditions": [
                  {
                    "id": "cond-pagar",
                    "leftValue": "={{ $json.intent }}",
                    "rightValue": "PAGAR",
                    "operator": {
                      "type": "string",
                      "operation": "equals",
                      "singleValue": true
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "PAGAR"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": false,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "conditions": [
                  {
                    "id": "cond-confirmar",
                    "leftValue": "={{ $json.intent }}",
                    "rightValue": "CONFIRMAR",
                    "operator": {
                      "type": "string",
                      "operation": "equals",
                      "singleValue": true
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "CONFIRMAR"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": false,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "conditions": [
                  {
                    "id": "cond-cerrar",
                    "leftValue": "={{ $json.intent }}",
                    "rightValue": "CERRAR",
                    "operator": {
                      "type": "string",
                      "operation": "equals",
                      "singleValue": true
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "CERRAR"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": false,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "conditions": [
                  {
                    "id": "cond-fuera",
                    "leftValue": "={{ $json.intent }}",
                    "rightValue": "FUERA_SCOPE",
                    "operator": {
                      "type": "string",
                      "operation": "equals",
                      "singleValue": true
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "FUERA_SCOPE"
            }
          ]
        },
        "fallbackOutput": "5",
        "options": {}
      }
    },
    {
      "id": "node-auth-api",
      "name": "Auth \u2014 Buscar Alumno",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1500,
        60
      ],
      "parameters": {
        "method": "GET",
        "url": "=https://proyecto-amicana-20-production.up.railway.app/alumnos/buscar?{{ $json.params.dni ? 'dni=' + $json.params.dni : 'email=' + ($json.params.email || '') }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Chatbot-Key",
              "value": "={{ $env.CHATBOT_INTERNAL_KEY }}"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      }
    },
    {
      "id": "node-auth-format",
      "name": "Auth \u2014 Format Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1700,
        60
      ],
      "parameters": {
        "jsCode": "const apiResult = $input.first().json;\nconst prev = $('Parse Intent').first().json;\nconst session = { history: prev.history || [], alumno_id: prev.alumno_id, estado: prev.session_state || 'sin_autenticar', intentos_auth: prev.intentos_auth || 0 };\n\nlet text;\n\nif (apiResult.ok && apiResult.alumno) {\n  const a = apiResult.alumno;\n  text = `\u00a1Hola, ${a.nombre}! Te identifiqu\u00e9 correctamente. Pod\u00e9s consultar tus cuotas, pagar una cuota o confirmar un pago ya realizado.`;\n  session.estado = 'autenticado';\n  session.alumno_id = a.id;\n  session.intentos_auth = 0;\n} else {\n  session.intentos_auth = (session.intentos_auth || 0) + 1;\n  if (session.intentos_auth >= 3) {\n    text = 'No pude identificarte luego de 3 intentos. Comunicate con la secretaria de AMICANA. \u00a1Hasta luego!';\n    session.estado = 'cerrado';\n  } else {\n    text = `No encontr\u00e9 ning\u00fan alumno con esos datos (intento ${session.intentos_auth}/3). \u00bfPod\u00e9s verificar tu DNI o email e intentar de nuevo?`;\n  }\n}\n\nconst updated = (prev.history || []).concat(\n  { role: 'user',  parts: [{ text: prev.message }] },\n  { role: 'model', parts: [{ text: text }] }\n);\nconst MAX_TURNS = 10;\nsession.history = updated.slice(-MAX_TURNS * 2);\n\nreturn [{ json: { text, qr_url: null, pdf_url: null, session_id: prev.session_id, _session: { session_id: prev.session_id, alumno_id: session.alumno_id, estado: session.estado, intentos_auth: session.intentos_auth, history: session.history } } }];"
      }
    },
    {
      "id": "node-estado-api",
      "name": "Estado \u2014 Cuotas Alumno",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1500,
        200
      ],
      "parameters": {
        "method": "GET",
        "url": "=https://proyecto-amicana-20-production.up.railway.app/alumnos/{{ $json.alumno_id }}/cuotas",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Chatbot-Key",
              "value": "={{ $env.CHATBOT_INTERNAL_KEY }}"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      }
    },
    {
      "id": "node-estado-format",
      "name": "Estado \u2014 Format Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1700,
        200
      ],
      "parameters": {
        "jsCode": "const apiResult = $input.first().json;\nconst prev = $('Parse Intent').first().json;\nconst session = { history: prev.history || [], alumno_id: prev.alumno_id, estado: prev.session_state || 'autenticado', intentos_auth: prev.intentos_auth || 0 };\n\nlet text;\nif (!prev.alumno_id) {\n  text = 'Primero necesito identificarte. \u00bfCu\u00e1l es tu DNI o email?';\n} else if (apiResult.ok) {\n  const r = apiResult.resumen;\n  if (r.estado === 'Al d\u00eda') {\n    text = `\u00a1Est\u00e1s al d\u00eda! Ten\u00e9s ${r.cuotas_pagadas} cuota(s) paga(s). No hay deuda pendiente.`;\n  } else {\n    const lista = (apiResult.pendientes || []).map(c => `\u2022 ${c.concepto}: $${c.monto} (vence ${c.fecha_vencimiento})`).join('\\n');\n    text = `Ten\u00e9s deuda por $${r.deuda_total}. Cuotas pendientes:\\n${lista}\\n\\n\u00bfQuer\u00e9s pagar alguna? Decime el n\u00famero de cuota.`;\n  }\n} else {\n  text = 'No pude obtener tu estado de cuenta. Intent\u00e1 de nuevo en unos segundos.';\n}\n\nconst updated = (prev.history || []).concat(\n  { role: 'user',  parts: [{ text: prev.message }] },\n  { role: 'model', parts: [{ text: text }] }\n);\nconst MAX_TURNS = 10;\nsession.history = updated.slice(-MAX_TURNS * 2);\n\nreturn [{ json: { text, qr_url: null, pdf_url: null, session_id: prev.session_id, _session: { session_id: prev.session_id, alumno_id: session.alumno_id, estado: session.estado, intentos_auth: session.intentos_auth, history: session.history } } }];"
      }
    },
    {
      "id": "node-pagar-mp",
      "name": "Pagar \u2014 Crear Pago MP",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1500,
        340
      ],
      "parameters": {
        "method": "POST",
        "url": "=https://proyecto-amicana-20-production.up.railway.app/pagar-cuota/{{ $('Parse Intent').first().json.params.cuota_id || 0 }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "X-Chatbot-Key",
              "value": "={{ $env.CHATBOT_INTERNAL_KEY }}"
            },
            {
              "name": "X-Chatbot-Session-Id",
              "value": "={{ $('Get Session History').first().json.session_id }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={ \"alumno_id\": {{ $('Parse Intent').first().json.alumno_id || 0 }} }",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      }
    },
    {
      "id": "node-pagar-pdf",
      "name": "Pagar \u2014 Generar PDF",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1500,
        430
      ],
      "parameters": {
        "method": "POST",
        "url": "=https://proyecto-amicana-20-production.up.railway.app/pagos/generar-factura-pdf",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "X-Chatbot-Key",
              "value": "={{ $env.CHATBOT_INTERNAL_KEY }}"
            },
            {
              "name": "X-Chatbot-Session-Id",
              "value": "={{ $('Get Session History').first().json.session_id }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={ \"cuota_id\": {{ $('Parse Intent').first().json.params.cuota_id || 0 }}, \"alumno_id\": {{ $('Parse Intent').first().json.alumno_id || 0 }} }",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      }
    },
    {
      "id": "node-pagar-format",
      "name": "Pagar \u2014 Format Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1700,
        385
      ],
      "parameters": {
        "jsCode": "const pdfResult = $('Pagar \u2014 Generar PDF').first().json;\nconst mpResult  = $('Pagar \u2014 Crear Pago MP').first().json;\nconst prev = $('Parse Intent').first().json;\nconst session = { history: prev.history || [], alumno_id: prev.alumno_id, estado: prev.session_state || 'autenticado', intentos_auth: prev.intentos_auth || 0 };\n\nlet text, qr_url = null, pdf_url = null;\n\nif (!prev.alumno_id) {\n  text = 'Primero necesito identificarte. \u00bfCu\u00e1l es tu DNI o email?';\n} else if (!prev.params?.cuota_id) {\n  text = 'Para generar el pago necesito saber qu\u00e9 cuota quer\u00e9s abonar. \u00bfPod\u00e9s decirme el n\u00famero de cuota?';\n} else if (mpResult.ok) {\n  const link = mpResult.sandbox_init_point || mpResult.init_point || null;\n  const monto = mpResult.monto || '';\n  text = monto ? `Gener\u00e9 el enlace de pago por $${monto}.` : 'Gener\u00e9 el enlace de pago.';\n  if (link) text += ` Pag\u00e1 con MercadoPago: ${link}`;\n  if (pdfResult.ok && pdfResult.pdf_url) {\n    pdf_url = pdfResult.pdf_url;\n    text += ' Tambi\u00e9n pod\u00e9s descargar tu comprobante en PDF.';\n  }\n} else {\n  text = 'No pude generar el pago en este momento. Intent\u00e1 de nuevo o contact\u00e1 a secretar\u00eda.';\n}\n\nconst updated = (prev.history || []).concat(\n  { role: 'user',  parts: [{ text: prev.message }] },\n  { role: 'model', parts: [{ text: text }] }\n);\nconst MAX_TURNS = 10;\nsession.history = updated.slice(-MAX_TURNS * 2);\n\nreturn [{ json: { text, qr_url, pdf_url, session_id: prev.session_id, _session: { session_id: prev.session_id, alumno_id: session.alumno_id, estado: session.estado, intentos_auth: session.intentos_auth, history: session.history } } }];"
      }
    },
    {
      "id": "node-confirmar-api",
      "name": "Confirmar \u2014 Registrar Pago",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1500,
        555
      ],
      "parameters": {
        "method": "POST",
        "url": "=https://proyecto-amicana-20-production.up.railway.app/pagos/confirmar-manual",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "X-Chatbot-Key",
              "value": "={{ $env.CHATBOT_INTERNAL_KEY }}"
            },
            {
              "name": "X-Chatbot-Session-Id",
              "value": "={{ $('Get Session History').first().json.session_id }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"alumno_id\":   {{ $json.alumno_id || 0 }},\n  \"cuota_id\":    {{ $json.params.cuota_id || 0 }},\n  \"comprobante\": \"{{ $json.params.comprobante || 'Sin comprobante' }}\"\n}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      }
    },
    {
      "id": "node-confirmar-format",
      "name": "Confirmar \u2014 Format Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1700,
        555
      ],
      "parameters": {
        "jsCode": "const apiResult = $input.first().json;\nconst prev = $('Parse Intent').first().json;\nconst session = { history: prev.history || [], alumno_id: prev.alumno_id, estado: prev.session_state || 'autenticado', intentos_auth: prev.intentos_auth || 0 };\n\nlet text;\nif (!prev.alumno_id) {\n  text = 'Primero necesito identificarte. \u00bfCu\u00e1l es tu DNI o email?';\n} else if (apiResult.ok) {\n  text = apiResult.mensaje || 'Tu confirmaci\u00f3n de pago fue registrada. Un administrador la validar\u00e1 pronto.';\n} else {\n  text = `No pude registrar la confirmaci\u00f3n: ${apiResult.detail || 'error desconocido'}. Revis\u00e1 los datos e intent\u00e1 de nuevo.`;\n}\n\nconst updated = (prev.history || []).concat(\n  { role: 'user',  parts: [{ text: prev.message }] },\n  { role: 'model', parts: [{ text: text }] }\n);\nconst MAX_TURNS = 10;\nsession.history = updated.slice(-MAX_TURNS * 2);\n\nreturn [{ json: { text, qr_url: null, pdf_url: null, session_id: prev.session_id, _session: { session_id: prev.session_id, alumno_id: session.alumno_id, estado: session.estado, intentos_auth: session.intentos_auth, history: session.history } } }];"
      }
    },
    {
      "id": "node-cerrar-format",
      "name": "Cerrar \u2014 Format Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1700,
        670
      ],
      "parameters": {
        "jsCode": "const prev = $('Parse Intent').first().json;\nconst text = prev.response || '\u00a1Hasta luego! Si necesit\u00e1s ayuda de nuevo, estoy por ac\u00e1.';\n// Cerrar marca la sesi\u00f3n y deja el history como estaba (sin agregar m\u00e1s turnos).\nreturn [{ json: { text, qr_url: null, pdf_url: null, session_id: prev.session_id, _session: { session_id: prev.session_id, alumno_id: prev.alumno_id, estado: 'cerrado', intentos_auth: prev.intentos_auth || 0, history: prev.history || [] } } }];"
      }
    },
    {
      "id": "node-fuera-scope-format",
      "name": "Fuera Scope \u2014 Format Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1700,
        785
      ],
      "parameters": {
        "jsCode": "const prev = $('Parse Intent').first().json;\nconst session = { history: prev.history || [], alumno_id: prev.alumno_id, estado: prev.session_state || 'sin_autenticar', intentos_auth: prev.intentos_auth || 0 };\n\nconst text = prev.response || 'Solo puedo ayudarte con: consultar tu estado de cuenta, pagar una cuota o confirmar un pago. \u00bfCon cu\u00e1l quer\u00e9s continuar?';\n\nconst updated = (prev.history || []).concat(\n  { role: 'user',  parts: [{ text: prev.message }] },\n  { role: 'model', parts: [{ text: text }] }\n);\nconst MAX_TURNS = 10;\nsession.history = updated.slice(-MAX_TURNS * 2);\n\nreturn [{ json: { text, qr_url: null, pdf_url: null, session_id: prev.session_id, _session: { session_id: prev.session_id, alumno_id: session.alumno_id, estado: session.estado, intentos_auth: session.intentos_auth, history: session.history } } }];"
      }
    },
    {
      "id": "node-save-session",
      "name": "Save Session API",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1880,
        400
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json;\nconst sessionData = data._session;\n\nif (sessionData) {\n  try {\n    await $helpers.httpRequest({\n      method: 'POST',\n      url: 'https://proyecto-amicana-20-production.up.railway.app/chatbot/session',\n      headers: {\n        'Content-Type': 'application/json',\n        'X-Chatbot-Key': $env.CHATBOT_INTERNAL_KEY || 'amicana-internal'\n      },\n      body: sessionData,\n      timeout: 8000\n    });\n  } catch(e) {\n    // non-fatal\n  }\n}\n\nreturn [{ json: {\n  text: data.text || 'Lo siento, ocurri\u00f3 un error.',\n  qr_url: data.qr_url || null,\n  pdf_url: data.pdf_url || null\n} }];"
      }
    },
    {
      "id": "node-respond",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        2060,
        400
      ],
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ { text: $json.text, qr_url: $json.qr_url || null, pdf_url: $json.pdf_url || null } }}",
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "application/json"
              },
              {
                "name": "Access-Control-Allow-Origin",
                "value": "*"
              }
            ]
          }
        }
      }
    },
    {
      "id": "node-sticky",
      "name": "SETUP",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        240,
        60
      ],
      "parameters": {
        "content": "## AMICANA Chatbot \u2014 Gemini\n\n**Variables requeridas** (Settings \u2192 Variables):\n- `GEMINI_API_KEY` = clave de aistudio.google.com\n- `CHATBOT_INTERNAL_KEY` = amicana-internal\n\n**Opcional**:\n- `FASTAPI_BASE_URL` = URL base de FastAPI. Default: `http://host.docker.internal:8000` (Docker Desktop). Producci\u00f3n: `https://api.amicana.com`.\n\nEl flow persiste sesiones en BD v\u00eda `/chatbot/session` (no usa staticData).\nActivar workflow con el toggle arriba a la derecha.",
        "height": 220,
        "width": 360,
        "color": 4
      }
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Get Session History",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Session History": {
      "main": [
        [
          {
            "node": "Load Session API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Load Session API": {
      "main": [
        [
          {
            "node": "Hydrate Session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Hydrate Session": {
      "main": [
        [
          {
            "node": "Gemini Router",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini Router": {
      "main": [
        [
          {
            "node": "Parse Intent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Intent": {
      "main": [
        [
          {
            "node": "Intent Switch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Intent Switch": {
      "main": [
        [
          {
            "node": "Auth \u2014 Buscar Alumno",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Estado \u2014 Cuotas Alumno",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Pagar \u2014 Crear Pago MP",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Confirmar \u2014 Registrar Pago",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Cerrar \u2014 Format Response",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Fuera Scope \u2014 Format Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Auth \u2014 Buscar Alumno": {
      "main": [
        [
          {
            "node": "Auth \u2014 Format Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Estado \u2014 Cuotas Alumno": {
      "main": [
        [
          {
            "node": "Estado \u2014 Format Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pagar \u2014 Crear Pago MP": {
      "main": [
        [
          {
            "node": "Pagar \u2014 Generar PDF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pagar \u2014 Generar PDF": {
      "main": [
        [
          {
            "node": "Pagar \u2014 Format Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Confirmar \u2014 Registrar Pago": {
      "main": [
        [
          {
            "node": "Confirmar \u2014 Format Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Auth \u2014 Format Response": {
      "main": [
        [
          {
            "node": "Save Session API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Estado \u2014 Format Response": {
      "main": [
        [
          {
            "node": "Save Session API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pagar \u2014 Format Response": {
      "main": [
        [
          {
            "node": "Save Session API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Confirmar \u2014 Format Response": {
      "main": [
        [
          {
            "node": "Save Session API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Cerrar \u2014 Format Response": {
      "main": [
        [
          {
            "node": "Save Session API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fuera Scope \u2014 Format Response": {
      "main": [
        [
          {
            "node": "Save Session API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Session API": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "saveManualExecutions": true
  },
  "staticData": null,
  "tags": [
    {
      "name": "AMICANA"
    },
    {
      "name": "chatbot"
    },
    {
      "name": "gemini"
    }
  ]
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

AMICANA Chatbot — Cuotas (Gemini). Uses httpRequest. Webhook trigger; 21 nodes.

Source: https://github.com/MartinZ18/Proyecto-Amicana-2.0/blob/95ace156db88130fd81e262b2422dc03ee9c3dae/n8n/amicana-chatbot.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

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

AI & RAG

This workflow builds a fully private, self-hosted AI chatbot using Meta Llama models. Unlike cloud-based AI APIs, every conversation stays on your infrastructure — no data leaves your environment. The

HTTP Request, Google Sheets
AI & RAG

RAG Assistant - Query. Uses httpRequest. Webhook trigger; 4 nodes.

HTTP Request
AI & RAG

The Ultimate Scraper for n8n uses Selenium and AI to retrieve any information displayed on a webpage. You can also use session cookies to log in to the targeted webpage for more advanced scraping need

OpenAI Chat, HTTP Request, Information Extractor +1
AI & RAG

z-Api. Uses httpRequest, openAi, redis, postgres. Webhook trigger; 61 nodes.

HTTP Request, OpenAI, Redis +4
AI & RAG

How it works: • Receives WhatsApp messages via webhook from Whapi.Cloud • Routes commands: AI chat (/ai), numeric commands (1-9), or help menu • Sends responses: text, images, documents, videos, conta

HTTP Request, OpenAI