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": "Reagendamiento",
"nodes": [
{
"parameters": {
"workflowInputs": {
"values": [
{
"name": "CallerID"
},
{
"name": "Message"
},
{
"name": "Instance"
}
]
}
},
"type": "n8n-nodes-base.executeWorkflowTrigger",
"typeVersion": 1.1,
"position": [
-112,
656
],
"id": "beabcd89-f0fa-4f01-9a0c-c94796eeff23",
"name": "When Executed by Another Workflow"
},
{
"parameters": {
"operation": "get",
"propertyName": "state",
"key": "={{ $json.CallerID }}-state",
"options": {}
},
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
336,
288
],
"id": "7220a0bd-798b-4441-81dc-a3d9e804157e",
"name": "Redis",
"credentials": {
"redis": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"leftValue": "={{ $json.state }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "empty",
"singleValue": true
},
"id": "499e6b74-e44a-478f-9477-599ef97cc445"
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "Inicio"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "732a7223-5dba-4eec-a8e4-0a2e6f67de6b",
"leftValue": "={{ $json.state }}",
"rightValue": "Nombre",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "Nombre"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "5aaca3f3-7a1f-479e-8396-7f639a6805d0",
"leftValue": "={{ $json.state }}",
"rightValue": "Eleccion",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "Eleccion"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "c0def2a0-eb7f-42ef-83b3-1f00a34c233b",
"leftValue": "={{ $json.state }}",
"rightValue": "Reagendar",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "Reagendar"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "ed95d16b-41e1-4ada-afc3-8068a9c0bc6c",
"leftValue": "={{ $json.state }}",
"rightValue": "Fecha",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "Fecha"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "cac52561-204c-4dcf-b6ae-c69ca96bc9dd",
"leftValue": "={{ $json.state }}",
"rightValue": "Hora",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "Hora"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "69256363-aec0-43e3-bf64-4e8be27fc803",
"leftValue": "={{ $json.state }}",
"rightValue": "Confirmacion",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "Confirmacion"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.switch",
"typeVersion": 3.4,
"position": [
560,
224
],
"id": "4c5ebf71-9ec2-4d70-be07-a4c3e907d318",
"name": "Switch"
},
{
"parameters": {
"operation": "set",
"key": "={{ $('When Executed by Another Workflow').item.json.CallerID }}-state",
"value": "Nombre"
},
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
1008,
-288
],
"id": "9433c83f-0e81-46ea-a2fa-8a37ebeaad09",
"name": "Set_name",
"credentials": {
"redis": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "messages-api",
"instanceName": "={{ $('When Executed by Another Workflow').item.json.Instance }}",
"remoteJid": "={{ $('When Executed by Another Workflow').item.json.CallerID }}",
"messageText": "=\ud83d\udd04 Elegiste *reagendar una cita*.\n\nPor favor, escrib\u00ed el *nombre de la persona* que figura en la consulta para poder continuar.\n\nSi dese\u00e1s volver al men\u00fa principal, escrib\u00ed *CANCELAR*.\n",
"options_message": {}
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
784,
-288
],
"id": "e368622c-b506-4d40-ab90-b4445de6d05d",
"name": "Inicio",
"credentials": {
"evolutionApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "messages-api",
"instanceName": "={{ $('When Executed by Another Workflow').item.json.Instance }}",
"remoteJid": "={{ $('When Executed by Another Workflow').item.json.CallerID }}",
"messageText": "=\u274c No se encontr\u00f3 ninguna cita asociada a ese nombre.\n\nPor favor, verific\u00e1 el nombre ingresado e intent\u00e1 nuevamente.\nSi necesit\u00e1s ayuda, escrib\u00ed *CANCELAR* para volver al inicio.\n",
"options_message": {}
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
1232,
96
],
"id": "3185912a-79ec-474a-89cd-c88eb456996d",
"name": "No_Date_With_That_Name",
"credentials": {
"evolutionApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "messages-api",
"instanceName": "={{ $('When Executed by Another Workflow').item.json.Instance }}",
"remoteJid": "={{ $('When Executed by Another Workflow').item.json.CallerID }}",
"messageText": "=\u26a0\ufe0f Encontramos una cita asociada a ese nombre, pero el n\u00famero desde el que est\u00e1s escribiendo no coincide con el registrado en la reserva.\n\nPor favor, escrib\u00ed el *n\u00famero de tel\u00e9fono* desde el cual se realiz\u00f3 la reserva para poder continuar.\n",
"options_message": {}
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
1456,
80
],
"id": "5ad5f780-9f67-4d46-97ae-98b545322ff3",
"name": "Number_not_Associated",
"credentials": {
"evolutionApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "set",
"key": "={{ $('When Executed by Another Workflow').item.json.CallerID }}-state",
"value": "Numero"
},
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
1680,
80
],
"id": "f3e6380a-2056-4a55-a5c7-96d1612387c4",
"name": "Set_number",
"credentials": {
"redis": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "get",
"dataTableId": {
"__rl": true,
"value": "wA0dcDuSPu44zLSy",
"mode": "list",
"cachedResultName": "Appointment",
"cachedResultUrl": "/projects/2FKcChTlWLe9b3wY/datatables/wA0dcDuSPu44zLSy"
},
"matchType": "allConditions",
"filters": {
"conditions": [
{
"keyName": "paciente_nombre",
"condition": "ilike",
"keyValue": "={{ $('When Executed by Another Workflow').item.json.Message.toTitleCase() }}"
},
{
"keyName": "estado",
"condition": "neq",
"keyValue": "cancelado"
}
]
}
},
"type": "n8n-nodes-base.dataTable",
"typeVersion": 1.1,
"position": [
784,
-96
],
"id": "f0f3e3df-8468-47ae-826e-73b28c245eed",
"name": "GetTurnos",
"alwaysOutputData": true
},
{
"parameters": {
"resource": "messages-api",
"instanceName": "={{ $json.Instance }}",
"remoteJid": "={{ $json.CallerID }}",
"messageText": "={{ $json.mensaje }}",
"options_message": {}
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
1904,
-208
],
"id": "308c6b2f-201a-45c4-ac4f-e9c1f11d6514",
"name": "Unica_Cita",
"credentials": {
"evolutionApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "set",
"key": "={{ $('Message_Builder').item.json.CallerID }}-state",
"value": "Reagendar"
},
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
2128,
-208
],
"id": "aec9e32b-a351-4c26-bda6-64d4e08d3b49",
"name": "Set_cancelacion",
"credentials": {
"redis": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "6cf103df-c30f-4639-ab1c-a6fdfa1a40ad",
"leftValue": "={{ $json }}",
"rightValue": 0,
"operator": {
"type": "object",
"operation": "notEmpty",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
1008,
-96
],
"id": "005ba53d-edca-49a6-81f0-05d51582833a",
"name": "Is_name_associated"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose",
"version": 3
},
"conditions": [
{
"id": "eea10787-6084-4c2c-9d6d-dd22fd64d147",
"leftValue": "={{ $json.paciente_telefono }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "empty",
"singleValue": true
}
},
{
"id": "4edf745f-f222-41eb-b37e-a710f5e71b0f",
"leftValue": "={{ $('When Executed by Another Workflow').item.json.CallerID }}",
"rightValue": "={{ $json.paciente_telefono}}",
"operator": {
"type": "string",
"operation": "startsWith"
}
}
],
"combinator": "or"
},
"looseTypeValidation": true,
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
1232,
-96
],
"id": "2889cb43-8d2a-4011-80fe-9dc6ece9cb3d",
"name": "Is_number_associated"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "d4b53fe6-2696-4ead-a55d-89f9c36b0bfe",
"leftValue": "={{ $json.unica_cita }}",
"rightValue": false,
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
1680,
-112
],
"id": "04758230-eedd-4f02-9aff-8565764a0d97",
"name": "Is_date_unique"
},
{
"parameters": {
"operation": "set",
"key": "={{ $('Message_Builder').item.json.CallerID }}-dateID",
"value": "={{ $('Message_Builder').item.json.cita.id.toString() }}",
"keyType": "string"
},
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
2352,
-208
],
"id": "961bab46-5a1d-41ef-a528-18b02a953fa8",
"name": "Set_DateID",
"credentials": {
"redis": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"leftValue": "={{ $('When Executed by Another Workflow').item.json.Message.toUpperCase() }}",
"rightValue": "SI",
"operator": {
"type": "string",
"operation": "equals"
},
"id": "d1cb1abb-bae5-4ffb-8c41-e8f3eebe172f"
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "confirmado"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "db831cfd-f3e0-406c-9ece-dd7856bc1cd1",
"leftValue": "={{ $('When Executed by Another Workflow').item.json.Message.toUpperCase() }}",
"rightValue": "NO",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "cancelado"
}
]
},
"options": {
"fallbackOutput": "extra"
}
},
"type": "n8n-nodes-base.switch",
"typeVersion": 3.4,
"position": [
784,
576
],
"id": "5591d8b6-fc39-4829-8329-f2a4e8b625f0",
"name": "Switch1"
},
{
"parameters": {
"resource": "messages-api",
"instanceName": "={{ $('When Executed by Another Workflow').item.json.Instance }}",
"remoteJid": "={{ $('When Executed by Another Workflow').item.json.CallerID }}",
"messageText": "=\u274c Respuesta inv\u00e1lida. Por favor respond\u00e9 SI o NO.",
"options_message": {}
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
1008,
880
],
"id": "6463ca27-c570-4982-91b8-45f432d2c650",
"name": "Neither",
"credentials": {
"evolutionApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"workflowId": {
"__rl": true,
"value": "9npekj3aPag37ifbW5rTQ",
"mode": "list",
"cachedResultUrl": "/workflow/9npekj3aPag37ifbW5rTQ",
"cachedResultName": "Deletion_of_Redis"
},
"workflowInputs": {
"mappingMode": "defineBelow",
"value": {
"CallerID": "={{ $('When Executed by Another Workflow').item.json.CallerID }}"
},
"matchingColumns": [
"CallerID"
],
"schema": [
{
"id": "CallerID",
"displayName": "CallerID",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string",
"removed": false
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": true
},
"options": {}
},
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.3,
"position": [
1232,
768
],
"id": "5b199ed1-2193-4b8b-b365-68272c98f24d",
"name": "Call 'Deletion_of_Redis'"
},
{
"parameters": {
"content": "{CallerID}-state\n{CallerID}-DoctorId\n{CallerID}-fecha\n{CallerID}-fechaHoraInicio\n{CallerID}-fechaHoraFin\n{CallerID}-doctores_disponibles\n{CallerID}-HorariosDisponibles\n{{CallerID }}-menu\n{{ CallerID }}-name",
"height": 272
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
0,
-512
],
"id": "d75a0cce-9925-41cb-9aea-b4297d10c647",
"name": "Sticky Note"
},
{
"parameters": {
"resource": "messages-api",
"instanceName": "={{ $('When Executed by Another Workflow').item.json.Instance }}",
"remoteJid": "={{ $('When Executed by Another Workflow').item.json.CallerID }}",
"messageText": "=\u2139\ufe0f El reagendamiento fue cancelado.",
"options_message": {}
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
1008,
688
],
"id": "6a0c5f9e-0ca8-4abf-8d5e-15d1bbc33334",
"name": "Cancelar",
"credentials": {
"evolutionApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Citas existentes\nconst appointments = $('GetTurnos').all().map(i => i.json);\n\n// Fecha actual\nconst ahora = new Date();\n\n// Filtrar citas futuras y reservadas\nconst citasFuturas = appointments.filter(cita => {\n return (\n cita.estado === 'reservado' &&\n new Date(cita.fecha_hora_inicio) > ahora\n );\n});\n\n// Si no tiene citas futuras\nif (citasFuturas.length === 0) {\n return [{\n json: {\n tiene_citas: false,\n mensaje: '\u274c No ten\u00e9s citas futuras registradas para reagendar.'\n }\n }];\n}\n\n// Si tiene una sola cita\nif (citasFuturas.length === 1) {\n const cita = citasFuturas[0];\n const inicio = new Date(cita.fecha_hora_inicio);\n\n const mensaje =\n`\ud83d\udcc5 Ten\u00e9s una cita programada:\n\n\ud83d\udcc6 Fecha: ${inicio.toLocaleDateString('es-ES')}\n\u23f0 Hora: ${inicio.toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' })}\n\n\u00bfDese\u00e1s *reagendar* esta cita?\nRespond\u00e9 *SI* para continuar o *NO* para mantenerla.`;\n\n return [{\n json: {\n unica_cita: true,\n cita,\n mensaje,\n CallerID: $('When Executed by Another Workflow').first().json.CallerID,\n Instance: $('When Executed by Another Workflow').first().json.Instance\n }\n }];\n}\n\n// Si tiene m\u00e1s de una cita\nlet menu = `\ud83d\udcc5 Ten\u00e9s las siguientes citas programadas:\\n\\n`;\n\ncitasFuturas.forEach((cita, index) => {\n const inicio = new Date(cita.fecha_hora_inicio);\n menu += `${index + 1}\ufe0f\u20e3 ${inicio.toLocaleDateString('es-ES')} - ${inicio.toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' })}\\n`;\n});\n\nmenu += `\\nPor favor, respond\u00e9 con el *n\u00famero* de la cita que dese\u00e1s *reagendar*.`;\n\nreturn [{\n json: {\n unica_cita: false,\n citas: citasFuturas,\n mensaje: menu,\n CallerID: $('When Executed by Another Workflow').first().json.CallerID,\n Instance: $('When Executed by Another Workflow').first().json.Instance\n }\n}];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1456,
-112
],
"id": "72fbc5dc-398e-401a-997e-0ad3ab8f87cb",
"name": "Message_Builder"
},
{
"parameters": {
"resource": "messages-api",
"instanceName": "={{ $json.Instance }}",
"remoteJid": "={{ $json.CallerID }}",
"messageText": "={{ $json.mensaje }}",
"options_message": {}
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
1904,
-16
],
"id": "1075e057-916e-4a53-9dbf-635dba7d1a5d",
"name": "Multiples_Citas",
"credentials": {
"evolutionApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "set",
"key": "={{ $('Message_Builder').item.json.CallerID }}-HorariosDisponibles",
"value": "={{ $('Message_Builder').item.json.citas.toJsonString() }}"
},
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
2352,
-16
],
"id": "2e21f3e7-66ee-4dfe-866d-ae1bac4a52b4",
"name": "Set_HorariosDisponibles",
"credentials": {
"redis": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "set",
"key": "={{ $('Message_Builder').item.json.CallerID }}-state",
"value": "Eleccion"
},
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
2128,
-16
],
"id": "38f9cefd-4b58-4d8e-8e69-816da9f633b0",
"name": "Set_eleccion",
"credentials": {
"redis": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "get",
"propertyName": "citas",
"key": "={{ $('When Executed by Another Workflow').item.json.CallerID }}-HorariosDisponibles",
"options": {}
},
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
784,
288
],
"id": "9db6fa18-ea2d-4e56-8f94-4a2d11a288cd",
"name": "Get_HorariosDisponibles",
"credentials": {
"redis": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// JSON en formato string (array de citas)\nconst jsonString = $input.first().json.citas;\n\n// Opci\u00f3n ingresada por el usuario\nconst opcionRaw = $('When Executed by Another Workflow').first().json.Message;\nconst opcion = parseInt(opcionRaw, 10);\n\n// Parsear JSON string a JSON real\nconst citas = JSON.parse(jsonString);\n\n// Validar que sea n\u00famero\nif (isNaN(opcion)) {\n return [{\n json: {\n valido: false,\n mensaje: '\u274c Opci\u00f3n inv\u00e1lida. Por favor, respond\u00e9 con un n\u00famero.'\n }\n }];\n}\n\n// Validar rango\nif (opcion < 1 || opcion > citas.length) {\n return [{\n json: {\n valido: false,\n mensaje: '\u274c El n\u00famero ingresado no corresponde a ninguna cita.'\n }\n }];\n}\n\n// Obtener la cita seleccionada (1 \u2192 \u00edndice 0)\nconst citaSeleccionada = citas[opcion - 1];\n\n// Fecha de inicio de la cita\nconst inicio = new Date(citaSeleccionada.fecha_hora_inicio);\n\n// Mensaje de reagendamiento\nconst mensaje =\n`\ud83d\udcc6 Fecha actual: ${inicio.toLocaleDateString('es-ES')}\n\u23f0 Hora actual: ${inicio.toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' })}\n\n\u00bfPara qu\u00e9 d\u00eda quer\u00e9s agendar la consulta?\n\n\ud83d\udcc5 Escrib\u00ed la fecha en formato DD/MM/AAAA.`;\n\n// OK\nreturn [{\n json: {\n valido: true,\n eleccion_usuario: opcion,\n cita_seleccionada: citaSeleccionada,\n mensaje\n }\n}];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1008,
288
],
"id": "f606e637-97b7-4a34-83b5-bd31e0d23c68",
"name": "Code in JavaScript"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "4c55dea2-9a0c-4719-97cf-03f33cc4712c",
"leftValue": "={{ $json.Message.toUpperCase() }}",
"rightValue": "CANCELAR",
"operator": {
"type": "string",
"operation": "notEquals"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
112,
656
],
"id": "9b9acaf4-87e7-4999-bfc5-901bf93c1ea4",
"name": "Canceled?"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "ddc0e4cb-e3b1-4c3c-99d8-3a773a2db127",
"leftValue": "={{ $json.valido }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
1232,
288
],
"id": "35c2559d-0729-42ed-a2e8-5f5d326cd191",
"name": "If"
},
{
"parameters": {
"resource": "messages-api",
"instanceName": "={{ $('When Executed by Another Workflow').item.json.Instance }}",
"remoteJid": "={{ $('When Executed by Another Workflow').item.json.CallerID }}",
"messageText": "={{ $json.mensaje }}",
"options_message": {}
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
1456,
288
],
"id": "2384aec8-7e9e-448a-ab2c-430669b921db",
"name": "Eleccion_valida",
"credentials": {
"evolutionApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "set",
"key": "={{ $('When Executed by Another Workflow').item.json.CallerID }}-dateID",
"value": "={{ $('Code in JavaScript').item.json.cita_seleccionada.id.toString() }}",
"keyType": "string"
},
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
1904,
288
],
"id": "83071630-7976-4fbe-832d-e5bb4abb701e",
"name": "Set_DateID1",
"credentials": {
"redis": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "messages-api",
"instanceName": "={{ $('When Executed by Another Workflow').item.json.Instance }}",
"remoteJid": "={{ $('When Executed by Another Workflow').item.json.CallerID }}",
"messageText": "=\ud83d\uddd1\ufe0f Tu turno fue cancelado correctamente.",
"options_message": {}
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
1232,
2096
],
"id": "45ba7be6-0327-4f01-b1a6-9fab9c452345",
"name": "cancelacion",
"credentials": {
"evolutionApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "398f759c-6c2e-4e17-8723-237c215b9358",
"leftValue": "={{ $('When Executed by Another Workflow').item.json.Message.toUpperCase() }}",
"rightValue": "NO",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
1008,
2096
],
"id": "285d0a46-4674-474c-b7ca-ee43d3c12374",
"name": "If2"
},
{
"parameters": {
"resource": "messages-api",
"instanceName": "={{ $('When Executed by Another Workflow').item.json.Instance }}",
"remoteJid": "={{ $('When Executed by Another Workflow').item.json.CallerID }}",
"messageText": "=\u2705 \u00a1Turno confirmado!\n\nTu consulta qued\u00f3 *reagendada* para:\n\n\ud83d\udcc5 {{ $('Get_date_inicio').item.json.dateInicio.toDateTime().toFormat('dd/MM/yyyy') }}\n\ud83d\udd52 {{ $('Get_date_inicio').item.json.dateInicio.toDateTime().toLocaleString({timeStyle: 'short'}) }}",
"options_message": {}
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
2544,
1840
],
"id": "d0ceefd7-c467-46f4-87ba-71fd6af0c504",
"name": "Confirmacion",
"credentials": {
"evolutionApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "get",
"propertyName": "dateFin",
"key": "={{ $('When Executed by Another Workflow').item.json.CallerID }}-fechaHoraFin",
"options": {}
},
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
1456,
1840
],
"id": "baf636ae-85e1-4aa4-9d7a-933fc6f8d578",
"name": "Get_date_fin",
"credentials": {
"redis": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "get",
"propertyName": "dateInicio",
"key": "={{ $('When Executed by Another Workflow').item.json.CallerID }}-fechaHoraInicio",
"options": {}
},
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
1232,
1840
],
"id": "4830d965-7ec0-40a6-b9c5-fa4e4d626779",
"name": "Get_date_inicio",
"credentials": {
"redis": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "e2f2a6a2-e6a8-40b0-a429-d1ce21795dfa",
"leftValue": "={{ $('When Executed by Another Workflow').item.json.Message.toUpperCase() }}",
"rightValue": "SI",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
784,
1936
],
"id": "772427cf-3035-4422-99c3-d51dc884009f",
"name": "If1"
},
{
"parameters": {
"operation": "get",
"propertyName": "HorariosDisponibles",
"key": "={{ $('When Executed by Another Workflow').item.json.CallerID }}-HorariosDisponibles",
"keyType": "list",
"options": {}
},
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
1008,
1440
],
"id": "c5a456df-41ac-4d83-b5f6-5908af808c94",
"name": "get_HorariosDisponibles",
"credentials": {
"redis": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Input del usuario (ej: \"1\", \"2\", etc.)\nconst choiceRaw = $('When Executed by Another Workflow').first().json.Message;\nconst index = parseInt(choiceRaw, 10);\n\n// Validar input num\u00e9rico\nif (isNaN(index) || index < 1) {\n return [{\n json: {\n valido: false,\n mensaje: '\u274c Opci\u00f3n inv\u00e1lida. Respond\u00e9 con el n\u00famero del horario.'\n }\n }];\n}\n\n// Horarios disponibles (array de \"HH:mm\")\nconst horariosDisponibles = $('get_HorariosDisponibles').first().json.HorariosDisponibles;\n\n// Validar que est\u00e9 dentro del array\nif (index > horariosDisponibles.length) {\n return [{\n json: {\n valido: false,\n mensaje: '\u274c El horario seleccionado no existe.'\n }\n }];\n}\n\n// Hora elegida (HH:mm)\nconst horaElegida = horariosDisponibles[index - 1];\n\n// Fecha base ISO\nconst fechaBaseISO = $('Get_date').first().json.fecha_selected;\nconst inicioElegido = new Date(fechaBaseISO);\n\n// Setear hora elegida\nconst [hh, mm] = horaElegida.split(':').map(Number);\ninicioElegido.setUTCHours(hh, mm, 0, 0);\n\n// Duraci\u00f3n\nconst duracion = $('GetDoctor1').first().json.duracion_consulta;\nconst finElegido = new Date(inicioElegido);\nfinElegido.setMinutes(finElegido.getMinutes() + duracion);\n\n// Mensaje de confirmaci\u00f3n\nconst mensaje =\n`\ud83d\udcc5 *Confirmaci\u00f3n de turno*\n\n\ud83d\udc68\u200d\u2695\ufe0f Doctor: ${$('GetDoctor1').first().json.nombre}\n\ud83d\udc64 Paciente: ${$('Get_appointment1').first().json.paciente_nombre}\n\ud83d\udcc6 Fecha: ${inicioElegido.toLocaleDateString('es-ES')}\n\u23f0 Hora: ${horaElegida}\n\u231b Duraci\u00f3n: ${duracion} minutos\n\n\u00bfLos datos son correctos?\nRespond\u00e9 *SI* para confirmar o *NO* para cancelar.`;\n\n// OK\nreturn [{\n json: {\n valido: true,\n fecha_hora_inicio: inicioElegido.toISOString(),\n fecha_hora_fin: finElegido.toISOString(),\n mensaje\n }\n}];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2128,
1456
],
"id": "c725e9f4-8ada-47c7-a208-2d32f7f85dca",
"name": "Code in JavaScript3"
},
{
"parameters": {
"resource": "messages-api",
"instanceName": "={{ $('When Executed by Another Workflow').item.json.Instance }}",
"remoteJid": "={{ $('When Executed by Another Workflow').item.json.CallerID }}",
"messageText": "={{ $json.mensaje }}",
"options_message": {}
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
2352,
1456
],
"id": "d1a23df0-07c0-4c3b-bb0e-9128acb21925",
"name": "Hora1",
"credentials": {
"evolutionApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "get",
"dataTableId": {
"__rl": true,
"value": "hMRPl1eK7rwrIssD",
"mode": "list",
"cachedResultName": "Doctor",
"cachedResultUrl": "/projects/2FKcChTlWLe9b3wY/datatables/hMRPl1eK7rwrIssD"
},
"matchType": "allConditions",
"filters": {
"conditions": [
{
"keyValue": "={{ $json.doctor_id }}"
}
]
}
},
"type": "n8n-nodes-base.dataTable",
"typeVersion": 1.1,
"position": [
1904,
1456
],
"id": "0395f93d-7ac8-4b96-b64e-3004490d1416",
"name": "GetDoctor1",
"alwaysOutputData": true,
"executeOnce": true
},
{
"parameters": {
"operation": "set",
"key": "={{ $('When Executed by Another Workflow').item.json.CallerID }}-fechaHoraInicio",
"value": "={{ $('Code in JavaScript3').item.json.fecha_hora_inicio }}"
},
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
2576,
1456
],
"id": "a0fd6361-47a3-4006-87fd-6f67fe7f0147",
"name": "Set_date_inicio",
"credentials": {
"redis": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "set",
"key": "={{ $('When Executed by Another Workflow').item.json.CallerID }}-fechaHoraFin",
"value": "={{ $('Code in JavaScript3').item.json.fecha_hora_fin }}"
},
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
2800,
1456
],
"id": "4b310c9c-bc3a-451c-8db8-581551a1f0a4",
"name": "Set_date_fin",
"credentials": {
"redis": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "set",
"key": "={{ $('When Executed by Another Workflow').item.json.CallerID }}-state",
"value": "Confirmacion",
"keyType": "string"
},
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
3024,
1456
],
"id": "e1f740f6-ff79-4a30-9fca-d0042928468c",
"name": "Set_confirmacion",
"credentials": {
"redis": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "get",
"propertyName": "fecha_selected",
"key": "={{ $('When Executed by Another Workflow').item.json.CallerID }}-fecha",
"options": {}
},
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
1232,
1456
],
"id": "3aeab449-9c23-4bee-b1bb-e687d541dccd",
"name": "Get_date",
"credentials": {
"redis": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "messages-api",
"instanceName": "={{ $('When Executed by Another Workflow').item.json.Instance }}",
"remoteJid": "={{ $('When Executed by Another Workflow').item.json.CallerID }}",
"messageText": "=No pude reconocer la fecha que enviaste.\nPor favor, escribila en el formato dd/MM/yyyy (por ejemplo: 15/03/2026).",
"options_message": {}
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
1232,
1264
],
"id": "296f1c44-3d74-41d2-84cd-80ae957f5a35",
"name": "FechaInvalida",
"credentials": {
"evolutionApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const texto = $('When Executed by Another Workflow').first().json.Message;\n\n// 1. Validar formato dd/MM/yyyy con regex estricta\nconst regex = /^(0[1-9]|[12][0-9]|3[01])\\/(0[1-9]|1[0-2])\\/\\d{4}$/;\n\nif (!regex.test(texto)) {\n return [{ json: { valido: false, motivo: 'Formato inv\u00e1lido (dd/MM/yyyy)' } }];\n}\n\n// 2. Validar que la fecha exista realmente\nconst [dia, mes, anio] = texto.split('/').map(Number);\nconst fecha = new Date(anio, mes - 1, dia);\n\nconst fechaValida =\n fecha.getFullYear() === anio &&\n fecha.getMonth() === mes - 1 &&\n fecha.getDate() === dia;\n\nif (!fechaValida) {\n return [{ json: { valido: false, motivo: 'La fecha no existe' } }];\n}\n\n// 3. Fecha v\u00e1lida\nreturn [{\n json: {\n valido: true,\n fecha_original: texto,\n fecha_date: fecha.toISOString()\n }\n}];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
784,
1072
],
"id": "f34128da-183f-43ee-b807-5f352d7a6b79",
"name": "Code in JavaScript1"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose",
"version": 3
},
"conditions": [
{
"id": "60e72980-9c72-416d-ab7f-402b7d4a2a2a",
"leftValue": "={{ $json.valido }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "or"
},
"looseTypeValidation": true,
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
1008,
1072
],
"id": "6f3ee9e9-2c9b-4bb9-a5d1-3321f0ebf04e",
"name": "Is_Date"
},
{
"parameters": {
"resource": "messages-api",
"instanceName": "={{ $('When Executed by Another Workflow').item.json.Instance }}",
"remoteJid": "={{ $('When Executed by Another Workflow').item.json.CallerID }}",
"messageText": "=Lo siento, {{ $json.nombre.includes('Dr.')? 'el ' : \"la \"}}{{ $json.nombre}} no atiende el d\u00eda seleccionado. ",
"options_message": {}
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
2800,
1136
],
"id": "ab885be2-154c-4771-9d6a-fa169ab8afe6",
"name": "NoAtiende",
"credentials": {
"evolutionApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "messages-api",
"instanceName": "={{ $('When Executed by Another Workflow').item.json.Instance }}",
"remoteJid": "={{ $('When Executed by Another Workflow').item.json.CallerID }}",
"messageText": "=La fecha que indicaste corresponde a un d\u00eda que ya pas\u00f3.\nPor favor, env\u00edame una fecha posterior a hoy en el formato dd/MM/yyyy (por ejemplo: 25/03/2026).",
"options_message": {}
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
1456,
1232
],
"id": "93073a76-7b0e-4058-8e94-832ce99ce738",
"name": "FechaPasada",
"credentials": {
"evolutionApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "1e924288-3c7c-4f79-9bca-2883bde79c02",
"leftValue": "={{ $json.fecha_date }}",
"rightValue": "={{ $now }}",
"operator": {
"type": "dateTime",
"operation": "after"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
1232,
1072
],
"id": "cdbf7267-f270-48b7-9082-59988f4a45d0",
"name": "Is_Valid_Date"
},
{
"parameters": {
"operation": "get",
"dataTableId": {
"__rl": true,
"value": "Vh1ixrYCpUuuKazI",
"mode": "list",
"cachedResultName": "Horario",
"cachedResultUrl": "/projects/2FKcChTlWLe9b3wY/datatables/Vh1ixrYCpUuuKazI"
},
"matchType": "allConditions",
"filters": {
"conditions": [
{
"keyName": "doctor_id",
"keyValue": "={{ $json.doctor_id }}"
},
{
"keyName": "dia_semana",
"keyValue": "={{ $('Code in JavaScript1').item.json.fecha_date.toDateTime().extract(\"weekday\") }}"
}
]
}
},
"type": "n8n-nodes-base.dataTable",
"typeVersion": 1.1,
"position": [
1904,
1040
],
"id": "f3c17093-0cfd-4182-b207-54fe9f0c0601",
"name": "GetHorario",
"alwaysOutputData": true
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "6fbcb57f-c881-468d-a6b5-e1720df81894",
"leftValue": "={{ $('GetHorario').item.json.dia_semana }}",
"rightValue": "",
"operator": {
"type": "number",
"operation": "notEmpty",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
2576,
1040
],
"id": "7eb1ba24-1eda-4abd-a168-37530c0abcc5",
"name": "Is_attending that date"
},
{
"parameters": {
"resource": "messages-api",
"instanceName": "={{ $('When Executed by Another Workflow').item.json.Instance }}",
"remoteJid": "={{ $('When Executed by Another Workflow').item.json.CallerID }}",
"messageText": "={{ $json.mensaje }}",
"options_message": {}
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
3024,
944
],
"id": "f00ea52d-c903-42e8-b9a0-c095ff191226",
"name": "Hora",
"credentials": {
"evolutionApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "get",
"dataTableId": {
"__rl": true,
"value": "hMRPl1eK7rwrIssD",
"mode": "list",
"cachedResultName": "Doctor",
"cachedResultUrl": "/projects/2FKcChTlWLe9b3wY/datatables/hMRPl1eK7rwrIssD"
},
"matchType": "allConditions",
"filters": {
"conditions": [
{
"keyValue": "={{ $('Get_appointment').item.json.doctor_id }}"
}
]
}
},
"type": "n8n-nodes-base.dataTable",
"typeVersion": 1.1,
"position": [
2352,
1040
],
"id": "b0447a35-2ed0-4395-89bf-889fb04cd7be",
"name": "GetDoctor",
"alwaysOutputData": true,
"executeOnce": true
},
{
"parameters": {
"operation": "set",
"key": "={{ $('When Executed by Another Workflow').item.json.CallerID }}-fecha",
"value": "={{ $('Code in JavaScript1').item.json.fecha_date }}"
},
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
3696,
944
],
"id": "fb605b0d-65bb-4600-95e8-2a9c6cf611d6",
"name": "Set_date",
"credentials": {
"redis": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "set",
"key": "={{ $('When Executed by Another Workflow').item.json.CallerID }}-state",
"value": "Hora",
"keyType": "string"
},
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
3920,
944
],
"id": "81dc7eeb-0484-439c-aeb2-7144896b98f8",
"name": "Set_hora",
"credentials": {
"redis": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "push",
"list": "={{ $('When Executed by Another Workflow').item.json.CallerID }}-HorariosDisponibles",
"messageData": "={{ $('Code in JavaScript4').item.json.horariosDisponibles }}",
"tail": true
},
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
3472,
944
],
"id": "5915c7f3-307a-4492-93fe-94c0a1c995f3",
"name": "Save_Horarios",
"credentials": {
"redis": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"workflowId": {
"__rl": true,
"value": "9npekj3aPag37ifbW5rTQ",
"mode": "list",
"cachedResultUrl": "/workflow/9npekj3aPag37ifbW5rTQ",
"cachedResultName": "Deletion_of_Redis"
},
"workflowInputs": {
"mappingMode": "defineBelow",
"value": {
"CallerID": "={{ $('When Executed by Another Workflow').item.json.CallerID }}"
},
"matchingColumns": [
"CallerID"
],
"schema": [
{
"id": "CallerID",
"displayName": "CallerID",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string",
"removed": false
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": true
},
"options": {}
},
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.3,
"position": [
2768,
1936
],
"id": "daa2ed7f-9248-46aa-88a3-894f13fce9e4",
"name": "Call 'DeleteKeys'"
},
{
"parameters": {
"resource": "messages-api",
"instanceName": "={{ $('When Executed by Another Workflow').item.json.Instance }}",
"remoteJid": "={{ $('When Executed by Another Workflow').item.json.CallerID }}",
"messageText": "=Lo siento, no entend\u00ed tu respuesta.\n\nPor favor, respond\u00e9 con:\nSI \ud83d\udc49 para confirmar el turno \nNO \ud83d\udc49 para cancelarlo",
"options_message": {}
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
1232,
2288
],
"id": "66e8f20e-9669-4f24-bbbd-8f4e7332674e",
"name": "cancelacion1",
"credentials": {
"evolutionApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const appointments = $('GetTurnos1').all().map(i => i.json);\nconst horarios = $('GetHorario').all().map(i => i.json);\n\nconst doctorNombre = $('GetDoctor').first().json.nombre;\nconst duracion_consulta = $('GetDoctor').first().json.duracion_consulta; // minutos\nconst fechaISO = $('Code in JavaScript1').first().json.fecha_date;\n\nconst fecha = new Date(fechaISO);\nconst diaSemana = fecha.getUTCDay(); // 0=domingo ... 6=s\u00e1bado\nlet prefix;\nconst horariosDelDia = horarios.filter(h => h.dia_semana === diaSemana);\nif(doctorNombre.startsWith(\"Dr.\")){\n prefix = 'el';\n} else {\n prefix = 'la';\n}\nconst citasReservadas = appointments\n .filter(a => a.estado === 'reservado')\n .map(a => ({\n inicio: new Date(a.fecha_hora_inicio),\n fin: new Date(a.fecha_hora_fin),\n }));\n\nconst disponibles = [];\n\nfor (const h of horariosDelDia) {\n let hora = h.hora_inicio;\n\n while (hora < h.hora_fin) {\n const horas = Math.floor(hora / 100);\n const minutos = hora % 100;\n\n const inicioSlot = new Date(fecha);\n inicioSlot.setUTCHours(horas, minutos, 0, 0);\n\n const finSlot = new Date(inicioSlot);\n finSlot.setMinutes(finSlot.getMinutes() + duracion_consulta);\n\n const ocupado = citasReservadas.some(c =>\n inicioSlot < c.fin && finSlot > c.inicio\n );\n\n if (!ocupado && finSlot.getUTCHours() * 100 + finSlot.getUTCMinutes() <= h.hora_fin) {\n disponibles.push(inicioSlot.toISOString().substring(11, 16));\n }\n\n // \u23ed avanzar seg\u00fan duraci\u00f3n real\n const totalMin = minutos + duracion_consulta;\n hora = horas * 100 + totalMin;\n\n // corregir overflow de minutos (> 60)\n if (hora % 100 >= 60) {\n hora += Math.floor((hora % 100) / 60) * 40;\n }\n }\n}\n\nconst emojis = ['1\ufe0f\u20e3','2\ufe0f\u20e3','3\ufe0f\u20e3','4\ufe0f\u20e3','5\ufe0f\u20e3','6\ufe0f\u20e3','7\ufe0f\u20e3','8\ufe0f\u20e3','9\ufe0f\u20e3','\ud83d\udd1f'];\nconst fechaTexto = fecha.toLocaleDateString('es-ES');\nlet mensaje;\n\nif (disponibles.length === 0) {\n mensaje = `\u274c No hay horarios disponibles para ${prefix} ${doctorNombre} el ${fechaTexto}.\\n\\n` +\n `Por favor, eleg\u00ed otra fecha o contact\u00e1 con recepci\u00f3n.`;\n} else {\n const emojis = ['1\ufe0f\u20e3','2\ufe0f\u20e3','3\ufe0f\u20e3','4\ufe0f\u20e3','5\ufe0f\u20e3','6\ufe0f\u20e3','7\ufe0f\u20e3','8\ufe0f\u20e3','9\ufe0f\u20e3','\ud83d\udd1f'];\n\n mensaje = `Estos son los horarios disponibles para ${prefix} ${doctorNombre} el ${fechaTexto}:\\n\\n`;\n\n disponibles.forEach((h, i) => {\n mensaje += `${emojis[i] || '\u23f1\ufe0f'} ${h}\\n`;\n });\n\n mensaje += `\\nRespond\u00e9 escribiendo el numero de la hora exacta que prefieras. \\n(Ejemplo: 2)`;\n}\n\nreturn [\n {\n json: {\n mensaje,\n horariosDisponibles: disponibles,\n hayDisponibilidad: disponibles.length > 0,\n duracion_consulta\n }\n }\n];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2800,
944
],
"id": "5b85397a-6a75-4c6e-8ac9-52a33fe6ff45",
"name": "Code in JavaScript4"
},
{
"parameters": {
"operation": "get",
"dataTableId": {
"__rl": true,
"value": "wA0dcDuSPu44zLSy",
"mode": "list",
"cachedResultName": "Appointment",
"cachedResultUrl": "/projects/2FKcChTlWLe9b3wY/datatables/wA0dcDuSPu44zLSy"
},
"matchType": "allConditions",
"filters": {
"conditions": [
{
"keyName": "doctor_id",
"keyValue": "={{ $('Get_appointment').item.json.doctor_id }}"
},
{
"keyName": "fecha_hora_inicio",
"condition": "gte",
"keyValue": "={{ $('Is_Valid_Date').item.json.fecha_date.toDateTime() }}"
},
{
"keyName": "fecha_hora_fin",
"condition": "lt",
"keyValue": "={{ $('Is_Valid_Date').item.json.fecha_date.toDateTime().plus(1,\"days\") }}"
}
]
}
},
"type": "n8n-nodes-base.dataTable",
"typeVersion": 1.1,
"position": [
2128,
1040
],
"id": "c689ec2f-104f-45de-860b-440f82d7c7ce",
"name": "GetTurnos1",
"alwaysOutputData": true
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "ebe600fb-d3ac-4886-9b64-109ecbede96a",
"leftValue": "={{ $('Code in JavaScript4').item.json.hayDisponibilidad }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
3248,
944
],
"id": "214bd5ae-0e83-43b4-a2de-b56f9147d632",
"name": "If4"
},
{
"parameters": {
"operation": "set",
"key": "={{ $('When Executed by Another Workflow').item.json.CallerID }}-state",
"value": "Fecha",
"keyType": "string"
},
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
1680,
496
],
"id": "67a0cba9-3deb-4006-aaab-95b84356361e",
"name": "Set_fecha",
"credentials": {
"redis": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "messages-api",
"instanceName": "={{ $('When Executed by Another Workflow').item.json.Instance }}",
"remoteJid": "={{ $('When Executed by Another Workflow').item.json.CallerID }}",
"messageText": "=\u00bfPara qu\u00e9 d\u00eda quer\u00e9s agendar la consulta?\n\n\ud83d\udcc5 Escrib\u00ed la fecha en formato DD/MM/AAAA.",
"options_message": {}
},
"type": "n8n-nodes-evolution-api.evolutionApi",
"typeVersion": 1,
"position": [
1456,
496
],
"id": "73fdb586-c866-446a-a8fb-8b33681d6180",
"name": "Fecha",
"credentials": {
"evolutionApi": {
"name": "<your credential>"
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.
evolutionApigoogleCalendarOAuth2Apiredis
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Reagendamiento. Uses executeWorkflowTrigger, redis, n8n-nodes-evolution-api, dataTable. Event-driven trigger; 73 nodes.
Source: https://github.com/ignacioelizeche/BackOffice-Odonto/blob/13b047f47b8a3442a521b3ff148aff5b26d8f464/N8N/Reagendamiento.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.
Agendamiento. Uses n8n-nodes-evolution-api, redis, dataTable, executeWorkflowTrigger. Event-driven trigger; 60 nodes.
Prevent concurrent workflow runs using Redis. Uses executeWorkflowTrigger, manualTrigger, stickyNote, executeWorkflow. Event-driven trigger; 43 nodes.
This workflow sets a small "lock" value in Redis so that only one copy of a long job can run at the same time. If another trigger fires while the job is still busy, the workflow sees the lock, stops e
This implementation aggregates incoming data into a Redis list from potentially concurrent workflow executions. It buffers the data for a set period before a single execution retrieves and processes t
This workflow provides a reusable error handling, audit logging, and observability pattern for n8n workflows using two n8n custom Data Tables: and .