AutomationFlowsSlack & Telegram › Vitalis - Respuestas Whatsapp Twilio

Vitalis - Respuestas Whatsapp Twilio

Vitalis - Respuestas WhatsApp Twilio. Uses httpRequest. Webhook trigger; 11 nodes.

Webhook trigger★★★★☆ complexity11 nodesHTTP Request
Slack & Telegram Trigger: Webhook Nodes: 11 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": "Vitalis - Respuestas WhatsApp Twilio",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "twilio-respuestas",
        "responseMode": "onReceived",
        "responseData": ""
      },
      "id": "node-wh-02",
      "name": "Webhook Twilio Respuestas",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Twilio envia form-urlencoded. Extraemos Body y From.\nconst body = $input.first().json.body;\n\n// From llega como 'whatsapp:+5493624777801' -> extraemos solo el numero\nconst fromRaw = body.From || '';\nconst celular = fromRaw.replace('whatsapp:+', '').replace('whatsapp:', '').replace('+', '');\n\n// Mensaje en minusculas para el switch\nconst mensaje = (body.Body || '').toLowerCase().trim();\n\n// Fecha de hoy y manana para buscar el turno activo\nconst hoy = new Date();\nconst manana = new Date(hoy);\nmanana.setDate(manana.getDate() + 1);\n\nconst fmt = d => d.toISOString().split('T')[0];\n\nreturn [{\n  json: {\n    celular,\n    mensaje,\n    from_raw: fromRaw,\n    fecha_desde: fmt(hoy),\n    fecha_hasta: fmt(manana)\n  }\n}];"
      },
      "id": "node-code-02",
      "name": "Extraer datos",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        460,
        300
      ]
    },
    {
      "parameters": {
        "url": "=REEMPLAZAR_CON_SUPABASE_URL/rest/v1/turnos?select=id,estado,fecha,hora_inicio,paciente:pacientes!inner(id,celular)&paciente.celular=eq.{{ $json.celular }}&fecha=gte.{{ $json.fecha_desde }}&fecha=lte.{{ $json.fecha_hasta }}&estado=in.(reservado,confirmado,en_sala)&limit=1",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "apikey",
              "value": "REEMPLAZAR_CON_SUPABASE_SERVICE_ROLE_KEY"
            },
            {
              "name": "Authorization",
              "value": "<redacted-credential>"
            }
          ]
        },
        "options": {}
      },
      "id": "node-supa-find",
      "name": "Buscar turno en Supabase",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        680,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Combina el turno encontrado con los datos del paso anterior\nconst turno = $input.first().json[0] || null;\nconst prev  = $('Extraer datos').first().json;\n\nif (!turno) {\n  return [{ json: { turno_found: false, mensaje: prev.mensaje, from_raw: prev.from_raw } }];\n}\n\nreturn [{\n  json: {\n    turno_found: true,\n    turno_id:   turno.id,\n    estado_actual: turno.estado,\n    mensaje:    prev.mensaje,\n    from_raw:   prev.from_raw,\n    celular:    prev.celular\n  }\n}];"
      },
      "id": "node-code-03",
      "name": "Combinar turno + mensaje",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        900,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": false,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "cond-found",
              "leftValue": "={{ $json.turno_found }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        }
      },
      "id": "node-if-found",
      "name": "Turno encontrado?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1120,
        300
      ]
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": false
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.mensaje }}",
                    "rightValue": "confirmo el turno.",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "renameOutput": true,
              "outputKey": "confirmar"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": false
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.mensaje }}",
                    "rightValue": "cancelar turno",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "renameOutput": true,
              "outputKey": "cancelar"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": false
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.mensaje }}",
                    "rightValue": "reprogramar turno",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "renameOutput": true,
              "outputKey": "reagendar"
            }
          ]
        },
        "options": {}
      },
      "id": "node-switch",
      "name": "Switch accion",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3,
      "position": [
        1340,
        200
      ]
    },
    {
      "parameters": {
        "url": "=REEMPLAZAR_CON_SUPABASE_URL/rest/v1/turnos?id=eq.{{ $json.turno_id }}",
        "httpMethod": "PATCH",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "apikey",
              "value": "REEMPLAZAR_CON_SUPABASE_SERVICE_ROLE_KEY"
            },
            {
              "name": "Authorization",
              "value": "<redacted-credential>"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Prefer",
              "value": "return=minimal"
            }
          ]
        },
        "sendBody": true,
        "contentType": "json",
        "body": "={ \"estado\": \"confirmado\" }",
        "options": {}
      },
      "id": "node-supa-confirm",
      "name": "Supabase: confirmar turno",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1560,
        100
      ]
    },
    {
      "parameters": {
        "url": "=REEMPLAZAR_CON_SUPABASE_URL/rest/v1/turnos?id=eq.{{ $json.turno_id }}",
        "httpMethod": "PATCH",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "apikey",
              "value": "REEMPLAZAR_CON_SUPABASE_SERVICE_ROLE_KEY"
            },
            {
              "name": "Authorization",
              "value": "<redacted-credential>"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Prefer",
              "value": "return=minimal"
            }
          ]
        },
        "sendBody": true,
        "contentType": "json",
        "body": "={ \"estado\": \"cancelado\", \"motivo_cancelacion\": \"por_paciente\" }",
        "options": {}
      },
      "id": "node-supa-cancel",
      "name": "Supabase: cancelar turno",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1560,
        260
      ]
    },
    {
      "parameters": {
        "url": "https://api.twilio.com/2010-04-01/Accounts/REEMPLAZAR_CON_ACCOUNT_SID/Messages.json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "sendBody": true,
        "contentType": "form-urlencoded",
        "bodyParameters": {
          "parameters": [
            {
              "name": "From",
              "value": "whatsapp:+5493625456570"
            },
            {
              "name": "To",
              "value": "={{ $('Combinar turno + mensaje').first().json.from_raw }}"
            },
            {
              "name": "Body",
              "value": "\u2705 \u00a1Gracias! Tu turno est\u00e1 confirmado. Te esperamos \ud83d\ude4c"
            }
          ]
        },
        "options": {}
      },
      "id": "node-reply-confirm",
      "name": "WA: respuesta confirmado",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1780,
        100
      ],
      "credentials": {
        "httpBasicAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "url": "https://api.twilio.com/2010-04-01/Accounts/REEMPLAZAR_CON_ACCOUNT_SID/Messages.json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "sendBody": true,
        "contentType": "form-urlencoded",
        "bodyParameters": {
          "parameters": [
            {
              "name": "From",
              "value": "whatsapp:+5493625456570"
            },
            {
              "name": "To",
              "value": "={{ $('Combinar turno + mensaje').first().json.from_raw }}"
            },
            {
              "name": "Body",
              "value": "\u274c Entendemos, tu turno fue cancelado. Si quer\u00e9s sacar uno nuevo escribinos al +543624955955."
            }
          ]
        },
        "options": {}
      },
      "id": "node-reply-cancel",
      "name": "WA: respuesta cancelado",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1780,
        260
      ],
      "credentials": {
        "httpBasicAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "url": "https://api.twilio.com/2010-04-01/Accounts/REEMPLAZAR_CON_ACCOUNT_SID/Messages.json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "sendBody": true,
        "contentType": "form-urlencoded",
        "bodyParameters": {
          "parameters": [
            {
              "name": "From",
              "value": "whatsapp:+5493625456570"
            },
            {
              "name": "To",
              "value": "={{ $('Combinar turno + mensaje').first().json.from_raw }}"
            },
            {
              "name": "Body",
              "value": "\ud83d\udcc5 Claro, para reagendar escribinos directamente al +543624955955 y te ayudamos \ud83d\ude0a"
            }
          ]
        },
        "options": {}
      },
      "id": "node-reply-reagendar",
      "name": "WA: respuesta reagendar",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1780,
        420
      ],
      "credentials": {
        "httpBasicAuth": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Webhook Twilio Respuestas": {
      "main": [
        [
          {
            "node": "Extraer datos",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extraer datos": {
      "main": [
        [
          {
            "node": "Buscar turno en Supabase",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Buscar turno en Supabase": {
      "main": [
        [
          {
            "node": "Combinar turno + mensaje",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combinar turno + mensaje": {
      "main": [
        [
          {
            "node": "Turno encontrado?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Turno encontrado?": {
      "main": [
        [
          {
            "node": "Switch accion",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Switch accion": {
      "confirmar": [
        [
          {
            "node": "Supabase: confirmar turno",
            "type": "main",
            "index": 0
          }
        ]
      ],
      "cancelar": [
        [
          {
            "node": "Supabase: cancelar turno",
            "type": "main",
            "index": 0
          }
        ]
      ],
      "reagendar": [
        [
          {
            "node": "WA: respuesta reagendar",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Supabase: confirmar turno": {
      "main": [
        [
          {
            "node": "WA: respuesta confirmado",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Supabase: cancelar turno": {
      "main": [
        [
          {
            "node": "WA: respuesta cancelado",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

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

About this workflow

Vitalis - Respuestas WhatsApp Twilio. Uses httpRequest. Webhook trigger; 11 nodes.

Source: https://github.com/gkovalek/agendavitalis/blob/main/n8n/workflow-2-respuestas-paciente.json — original creator credit. Request a take-down →

More Slack & Telegram workflows → · Browse all categories →

Related workflows

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

Slack & Telegram

Flujowhatsapp. Uses @aldinokemal2104/n8n-nodes-gowa, postgres, httpRequest. Webhook trigger; 65 nodes.

@Aldinokemal2104/N8N Nodes Gowa, Postgres, HTTP Request
Slack & Telegram

HR teams, IT Operations, and System Administrators managing employee onboarding at scale. It’s perfect if you use Odoo 18 to trigger account requests and need Redmine + GitLab accounts created instant

HTTP Request, Slack
Slack & Telegram

This workflow is a complete, production-ready solution for recovering abandoned carts in Shopify stores using a multi-channel, multi-touch approach. It automates personalized follow-ups via Email, SMS

HTTP Request, Shopify, SendGrid +5
Slack & Telegram

WF_UNIFIED_LEGAL_AUTOMATION. Uses googleSheets, httpRequest, telegram, telegramTrigger. Webhook trigger; 53 nodes.

Google Sheets, HTTP Request, Telegram +1
Slack & Telegram

Backbrief: transcripts (Zoom webhook -> Slack + vault). Uses httpRequest, slack. Webhook trigger; 52 nodes.

HTTP Request, Slack