This workflow follows the HTTP Request → Supabase recipe pattern — see all workflows that pair these two integrations.
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": "02 - Recordatorio 24h antes (CON VERIFICACI\u00d3N) \u2705",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 10 * * *"
}
]
}
},
"id": "02b2470c-a797-4827-a2b9-6e1065a1784d",
"name": "\u23f0 Cron Diario 10:00 AM",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.1,
"position": [
-1088,
608
]
},
{
"parameters": {
"operation": "getAll",
"tableId": "reservations",
"returnAll": true
},
"id": "93d5bd94-8838-44c1-97f0-69d6ad539502",
"name": "\ud83d\udcca Obtener TODAS las Reservas",
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
-848,
608
],
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const tomorrow = new Date();\ntomorrow.setDate(tomorrow.getDate() + 1);\nconst tomorrowStr = tomorrow.toISOString().split('T')[0];\n\nconst filtered = $input.all().filter(item => {\n const data = item.json;\n return data.reservation_date === tomorrowStr &&\n data.status === 'pending' &&\n data.customer_phone !== null &&\n data.customer_phone !== '';\n});\n\nconsole.log(`\ud83d\udd0d Encontradas ${filtered.length} reservas para ma\u00f1ana (${tomorrowStr})`);\n\nreturn filtered;"
},
"id": "7dcf4283-5d93-416f-b7f5-ffd740aee264",
"name": "\ud83d\udd0d Filtrar: Ma\u00f1ana + Pending",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-608,
608
]
},
{
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $input.all().length }}",
"value2": 0,
"operator": {
"type": "number",
"operation": "gt"
}
}
]
},
"options": {}
},
"id": "bd9a6b86-4d10-4544-a3c0-aa3748c014f1",
"name": "\u2753 \u00bfHay Reservas?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
-416,
608
]
},
{
"parameters": {
"options": {}
},
"id": "5d133977-c65b-42d8-bd19-bdd7f6ef05ef",
"name": "\ud83d\udd01 Loop Cada Reserva",
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"position": [
-144,
400
]
},
{
"parameters": {
"method": "POST",
"url": "https://ktsqwvhqamedpmzkzjaz.supabase.co/rest/v1/rpc/check_confirmation_sent",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "supabaseApi",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"p_reservation_id\": \"{{ $json.id }}\",\n \"p_message_type\": \"24h\"\n}",
"options": {}
},
"id": "check-duplicate-24h",
"name": "\ud83d\udd0d Verificar Si Ya Enviado",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
80,
400
],
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"notes": "\u2705 Verifica duplicados v\u00eda RPC"
},
{
"parameters": {
"jsCode": "const reservation = $('\ud83d\udd01 Loop Cada Reserva').first().json;\nconst checkResult = $input.first().json;\n\nconst alreadySent = checkResult === true;\n\nconsole.log(`\ud83d\udd0d Reserva ${reservation.id}: ${alreadySent ? '\u26a0\ufe0f YA ENVIADO' : '\u2705 PENDIENTE DE ENVIAR'}`);\n\nreturn {\n ...reservation,\n already_sent: alreadySent,\n should_send: !alreadySent\n};"
},
"id": "process-check-24h",
"name": "\ud83d\udd04 Procesar Verificaci\u00f3n",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
304,
400
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "should-send",
"leftValue": "={{ $json.should_send }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "if-should-send-24h",
"name": "\u2753 \u00bfEnviar Mensaje?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
528,
400
]
},
{
"parameters": {
"jsCode": "const items = $input.all();\n\nconst results = items.map(item => {\n const data = item.json;\n let phone = data.customer_phone || '';\n \n if (phone && !phone.startsWith('+')) {\n if (phone.startsWith('34')) {\n phone = '+' + phone;\n } else if (phone.startsWith('0034')) {\n phone = '+' + phone.substring(2);\n } else {\n phone = '+34' + phone;\n }\n }\n \n return {\n json: {\n ...data,\n customer_phone_normalized: phone\n }\n };\n});\n\nreturn results;"
},
"id": "328006ee-56b3-4071-8c11-be0af71d4d46",
"name": "\ud83d\udcde Normalizar Tel\u00e9fono",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
752,
320
]
},
{
"parameters": {
"operation": "getAll",
"tableId": "restaurants",
"returnAll": true,
"filters": {
"conditions": [
{
"keyName": "id",
"condition": "eq",
"keyValue": "={{ $json.restaurant_id }}"
}
]
}
},
"id": "dad76973-867b-495e-bdd8-74230c02a031",
"name": "\ud83d\udccd Obtener Config Restaurante",
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
944,
320
],
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "getAll",
"tableId": "message_templates",
"returnAll": false,
"limit": 1,
"filters": {
"conditions": [
{
"keyName": "restaurant_id",
"condition": "eq",
"keyValue": "={{ $('\ud83d\udccd Obtener Config Restaurante').first().json.id }}"
},
{
"keyName": "category",
"condition": "eq",
"keyValue": "='confirmacion_24h'"
},
{
"keyName": "is_active",
"condition": "eq",
"keyValue": "true"
}
]
}
},
"id": "aaa111bb-2222-3333-4444-555566667777",
"name": "\ud83d\udcc4 Obtener Plantilla 24h",
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
1136,
320
],
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const templateData = $json;\nconst restaurantData = $('\ud83d\udccd Obtener Config Restaurante').first().json;\nconst reservaData = $('\ud83d\udcde Normalizar Tel\u00e9fono').first().json;\n\nif (!reservaData || !restaurantData) {\n throw new Error('\u274c Faltan datos de reserva o restaurante');\n}\n\nconst reserva = reservaData;\nconst restaurant = restaurantData;\n\nlet template = templateData?.content_markdown || '';\n\nif (!template || template.trim() === '') {\n template = `Hola {{customer_name}}! \ud83d\udc4b\\n\\nTe recordamos tu reserva en {{restaurant_name}} para ma\u00f1ana a las {{reservation_time}} para {{party_size}} persona(s).\\n\\n\u00a1Te esperamos!`;\n}\n\nconst variables = {\n customer_name: reserva.customer_name || 'Cliente',\n restaurant_name: restaurant.name || 'Restaurante',\n reservation_time: reserva.reservation_time || '',\n party_size: (reserva.party_size || 1).toString()\n};\n\nlet message = template;\nfor (const [key, value] of Object.entries(variables)) {\n const regex = new RegExp(`\\\\{\\\\{${key}\\\\}\\\\}`, 'g');\n message = message.replace(regex, value);\n}\n\nif (!message || message.trim() === '') {\n message = `Hola ${variables.customer_name}! Te recordamos tu reserva en ${variables.restaurant_name} para ma\u00f1ana a las ${variables.reservation_time}.`;\n}\n\nreturn [{\n json: {\n id: reserva.id,\n restaurant_id: reserva.restaurant_id,\n customer_id: reserva.customer_id,\n customer_name: reserva.customer_name,\n customer_phone: reserva.customer_phone,\n customer_phone_normalized: reserva.customer_phone_normalized,\n reservation_date: reserva.reservation_date,\n reservation_time: reserva.reservation_time,\n party_size: reserva.party_size,\n status: reserva.status,\n restaurant_phone: restaurant.phone,\n restaurant_name: restaurant.name,\n message_final: message\n }\n}];"
},
"id": "bbb222cc-3333-4444-5555-666677778888",
"name": "\ud83d\udd04 Reemplazar Variables",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1328,
320
]
},
{
"parameters": {
"from": "={{ $json.restaurant_phone }}",
"to": "={{ $json.customer_phone_normalized }}",
"toWhatsapp": true,
"message": "={{ $json.message_final }}",
"options": {}
},
"id": "fc22dd7a-a03e-4a35-87da-4b148f90f6f5",
"name": "\ud83d\udcf1 Twilio: Enviar WhatsApp",
"type": "n8n-nodes-base.twilio",
"typeVersion": 1,
"position": [
1520,
320
],
"credentials": {
"twilioApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "create",
"tableId": "confirmation_messages",
"fieldsUi": {
"fieldValues": [
{
"fieldId": "reservation_id",
"fieldValue": "={{ $('\ud83d\udd04 Reemplazar Variables').first().json.id }}"
},
{
"fieldId": "restaurant_id",
"fieldValue": "={{ $('\ud83d\udd04 Reemplazar Variables').first().json.restaurant_id }}"
},
{
"fieldId": "message_type",
"fieldValue": "24h"
},
{
"fieldId": "status",
"fieldValue": "sent"
},
{
"fieldId": "customer_phone",
"fieldValue": "={{ $('\ud83d\udd04 Reemplazar Variables').first().json.customer_phone_normalized }}"
},
{
"fieldId": "customer_name",
"fieldValue": "={{ $('\ud83d\udd04 Reemplazar Variables').first().json.customer_name }}"
},
{
"fieldId": "reservation_date",
"fieldValue": "={{ $('\ud83d\udd04 Reemplazar Variables').first().json.reservation_date }}"
},
{
"fieldId": "reservation_time",
"fieldValue": "={{ $('\ud83d\udd04 Reemplazar Variables').first().json.reservation_time }}"
},
{
"fieldId": "message_content",
"fieldValue": "={{ $('\ud83d\udd04 Reemplazar Variables').first().json.message_final }}"
}
]
},
"options": {}
},
"id": "45a665bb-1706-4f77-a66f-0c14db86251f",
"name": "\ud83d\udcbe Registrar en confirmation_messages",
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
1712,
320
],
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"notes": "\u2705 Registro en la tabla del nuevo sistema"
},
{
"parameters": {
"jsCode": "console.log('\u2705 Mensaje 24h enviado y registrado');\nreturn { success: true };"
},
"id": "log-sent-24h",
"name": "\u2705 Log: Enviado",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1904,
320
]
},
{
"parameters": {
"jsCode": "console.log('\u26a0\ufe0f Mensaje ya enviado anteriormente - Saltando');\nreturn { skipped: true };"
},
"id": "log-skipped-24h",
"name": "\u26a0\ufe0f Log: Saltado",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
752,
480
]
},
{
"parameters": {},
"id": "95e5a6ba-9bfc-4c24-a68d-c39cd570be42",
"name": "\u2705 Fin: Sin Reservas",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [
-128,
848
]
}
],
"connections": {
"\u23f0 Cron Diario 10:00 AM": {
"main": [
[
{
"node": "\ud83d\udcca Obtener TODAS las Reservas",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udcca Obtener TODAS las Reservas": {
"main": [
[
{
"node": "\ud83d\udd0d Filtrar: Ma\u00f1ana + Pending",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udd0d Filtrar: Ma\u00f1ana + Pending": {
"main": [
[
{
"node": "\u2753 \u00bfHay Reservas?",
"type": "main",
"index": 0
}
]
]
},
"\u2753 \u00bfHay Reservas?": {
"main": [
[
{
"node": "\ud83d\udd01 Loop Cada Reserva",
"type": "main",
"index": 0
}
],
[
{
"node": "\u2705 Fin: Sin Reservas",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udd01 Loop Cada Reserva": {
"main": [
[],
[
{
"node": "\ud83d\udd0d Verificar Si Ya Enviado",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udd0d Verificar Si Ya Enviado": {
"main": [
[
{
"node": "\ud83d\udd04 Procesar Verificaci\u00f3n",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udd04 Procesar Verificaci\u00f3n": {
"main": [
[
{
"node": "\u2753 \u00bfEnviar Mensaje?",
"type": "main",
"index": 0
}
]
]
},
"\u2753 \u00bfEnviar Mensaje?": {
"main": [
[
{
"node": "\ud83d\udcde Normalizar Tel\u00e9fono",
"type": "main",
"index": 0
}
],
[
{
"node": "\u26a0\ufe0f Log: Saltado",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udcde Normalizar Tel\u00e9fono": {
"main": [
[
{
"node": "\ud83d\udccd Obtener Config Restaurante",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udccd Obtener Config Restaurante": {
"main": [
[
{
"node": "\ud83d\udcc4 Obtener Plantilla 24h",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udcc4 Obtener Plantilla 24h": {
"main": [
[
{
"node": "\ud83d\udd04 Reemplazar Variables",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udd04 Reemplazar Variables": {
"main": [
[
{
"node": "\ud83d\udcf1 Twilio: Enviar WhatsApp",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udcf1 Twilio: Enviar WhatsApp": {
"main": [
[
{
"node": "\ud83d\udcbe Registrar en confirmation_messages",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udcbe Registrar en confirmation_messages": {
"main": [
[
{
"node": "\u2705 Log: Enviado",
"type": "main",
"index": 0
}
]
]
},
"\u2705 Log: Enviado": {
"main": [
[
{
"node": "\ud83d\udd01 Loop Cada Reserva",
"type": "main",
"index": 0
}
]
]
},
"\u26a0\ufe0f Log: Saltado": {
"main": [
[
{
"node": "\ud83d\udd01 Loop Cada Reserva",
"type": "main",
"index": 0
}
]
]
}
},
"meta": {
"templateCredsSetupCompleted": true
},
"tags": [
{
"createdAt": "2025-10-22T19:00:00.000Z",
"updatedAt": "2025-10-22T19:00:00.000Z",
"id": "recordatorio-24h-verificacion",
"name": "\u2705 Con Verificaci\u00f3n de Duplicados"
}
]
}
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.
supabaseApitwilioApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
02 - Recordatorio 24h antes (CON VERIFICACIÓN) ✅. Uses supabase, httpRequest, twilio. Scheduled trigger; 17 nodes.
Source: https://github.com/gustausantin/La-ia-app/blob/92b27fcbe408bc47a0a637cacdf245937e053944/n8n/workflows/02-recordatorio-24h-SIMPLE-FINAL.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.
03 - Recordatorio 4h (CON VERIFICACIÓN) ✅. Uses supabase, httpRequest, twilio. Scheduled trigger; 17 nodes.
This workflow solves a common problem with RSS feeds: they often only provide a short summary or snippet of the full article. This template automatically monitors a list of your favorite blog RSS feed
This workflow is a multi-system document synchronization pipeline built in n8n, designed to automatically sync and back up files between Microsoft SharePoint, Supabase/Postgres, and Google Drive.
• Fetches IT-related tenders from the French BOAMP API (filter: informatique) • Scores each tender with OpenAI (pertinence, budget, stack, GO/NO-GO) • Routes to Supabase as hot (≥75) or archived • Run
How it works: