This workflow follows the HTTP Request → Postgres 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": "dummy_client - Shopify abandoned carts",
"nodes": [
{
"parameters": {
"content": "## Recuperaci\u00f3n de datos del checkout abandonado y chequeo\n**Desde Shopify** se recuperan id del checkout, n\u00famero de tel\u00e9fono y nombre del usuario. ",
"height": 816,
"width": 1856,
"color": 4
},
"type": "n8n-nodes-base.stickyNote",
"position": [
-240,
-752
],
"typeVersion": 1,
"id": "df940db3-47fc-445c-88c4-093daff6d53b",
"name": "Sticky Note"
},
{
"parameters": {
"content": "## Chequeo cada una hora de checkouts \n**Que no se combirtieron en \u00f3rdenes pasadas las 3 horas del \u00faltimo update.**",
"height": 464,
"width": 1856,
"color": 6
},
"type": "n8n-nodes-base.stickyNote",
"position": [
-240,
80
],
"typeVersion": 1,
"id": "338aac18-37c4-4c84-ab1b-d1433e64405c",
"name": "Sticky Note1"
},
{
"parameters": {
"content": "## Chequeo de resultados y eliminaci\u00f3n de checkouts que hayan finalizado con compras.\n**Data sobre checkouts tras haber enviado la campa\u00f1a**",
"height": 432,
"width": 944
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-240,
560
],
"id": "189e7ed8-6768-44f9-8e0e-27a33ed2f1d7",
"name": "Sticky Note2"
},
{
"parameters": {
"jsCode": "\n// Configuraci\u00f3n: \u00bfCu\u00e1ntas horas de espera? (0.01 es aprox 36 segundos para testing)\nconst hoursDelay = 0.5; \n\n// 1. Obtenemos la hora ACTUAL forzada a Chile\nconst now = DateTime.now().setZone(\"America/Santiago\");\n\nreturn $input.all().filter(item => {\n const row = item.json;\n const phoneStr = row.customer_phone ? row.customer_phone.toString().trim() : \"\";\n // Validaci\u00f3n b\u00e1sica\n if (!row.updated_at || !row.customer_phone || phoneStr.length < 8) return false;\n\n // 2. Parseamos la fecha guardada (Luxon lee el -03:00 autom\u00e1ticamente)\n const lastUpdate = DateTime.fromISO(row.updated_at);\n\n // 3. Calculamos la diferencia en HORAS exactas\n // Esto resta matem\u00e1ticamente las fechas sin importar la zona del servidor\n const diffInHours = now.diff(lastUpdate, 'hours').hours;\n\n //Debug: Descomentar esto para ver en la consola cu\u00e1nto tiempo calcula\n // console.log(`ID: ${row.checkout_id} | Diff: ${diffInHours.toFixed(4)} horas`);\n\n // 4. La condici\u00f3n: Solo pasa si la diferencia es mayor al delay\n // Y agregamos seguridad: que no sea del futuro (diff > 0)\n return diffInHours > hoursDelay && diffInHours > 0; \n});"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
336,
272
],
"id": "bfbf4088-1cca-4f80-bc72-f77a2a28de96",
"name": "Code in JavaScript",
"alwaysOutputData": false
},
{
"parameters": {
"options": {}
},
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"position": [
640,
272
],
"id": "3abb43d0-f92b-491d-9b66-7202324b945a",
"name": "Loop Over Items"
},
{
"parameters": {
"method": "POST",
"url": "https://your-store.myshopify.com/admin/api/2025-10/graphql.json",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "shopifyAccessTokenApi",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"query\": \"mutation discountCodeBasicCreate($basicCodeDiscount: DiscountCodeBasicInput!) { discountCodeBasicCreate(basicCodeDiscount: $basicCodeDiscount) { codeDiscountNode { codeDiscount { ... on DiscountCodeBasic { title codes(first: 1) { nodes { code } } startsAt endsAt } } } userErrors { field message } } }\",\n \"variables\": {\n \"basicCodeDiscount\": {\n \"title\": \"Recuperaci\u00f3n {{ $node['Loop Over Items'].json.checkout_id }}\",\n \"code\": \"AGREGAR_REGEX_FIJO{{ Math.random().toString(36).substring(2, 6).toUpperCase() }}\",\n \"startsAt\": \"{{ DateTime.now().setZone('America/Santiago').toISO() }}\",\n \"endsAt\": \"{{ DateTime.now().setZone('America/Santiago').plus({ hours: 24 }).toISO() }}\",\n \"customerSelection\": {\n \"all\": true\n },\n \"usageLimit\": 1,\n \"combinesWith\": {\n \"orderDiscounts\": true,\n \"productDiscounts\": true,\n \"shippingDiscounts\": true\n },\n \"customerGets\": {\n \"value\": {\n \"percentage\": 0.1\n },\n \"items\": {\n \"all\": true\n }\n }\n }\n }\n}",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
928,
288
],
"id": "00b56a49-e297-440c-98c9-07b17f8c75e4",
"name": "Sin customer_id",
"credentials": {
"shopifyAccessTokenApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"authentication": "accessToken",
"topic": "orders/create"
},
"type": "n8n-nodes-base.shopifyTrigger",
"typeVersion": 1,
"position": [
-176,
736
],
"id": "bac48c9c-6fef-44cf-9ea1-2917f8132a7e",
"name": "Orders Trigger",
"credentials": {
"shopifyAccessTokenApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"authentication": "accessToken",
"topic": "checkouts/update"
},
"type": "n8n-nodes-base.shopifyTrigger",
"typeVersion": 1,
"position": [
-176,
-384
],
"id": "67ae7f8d-abd9-4d44-8bc6-3cda6c0eda35",
"name": "Checkout Trigger",
"credentials": {
"shopifyAccessTokenApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"leftValue": "={{ $json.status }}",
"rightValue": "pending",
"operator": {
"type": "string",
"operation": "equals"
},
"id": "4011efa8-89c4-4995-b80a-318ccb5d65d7"
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "status:pending"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "0ec00969-1a7c-4f51-83a9-77a8676a4b28",
"leftValue": "={{ $json.status }}",
"rightValue": "sent",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "status:sent"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.switch",
"typeVersion": 3.4,
"position": [
224,
736
],
"id": "5409b9a8-d32f-4046-b116-6c6b6afdf0da",
"name": "Switch"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "689719d0-3981-4488-a542-a9e9787ed9e5",
"leftValue": "={{ $('Checkout Trigger').item.json.discount_codes[0].code }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
}
},
{
"id": "d40cad10-6a78-414b-b879-566b2dc9c7fa",
"leftValue": "={{ $('Checkout Trigger').item.json.discount_codes[0].code }}",
"rightValue": "ALDUMMY",
"operator": {
"type": "string",
"operation": "contains"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
960,
-576
],
"id": "7c420f0b-0e28-4bb3-8547-0920fbc167f1",
"name": "If3"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "ced400d6-c9fe-4534-b6ea-b293b23f7ddf",
"leftValue": "={{ $('Checkout Trigger').item.json.discount_codes[0].code }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
}
},
{
"id": "fceb5470-233d-47c6-b445-b76cbb8af310",
"leftValue": "={{ $('Checkout Trigger').item.json.discount_codes[0].code }}",
"rightValue": "XXXXX",
"operator": {
"type": "string",
"operation": "startsWith"
}
},
{
"id": "1a3f6911-4a3a-4e3d-9dba-8af4decf5d78",
"leftValue": "={{ $json.discount }}",
"rightValue": "={{ $('Checkout Trigger').item.json.discount_codes[0].code }}",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
960,
-256
],
"id": "0737f293-ea35-4d20-890c-5b218dcc5fd2",
"name": "If1"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose",
"version": 3
},
"conditions": [
{
"id": "b1009047-a7d2-4260-8470-09f165247ff2",
"leftValue": "={{ $json.checkout_id }}",
"rightValue": "={{ $('Checkout Trigger').item.json.id }}",
"operator": {
"type": "number",
"operation": "notExists",
"singleValue": true
}
},
{
"id": "94f8dc54-7d32-43a4-a769-fc95f6d83cfa",
"leftValue": "={{ $json.status }}",
"rightValue": "pending",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "or"
},
"looseTypeValidation": true,
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
608,
-384
],
"id": "3bb273e2-0930-46bb-81ca-2aef672601a5",
"name": "If4",
"alwaysOutputData": false
},
{
"parameters": {
"phoneNumberId": "PHONE_NUMBER_ID_PLACEHOLDER",
"recipientPhoneNumber": "=\"{{ $('Loop Over Items').item.json.customer_phone }}\"",
"template": "dummy_recovery_template|es_ES",
"components": {
"component": [
{
"type": "header",
"headerParameters": {
"parameter": [
{
"type": "image",
"imageLink": "={{ \n(() => {\n // 1. Obtenemos la URL original de la imagen desde Shopify\n let originalUrl = \"URL_DE_TU_IMAGEN\";\n\n // 2. Limpiamos el protocolo (https://) para pasarlo limpio al proxy\n let cleanUrl = originalUrl.replace(/^https?:\\/\\//, '');\n\n // 3. CONSTRUIMOS EL LINK DE RECORTE HORIZONTAL\n // w=800 & h=418 -> Proporci\u00f3n 1.91:1 (Ideal para WhatsApp Header)\n // fit=cover -> \"Llena el espacio\". Esto obliga a recortar lo que sobre.\n // a=center -> \"Alignment Center\". Asegura que el contenido (que est\u00e1n al medio) no se corte.\n \n return `https://wsrv.nl/?url=${cleanUrl}&w=800&h=418&fit=cover&a=center&output=jpg`;\n})() }}"
}
]
}
},
{
"bodyParameters": {
"parameter": [
{
"text": "={{ $('Loop Over Items').item.json.customer_name.toString().split(\" \")[0] ?? \"\" }}"
},
{
"text": "={{ (() => {\n // 1. OBTENCI\u00d3N DE DATOS (Ajustado para el Wrapper)\n const rawData = $('Loop Over Items').item.json.line_items;\n \n // Intentamos leer .products, si no existe, vemos si rawData ya es el array, si no, array vac\u00edo.\n const items = rawData?.products || (Array.isArray(rawData) ? rawData : []) || [];\n\n // 2. Objeto para agrupar\n const counts = {};\n\n // Solo iteramos si tenemos items\n if (items.length > 0) {\n items.forEach(item => {\n const title = item.title;\n const qty = parseInt(item.quantity) || 1;\n\n if (counts[title]) {\n counts[title] += qty;\n } else {\n counts[title] = qty;\n }\n });\n }\n\n // 3. Generamos el texto final\n return Object.keys(counts).map(name => {\n const qty = counts[name];\n return `${name} (x${qty})`;\n }).join(', ');\n})() }}"
},
{
"text": "={{ $json.data.discountCodeBasicCreate.codeDiscountNode.codeDiscount.codes.nodes[0].code }}"
}
]
}
},
{
"type": "button",
"sub_type": "url",
"buttonParameters": {
"parameter": {
"type": "text",
"text": "={{ (() => {\n // 1. OBTENCI\u00d3N DE DATOS (Ajustado para el Wrapper)\n const rawData = $('Loop Over Items').item.json.line_items;\n \n // Buscamos dentro del envoltorio .products\n const items = rawData?.products || (Array.isArray(rawData) ? rawData : []) || [];\n \n // 2. Construir el string del carrito: \"12345:1,67890:2\"\n const cartString = items\n .map(item => `${item.variant_id}:${item.quantity}`)\n .join(',');\n\n // 3. Obtener el c\u00f3digo de descuento\n // (Aseg\u00farate que la ruta al descuento sea correcta seg\u00fan tu flujo)\n const discountCode = $json.data.discountCodeBasicCreate.codeDiscountNode\n .codeDiscount.codes.nodes[0].code;\n\n return `cart/${cartString}?discount=${discountCode}`;\n })() }}"
}
}
}
]
}
},
"type": "n8n-nodes-base.whatsApp",
"typeVersion": 1.1,
"position": [
1152,
288
],
"id": "31c53f68-8e0e-406c-9f31-a9a7810eff84",
"name": "Send template",
"credentials": {
"whatsAppApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 0 7-22/1 * * *"
}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.3,
"position": [
-192,
272
],
"id": "0316ee9a-e2c5-44a7-8fef-f0f41afa4500",
"name": "Schedule Trigger"
},
{
"parameters": {
"operation": "get",
"tableId": "dummy_table_checkouts",
"filters": {
"conditions": [
{
"keyName": "checkout_id",
"keyValue": "={{ $json.id }}"
}
]
}
},
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
368,
-384
],
"id": "d61c44cf-bd41-4f05-9050-28788468035c",
"name": "Get a row",
"alwaysOutputData": true,
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "upsert",
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"table": {
"__rl": true,
"value": "dummy_table_checkouts",
"mode": "list",
"cachedResultName": "dummy_table_checkouts"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"checkout_id": "={{ $('Checkout Trigger').item.json.id }}",
"customer_id": "={{ $('Checkout Trigger').item.json.customer?.id }}",
"line_items": "={{\n{\n \"products\": $('Checkout Trigger').item.json.line_items.map(item => ({\n title: item.title,\n quantity: item.quantity,\n price: item.price,\n variant_id: item.variant_id,\n product_id: item.product_id,\n sku: item.sku || \"\",\n image: item.image\n }))\n}\n}}",
"checkout_token": "={{ $('Checkout Trigger').item.json.token }}",
"customer_name": "={{\n(() => {\n // 1. FUENTE: Leemos directo del Trigger para evitar datos sucios de nodos intermedios\n const data = $('Checkout Trigger').item.json;\n\n // 2. FUNCI\u00d3N DE EXTRACCI\u00d3N SEGURA\n // Esta funci\u00f3n analiza el objeto y verifica que no sea un Array vac\u00edo [] ni null\n const getNameFrom = (obj) => {\n // Si no existe, o si es un Array (caso del JSON 3), devolvemos null\n if (!obj || Array.isArray(obj)) return null;\n \n // Si existe pero no tiene first_name, devolvemos null\n if (!obj.first_name) return null;\n\n // Si tiene datos, los unimos\n return (obj.first_name + ' ' + (obj.last_name || '')).trim();\n };\n\n // 3. CASCADA DE B\u00daSQUEDA (Prioridad de mejor a peor)\n\n // Intento A: Direcci\u00f3n de Env\u00edo (El m\u00e1s com\u00fan si llenaron datos)\n let finalName = getNameFrom(data.shipping_address);\n\n // Intento B: Direcci\u00f3n de Facturaci\u00f3n (A veces llenan esto primero)\n if (!finalName) finalName = getNameFrom(data.billing_address);\n\n // Intento C: Datos de Cuenta (Solo si es usuario logueado - Caso 1 y 2)\n if (!finalName) finalName = getNameFrom(data.customer);\n\n // Intento D: Direcci\u00f3n guardada en la cuenta\n if (!finalName && data.customer?.default_address) {\n finalName = getNameFrom(data.customer.default_address);\n }\n\n // 4. FALLBACK DE EMAIL (Para el Caso 3 - JSON \"Vac\u00edo\")\n // Si todo lo anterior fall\u00f3, tomamos lo que est\u00e1 antes del @\n // Ejemplo: \"dfrigferio.nazal@gmail.com\" -> \"dfrigferio.nazal\"\n if (!finalName && data.email) {\n finalName = data.email.split('@')[0];\n }\n\n // 5. RED DE SEGURIDAD FINAL\n return finalName || '';\n})()\n}}",
"customer_phone": "={{\n(() => {\n // 1. CASCADA DE PRIORIDAD: Busca el primer valor que no sea nulo ni vac\u00edo\n let phone = $json.shipping_address?.phone \n || $('Checkout Trigger').item.json.billing_address?.phone \n || $('Checkout Trigger').item.json.customer?.phone \n || $('Checkout Trigger').item.json.phone \n || ''; // Fallback vac\u00edo si no hay nada\n\n // 2. LIMPIEZA: Convierte a string y elimina todo lo que NO sea n\u00famero (quita +, espacios, guiones)\n phone = phone.toString().replace(/\\D/g, '');\n\n // 3. VALIDACI\u00d3N: Si qued\u00f3 vac\u00edo, retorna vac\u00edo\n if (!phone) return '';\n\n // 4. FORMATO CHILE: \n // Si ya empieza con 56, lo dejamos tal cual.\n // Si no empieza con 56 (ej: 954220507), le agregamos el 56.\n return phone.startsWith('56') ? phone : '56' + phone;\n})()\n}}",
"customer_email": "={{ $('Checkout Trigger').item.json.email || \"\" }}",
"total_price": "={{ $('Checkout Trigger').item.json.total_price }}",
"updated_at": "={{$('Checkout Trigger').item.json.updated_at ?? DateTime.now().setZone(\"America/Santiago\").toString() }}",
"status": "sent",
"checkout_url": "={{ $('Checkout Trigger').item.json.abandoned_checkout_url }}",
"discount": "={{ $('Checkout Trigger').item.json.discount_codes[0].code ?? $('Get a row').item.json.discount }}"
},
"matchingColumns": [
"checkout_id"
],
"schema": [
{
"id": "checkout_id",
"displayName": "checkout_id",
"required": true,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "checkout_token",
"displayName": "checkout_token",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "customer_id",
"displayName": "customer_id",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": false
},
{
"id": "customer_name",
"displayName": "customer_name",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "customer_phone",
"displayName": "customer_phone",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "customer_email",
"displayName": "customer_email",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "total_price",
"displayName": "total_price",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": false
},
{
"id": "discount",
"displayName": "discount",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false,
"removed": false
},
{
"id": "checkout_url",
"displayName": "checkout_url",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "line_items",
"displayName": "line_items",
"required": false,
"defaultMatch": false,
"display": true,
"type": "object",
"canBeUsedToMatch": false
},
{
"id": "updated_at",
"displayName": "updated_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": false
},
{
"id": "status",
"displayName": "status",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "recovered",
"displayName": "recovered",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false,
"removed": true
},
{
"id": "created_at",
"displayName": "created_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": false,
"removed": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
1408,
-688
],
"id": "9dc0b9a1-87d5-4e34-92e5-fee45723def9",
"name": "Checkout nuevo - Permalink",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "upsert",
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"table": {
"__rl": true,
"value": "dummy_table_checkouts",
"mode": "list",
"cachedResultName": "dummy_table_checkouts"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"checkout_id": "={{ $('Checkout Trigger').item.json.id }}",
"customer_id": "={{ $('Checkout Trigger').item.json.customer?.id }}",
"line_items": "={{\n{\n \"products\": $('Checkout Trigger').item.json.line_items.map(item => ({\n title: item.title,\n quantity: item.quantity,\n price: item.price,\n variant_id: item.variant_id,\n product_id: item.product_id,\n sku: item.sku || \"\",\n image: item.image\n }))\n}\n}}",
"checkout_token": "={{ $('Checkout Trigger').item.json.token }}",
"customer_name": "={{\n(() => {\n // 1. FUENTE: Leemos directo del Trigger para evitar datos sucios de nodos intermedios\n const data = $('Checkout Trigger').item.json;\n\n // 2. FUNCI\u00d3N DE EXTRACCI\u00d3N SEGURA\n // Esta funci\u00f3n analiza el objeto y verifica que no sea un Array vac\u00edo [] ni null\n const getNameFrom = (obj) => {\n // Si no existe, o si es un Array (caso del JSON 3), devolvemos null\n if (!obj || Array.isArray(obj)) return null;\n \n // Si existe pero no tiene first_name, devolvemos null\n if (!obj.first_name) return null;\n\n // Si tiene datos, los unimos\n return (obj.first_name + ' ' + (obj.last_name || '')).trim();\n };\n\n // 3. CASCADA DE B\u00daSQUEDA (Prioridad de mejor a peor)\n\n // Intento A: Direcci\u00f3n de Env\u00edo (El m\u00e1s com\u00fan si llenaron datos)\n let finalName = getNameFrom(data.shipping_address);\n\n // Intento B: Direcci\u00f3n de Facturaci\u00f3n (A veces llenan esto primero)\n if (!finalName) finalName = getNameFrom(data.billing_address);\n\n // Intento C: Datos de Cuenta (Solo si es usuario logueado - Caso 1 y 2)\n if (!finalName) finalName = getNameFrom(data.customer);\n\n // Intento D: Direcci\u00f3n guardada en la cuenta\n if (!finalName && data.customer?.default_address) {\n finalName = getNameFrom(data.customer.default_address);\n }\n\n // 4. FALLBACK DE EMAIL (Para el Caso 3 - JSON \"Vac\u00edo\")\n // Si todo lo anterior fall\u00f3, tomamos lo que est\u00e1 antes del @\n // Ejemplo: \"dfrigferio.nazal@gmail.com\" -> \"dfrigferio.nazal\"\n if (!finalName && data.email) {\n finalName = data.email.split('@')[0];\n }\n\n // 5. RED DE SEGURIDAD FINAL\n return finalName || '';\n})()\n}}",
"customer_phone": "={{\n(() => {\n // 1. CASCADA DE PRIORIDAD: Busca el primer valor que no sea nulo ni vac\u00edo\n let phone = $json.shipping_address?.phone \n || $('Checkout Trigger').item.json.billing_address?.phone \n || $('Checkout Trigger').item.json.customer?.phone \n || $('Checkout Trigger').item.json.phone \n || ''; // Fallback vac\u00edo si no hay nada\n\n // 2. LIMPIEZA: Convierte a string y elimina todo lo que NO sea n\u00famero (quita +, espacios, guiones)\n phone = phone.toString().replace(/\\D/g, '');\n\n // 3. VALIDACI\u00d3N: Si qued\u00f3 vac\u00edo, retorna vac\u00edo\n if (!phone) return '';\n\n // 4. FORMATO CHILE: \n // Si ya empieza con 56, lo dejamos tal cual.\n // Si no empieza con 56 (ej: 954220507), le agregamos el 56.\n return phone.startsWith('56') ? phone : '56' + phone;\n})()\n}}",
"customer_email": "={{ $('Checkout Trigger').item.json.email || \"\" }}",
"total_price": "={{ $('Checkout Trigger').item.json.total_price }}",
"updated_at": "={{$('Checkout Trigger').item.json.updated_at ?? DateTime.now().setZone(\"America/Santiago\").toString() }}",
"status": "sent",
"checkout_url": "={{ $('Checkout Trigger').item.json.abandoned_checkout_url }}",
"discount": "={{ $json.discount }}"
},
"matchingColumns": [
"checkout_id"
],
"schema": [
{
"id": "checkout_id",
"displayName": "checkout_id",
"required": true,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "checkout_token",
"displayName": "checkout_token",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "customer_id",
"displayName": "customer_id",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": false
},
{
"id": "customer_name",
"displayName": "customer_name",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "customer_phone",
"displayName": "customer_phone",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "customer_email",
"displayName": "customer_email",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "total_price",
"displayName": "total_price",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": false
},
{
"id": "discount",
"displayName": "discount",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false,
"removed": false
},
{
"id": "checkout_url",
"displayName": "checkout_url",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "line_items",
"displayName": "line_items",
"required": false,
"defaultMatch": false,
"display": true,
"type": "object",
"canBeUsedToMatch": false
},
{
"id": "updated_at",
"displayName": "updated_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": false
},
{
"id": "status",
"displayName": "status",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "recovered",
"displayName": "recovered",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false,
"removed": true
},
{
"id": "created_at",
"displayName": "created_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": false,
"removed": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
1344,
-320
],
"id": "19626075-c5d3-4b76-99bb-0d585713ff22",
"name": "Editar checkout de campa\u00f1a",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "upsert",
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"table": {
"__rl": true,
"value": "dummy_table_checkouts",
"mode": "list",
"cachedResultName": "dummy_table_checkouts"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"checkout_id": "={{ $('Checkout Trigger').item.json.id }}",
"customer_id": "={{ $('Checkout Trigger').item.json.customer?.id }}",
"line_items": "={{\n{\n \"products\": $('Checkout Trigger').item.json.line_items.map(item => ({\n title: item.title,\n quantity: item.quantity,\n price: item.price,\n variant_id: item.variant_id,\n product_id: item.product_id,\n sku: item.sku || \"\",\n image: item.image\n }))\n}\n}}",
"checkout_token": "={{ $('Checkout Trigger').item.json.token }}",
"customer_name": "={{\n(() => {\n // 1. FUENTE: Leemos directo del Trigger para evitar datos sucios de nodos intermedios\n const data = $('Checkout Trigger').item.json;\n\n // 2. FUNCI\u00d3N DE EXTRACCI\u00d3N SEGURA\n // Esta funci\u00f3n analiza el objeto y verifica que no sea un Array vac\u00edo [] ni null\n const getNameFrom = (obj) => {\n // Si no existe, o si es un Array (caso del JSON 3), devolvemos null\n if (!obj || Array.isArray(obj)) return null;\n \n // Si existe pero no tiene first_name, devolvemos null\n if (!obj.first_name) return null;\n\n // Si tiene datos, los unimos\n return (obj.first_name + ' ' + (obj.last_name || '')).trim();\n };\n\n // 3. CASCADA DE B\u00daSQUEDA (Prioridad de mejor a peor)\n\n // Intento A: Direcci\u00f3n de Env\u00edo (El m\u00e1s com\u00fan si llenaron datos)\n let finalName = getNameFrom(data.shipping_address);\n\n // Intento B: Direcci\u00f3n de Facturaci\u00f3n (A veces llenan esto primero)\n if (!finalName) finalName = getNameFrom(data.billing_address);\n\n // Intento C: Datos de Cuenta (Solo si es usuario logueado - Caso 1 y 2)\n if (!finalName) finalName = getNameFrom(data.customer);\n\n // Intento D: Direcci\u00f3n guardada en la cuenta\n if (!finalName && data.customer?.default_address) {\n finalName = getNameFrom(data.customer.default_address);\n }\n\n // 4. FALLBACK DE EMAIL (Para el Caso 3 - JSON \"Vac\u00edo\")\n // Si todo lo anterior fall\u00f3, tomamos lo que est\u00e1 antes del @\n // Ejemplo: \"dfrigferio.nazal@gmail.com\" -> \"dfrigferio.nazal\"\n if (!finalName && data.email) {\n finalName = data.email.split('@')[0];\n }\n\n // 5. RED DE SEGURIDAD FINAL\n return finalName || '';\n})()\n}}",
"customer_phone": "={{\n(() => {\n // 1. CASCADA DE PRIORIDAD: Busca el primer valor que no sea nulo ni vac\u00edo\n let phone = $json.shipping_address?.phone \n || $('Checkout Trigger').item.json.billing_address?.phone \n || $('Checkout Trigger').item.json.customer?.phone \n || $('Checkout Trigger').item.json.phone \n || ''; // Fallback vac\u00edo si no hay nada\n\n // 2. LIMPIEZA: Convierte a string y elimina todo lo que NO sea n\u00famero (quita +, espacios, guiones)\n phone = phone.toString().replace(/\\D/g, '');\n\n // 3. VALIDACI\u00d3N: Si qued\u00f3 vac\u00edo, retorna vac\u00edo\n if (!phone) return '';\n\n // 4. FORMATO CHILE: \n // Si ya empieza con 56, lo dejamos tal cual.\n // Si no empieza con 56 (ej: 954220507), le agregamos el 56.\n return phone.startsWith('56') ? phone : '56' + phone;\n})()\n}}",
"customer_email": "={{ $('Checkout Trigger').item.json.email || \"\" }}",
"total_price": "={{ $('Checkout Trigger').item.json.total_price }}",
"updated_at": "={{$('Checkout Trigger').item.json.updated_at ?? DateTime.now().setZone(\"America/Santiago\").toString() }}",
"status": "={{ \n(() => {\n // 1. Obtener datos hist\u00f3ricos de la hoja\n // Aseg\u00farate de que el nombre del nodo entre comillas sea EXACTO al tuyo\n const rowData = $node[\"Get a row\"].json; \n \n const oldStatus = rowData.status; \n const oldDate = rowData.updated_at; // Fecha de la \u00faltima vez que lo tocamos\n\n // ----------------------------------------------------\n\n // CASO 2: Si la fila no exist\u00eda (es nuevo), empieza el ciclo.\n if (!oldStatus) {\n return 'pending';\n }\n\n // CASO 3: L\u00f3gica Inteligente para 'sent' \n if (oldStatus === 'sent') {\n // Calculamos cu\u00e1nto tiempo pas\u00f3 desde la \u00faltima modificaci\u00f3n registrada\n const lastUpdateDate = new Date(oldDate);\n const now = new Date();\n const diffHours = (now - lastUpdateDate) / (1000 * 60 * 60);\n\n // SI han pasado m\u00e1s de 24 horas:\n // Asumimos que es una nueva sesi\u00f3n de compra abandonada.\n // Lo \"reseteamos\" a pending para enviarle un cup\u00f3n nuevo.\n if (diffHours > 24) {\n return 'pending';\n }\n\n // SI han pasado menos de 24 horas:\n // Asumimos que es el mismo usuario editando el carrito.\n // Lo dejamos en 'sent' para NO spamearlo con otro mensaje igual.\n return 'sent';\n }\n\n // CASO 4: Si estaba 'pending', sigue 'pending' hasta que el Cron Job lo agarre.\n return oldStatus;\n})()\n}}",
"checkout_url": "={{ $('Checkout Trigger').item.json.abandoned_checkout_url }}",
"discount": "="
},
"matchingColumns": [
"checkout_id"
],
"schema": [
{
"id": "checkout_id",
"displayName": "checkout_id",
"required": true,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "checkout_token",
"displayName": "checkout_token",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "customer_id",
"displayName": "customer_id",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": false
},
{
"id": "customer_name",
"displayName": "customer_name",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "customer_phone",
"displayName": "customer_phone",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "customer_email",
"displayName": "customer_email",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "total_price",
"displayName": "total_price",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": false
},
{
"id": "discount",
"displayName": "discount",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false,
"removed": false
},
{
"id": "checkout_url",
"displayName": "checkout_url",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "line_items",
"displayName": "line_items",
"required": false,
"defaultMatch": false,
"display": true,
"type": "object",
"canBeUsedToMatch": false
},
{
"id": "updated_at",
"displayName": "updated_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": false
},
{
"id": "status",
"displayName": "status",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "recovered",
"displayName": "recovered",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false,
"removed": true
},
{
"id": "created_at",
"displayName": "created_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": false,
"removed": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
1408,
-112
],
"id": "9937c838-f348-4505-81e9-0f08f840b06d",
"name": "checkout cacheado",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "upsert",
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"table": {
"__rl": true,
"value": "dummy_table_checkouts",
"mode": "list",
"cachedResultName": "dummy_table_checkouts"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"checkout_id": "={{ $('Checkout Trigger').item.json.id }}",
"customer_id": "={{ $('Checkout Trigger').item.json.customer?.id }}",
"line_items": "={{\n{\n \"products\": $('Checkout Trigger').item.json.line_items.map(item => ({\n title: item.title,\n quantity: item.quantity,\n price: item.price,\n variant_id: item.variant_id,\n product_id: item.product_id,\n sku: item.sku || \"\",\n image: item.image\n }))\n}\n}}",
"checkout_token": "={{ $('Checkout Trigger').item.json.token }}",
"customer_name": "={{\n(() => {\n // 1. FUENTE: Leemos directo del Trigger para evitar datos sucios de nodos intermedios\n const data = $('Checkout Trigger').item.json;\n\n // 2. FUNCI\u00d3N DE EXTRACCI\u00d3N SEGURA\n // Esta funci\u00f3n analiza el objeto y verifica que no sea un Array vac\u00edo [] ni null\n const getNameFrom = (obj) => {\n // Si no existe, o si es un Array (caso del JSON 3), devolvemos null\n if (!obj || Array.isArray(obj)) return null;\n \n // Si existe pero no tiene first_name, devolvemos null\n if (!obj.first_name) return null;\n\n // Si tiene datos, los unimos\n return (obj.first_name + ' ' + (obj.last_name || '')).trim();\n };\n\n // 3. CASCADA DE B\u00daSQUEDA (Prioridad de mejor a peor)\n\n // Intento A: Direcci\u00f3n de Env\u00edo (El m\u00e1s com\u00fan si llenaron datos)\n let finalName = getNameFrom(data.shipping_address);\n\n // Intento B: Direcci\u00f3n de Facturaci\u00f3n (A veces llenan esto primero)\n if (!finalName) finalName = getNameFrom(data.billing_address);\n\n // Intento C: Datos de Cuenta (Solo si es usuario logueado - Caso 1 y 2)\n if (!finalName) finalName = getNameFrom(data.customer);\n\n // Intento D: Direcci\u00f3n guardada en la cuenta\n if (!finalName && data.customer?.default_address) {\n finalName = getNameFrom(data.customer.default_address);\n }\n\n // 4. FALLBACK DE EMAIL (Para el Caso 3 - JSON \"Vac\u00edo\")\n // Si todo lo anterior fall\u00f3, tomamos lo que est\u00e1 antes del @\n // Ejemplo: \"dfrigferio.nazal@gmail.com\" -> \"dfrigferio.nazal\"\n if (!finalName && data.email) {\n finalName = data.email.split('@')[0];\n }\n\n // 5. RED DE SEGURIDAD FINAL\n return finalName || '';\n})()\n}}",
"customer_phone": "={{\n(() => {\n // 1. CASCADA DE PRIORIDAD: Busca el primer valor que no sea nulo ni vac\u00edo\n let phone = $json.shipping_address?.phone \n || $('Checkout Trigger').item.json.billing_address?.phone \n || $('Checkout Trigger').item.json.customer?.phone \n || $('Checkout Trigger').item.json.phone \n || ''; // Fallback vac\u00edo si no hay nada\n\n // 2. LIMPIEZA: Convierte a string y elimina todo lo que NO sea n\u00famero (quita +, espacios, guiones)\n phone = phone.toString().replace(/\\D/g, '');\n\n // 3. VALIDACI\u00d3N: Si qued\u00f3 vac\u00edo, retorna vac\u00edo\n if (!phone) return '';\n\n // 4. FORMATO CHILE: \n // Si ya empieza con 56, lo dejamos tal cual.\n // Si no empieza con 56 (ej: 954220507), le agregamos el 56.\n return phone.startsWith('56') ? phone : '56' + phone;\n})()\n}}",
"customer_email": "={{ $('Checkout Trigger').item.json.email || \"\" }}",
"total_price": "={{ $('Checkout Trigger').item.json.total_price }}",
"updated_at": "={{$('Checkout Trigger').item.json.updated_at ?? DateTime.now().setZone(\"America/Santiago\").toString() }}",
"status": "pending",
"checkout_url": "={{ $('Checkout Trigger').item.json.abandoned_checkout_url }}"
},
"matchingColumns": [
"checkout_id"
],
"schema": [
{
"id": "checkout_id",
"displayName": "checkout_id",
"required": true,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "checkout_token",
"displayName": "checkout_token",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "customer_id",
"displayName": "customer_id",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": false
},
{
"id": "customer_name",
"displayName": "customer_name",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "customer_phone",
"displayName": "customer_phone",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "customer_email",
"displayName": "customer_email",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "total_price",
"displayName": "total_price",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": false
},
{
"id": "discount",
"displayName": "discount",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false,
"removed": true
},
{
"id": "checkout_url",
"displayName": "checkout_url",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "line_items",
"displayName": "line_items",
"required": false,
"defaultMatch": false,
"display": true,
"type": "object",
"canBeUsedToMatch": false
},
{
"id": "updated_at",
"displayName": "updated_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": false
},
{
"id": "status",
"displayName": "status",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "recovered",
"displayName": "recovered",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false,
"removed": true
},
{
"id": "created_at",
"displayName": "created_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": false,
"removed": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
1344,
-496
],
"id": "e30a02ee-8f18-451d-b175-3d60dc83bdd0",
"name": "Checkout Inicial o pending",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "getAll",
"tableId": "dummy_table_checkouts",
"returnAll": true,
"matchType": "allFilters",
"filters": {
"conditions": [
{
"keyName": "status",
"condition": "eq",
"keyValue": "pending"
}
]
}
},
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
80,
272
],
"id": "a39fc618-c505-4951-b40d-f66469f4fc8d",
"name": "Get many rows",
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "update",
"tableId": "dummy_table_checkouts",
"filters": {
"conditions": [
{
"keyName": "checkout_id",
"condition": "eq",
"keyValue": "={{ $('Loop Over Items').item.json.checkout_id }}"
}
]
},
"fieldsUi": {
"fieldValues": [
{
"fieldId": "status",
"fieldValue": "=sent"
},
{
"fieldId": "discount",
"fieldValue": "={{$('Sin customer_id').item.json.data.discountCodeBasicCreate.codeDiscountNode.codeDiscount.codes.nodes[0].code}}"
}
]
}
},
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
1408,
288
],
"id": "2a7408fe-b70e-4511-b26a-05f14db54471",
"name": "Update a row",
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "update",
"tableId": "dummy_table_checkouts",
"filters": {
"conditions": [
{
"keyName": "checkout_id",
"condition": "eq",
"keyValue": "={{ $json.checkout_id }}"
}
]
},
"fieldsUi": {
"fieldValues": [
{
"fieldId": "total_price",
"fieldValue": "={{ $('Orders Trigger').item.json.total_price }}"
},
{
"fieldId": "recovered",
"fieldValue": "0"
},
{
"fieldId": "status",
"fieldValue": "purchased"
}
]
}
},
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
464,
656
],
"id": "6cd85038-646a-4676-9a36-4b2fe049e5f2",
"name": "Update a row1",
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "update",
"tableId": "dummy_table_checkouts",
"filters": {
"conditions": [
{
"keyName": "checkout_id",
"condition": "eq",
"keyValue": "={{ $json.checkout_id }}"
}
]
},
"fieldsUi": {
"fieldValues": [
{
"fieldId": "status",
"fieldValue": "=sent"
},
{
"fieldId": "status",
"fieldValue": "purchased"
},
{
"fieldId": "total_price",
"fieldValue": "={{ $('Orders Trigger').item.json.total_price }}"
},
{
"fieldId": "recovered",
"fieldValue": "1"
}
]
}
},
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
464,
832
],
"id": "4fc6ee83-eca1-4977-ba6c-fb09cef2a592",
"name": "Update a row2",
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "get",
"tableId": "dummy_table_checkouts",
"filters": {
"conditions": [
{
"keyName": "checkout_id",
"keyValue": "={{ $json.checkout_id }}"
}
]
}
},
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
32,
736
],
"id": "c09ba573-16cd-4a63-8c67-8d2dd8a404c4",
"name": "Get a row1",
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"amount": 0.5
},
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [
96,
-384
],
"id": "837a870a-2d05-4989-b052-1c7058ad6e1a",
"name": "Wait"
}
],
"connections": {
"Code in JavaScript": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Loop Over Items": {
"main": [
[],
[
{
"node": "Sin customer_id",
"type": "main",
"index": 0
}
]
]
},
"Sin customer_id": {
"main": [
[
{
"node": "Send template",
"type": "main",
"index": 0
}
]
]
},
"Orders Trigger": {
"main": [
[
{
"node": "Get a row1",
"type": "main",
"index": 0
}
]
]
},
"Checkout Trigger": {
"main": [
[
{
"node": "Wait",
"type": "main",
"index": 0
}
]
]
},
"Switch": {
"main": [
[
{
"node": "Update a row1",
"type": "main",
"index": 0
}
],
[
{
"node": "Update a row2",
"type": "main",
"index": 0
}
]
]
},
"If3": {
"main": [
[
{
"node": "Checkout nuevo - Permalink",
"type": "main",
"index": 0
}
],
[
{
"node": "Checkout Inicial o pending",
"type": "main",
"index": 0
}
]
]
},
"If4": {
"main": [
[
{
"node": "If3",
"type": "main",
"index": 0
}
],
[
{
"node": "If1",
"type": "main",
"index": 0
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.
postgresshopifyAccessTokenApisupabaseApiwhatsAppApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
dummy_client - Shopify abandoned carts. Uses httpRequest, shopifyTrigger, whatsApp, supabase. Event-driven trigger; 25 nodes.
Source: https://github.com/Btike/Cloud_Automations_portfolio/blob/3c10a42a30b099a761b16e494f4fcbc8c9ff401b/flows/shopify_abandoned_carts/Shopify_abandoned_carts.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.
You provide a list of prompts and a system instruction, the workflow batches them into a single OpenAI Batch API request. The batch job is tracked in a Supabase openai_batches table. A cron job polls
This workflow acts as a junior finance research analyst for a UK boutique M&A or corporate finance team. It listens for Slack messages, classifies the request, gathers company or market data, and prod
Agendamiento_v2. Uses n8n-nodes-evolution-api, redis, httpRequest, executeWorkflowTrigger. Event-driven trigger; 59 nodes.
Cancelacion_v2. Uses executeWorkflowTrigger, redis, httpRequest, n8n-nodes-evolution-api. Event-driven trigger; 46 nodes.
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.