AutomationFlowsData & Sheets › Shopify Abandoned Cart WhatsApp Recovery

Shopify Abandoned Cart WhatsApp Recovery

Original n8n title: Dummy_client - Shopify Abandoned Carts

dummy_client - Shopify abandoned carts. Uses httpRequest, shopifyTrigger, whatsApp, supabase. Event-driven trigger; 25 nodes.

Event trigger★★★★☆ complexity25 nodesHTTP RequestShopify TriggerWhatsAppSupabasePostgres
Data & Sheets Trigger: Event Nodes: 25 Complexity: ★★★★☆ Added:
Shopify Abandoned Cart WhatsApp Recovery — n8n workflow card showing HTTP Request, Shopify Trigger, WhatsApp integration

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 →

Download .json
{
  "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.

Pro

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 →

More Data & Sheets workflows → · Browse all categories →

Related workflows

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

Data & Sheets

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

HTTP Request, Supabase, Postgres
Data & Sheets

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

HTTP Request, Google Drive, Google Docs +5
Data & Sheets

Agendamiento_v2. Uses n8n-nodes-evolution-api, redis, httpRequest, executeWorkflowTrigger. Event-driven trigger; 59 nodes.

N8N Nodes Evolution Api, Redis, HTTP Request +3
Data & Sheets

Cancelacion_v2. Uses executeWorkflowTrigger, redis, httpRequest, n8n-nodes-evolution-api. Event-driven trigger; 46 nodes.

Execute Workflow Trigger, Redis, HTTP Request +3
Data & Sheets

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.

HTTP Request, Supabase, Postgres +1