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": "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"
}
]
}
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 →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
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
RAG Assistant - Query. Uses httpRequest. Webhook trigger; 4 nodes.
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
z-Api. Uses httpRequest, openAi, redis, postgres. Webhook trigger; 61 nodes.
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