{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "992f8f2a-7ee2-4270-8610-6bfb87b60edf",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -848,
        32
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "fc63a6b3-60a9-4449-9d32-d24b812c2adc",
      "name": "Get Specific Customer Data",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -624,
        48
      ],
      "parameters": {
        "url": "=https://your_shopify_domain/admin/api/2025-07/orders.json?customer_id={{ $json.customer.id }}&status=any",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Shopify-Access-Token",
              "value": "your_shop_access-token"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "f95da48b-e758-46c7-af74-2018af4cac30",
      "name": "Split Out",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        -1040,
        32
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "orders"
      },
      "typeVersion": 1
    },
    {
      "id": "66fa9744-d204-41a9-8b0a-5a623badaaae",
      "name": "Get All Cancelled Order",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1248,
        32
      ],
      "parameters": {
        "url": "https://your_shopify_domain/admin/api/2025-07/orders.json?customer_id=&status=cancelled",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Shopify-Access-Token",
              "value": "your_shop_access-token"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "43fb6336-dbf8-4a88-acfc-59dd3c5812e9",
      "name": "Get Product name with re-order llink",
      "type": "n8n-nodes-base.code",
      "position": [
        -416,
        48
      ],
      "parameters": {
        "jsCode": "const data = items[0].json.orders;\n\nif (!Array.isArray(data)) {\n  throw new Error(\"Orders not found or not an array\");\n}\n\nconst customersMap = {};\n\ndata.forEach((order) => {\n  const customer = order.customer || {};\n  const customerId = customer.id || null;\n  if (!customerId) return;\n\n  if (!customersMap[customerId]) {\n    const phone = customer.phone || (customer.default_address && customer.default_address.phone) || \"N/A\";\n    const name = [customer.first_name, customer.last_name].filter(Boolean).join(\" \") || \"N/A\";\n\n    customersMap[customerId] = {\n      customerId: customerId,\n      name: name,\n      email: customer.email || \"N/A\",\n      phone: phone,\n      totalSpent: 0,\n      products: {},\n      lastOrderLink: order.order_status_url || \"N/A\",\n    };\n  } else {\n    customersMap[customerId].lastOrderLink = order.order_status_url || customersMap[customerId].lastOrderLink;\n  }\n\n  customersMap[customerId].totalSpent += parseFloat(order.current_total_price || \"0\");\n\n  if (Array.isArray(order.line_items)) {\n    order.line_items.forEach((item) => {\n      if (item.name && item.id) {\n        if (!customersMap[customerId].products[item.id]) {\n          customersMap[customerId].products[item.id] = {\n            productName: item.name,\n            quantity: 0,\n          };\n        }\n        customersMap[customerId].products[item.id].quantity += item.quantity || 1;\n      }\n    });\n  }\n});\n\nconst result = Object.values(customersMap).map((customer) => ({\n  json: {\n    customerId: customer.customerId,\n    name: customer.name,\n    email: customer.email,\n    phone: customer.phone,\n    re_order_link: customer.lastOrderLink,\n    totalSpent: customer.totalSpent.toFixed(2),\n    productsBought: Object.entries(customer.products).map(\n      ([productId, productData]) => ({\n        productId: productId,\n        productName: productData.productName,\n        quantity: productData.quantity,\n      }),\n    ),\n  },\n}));\n\nreturn result;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "300043d5-8f43-4818-a590-43421fd8aac8",
      "name": "Get customer info",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -224,
        48
      ],
      "parameters": {
        "url": "=https://your_shopify_domain/admin/api/2025-07/customers/{{ $json.customerId }}.json ",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Shopify-Access-Token",
              "value": "your_shop_access-token"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "dd349ea5-87e6-4286-9733-b6120d35e46d",
      "name": "Get Customer Total Spent",
      "type": "n8n-nodes-base.code",
      "position": [
        -48,
        48
      ],
      "parameters": {
        "jsCode": "const data = items[0].json.customer;\n\nif (!data || typeof data !== \"object\") {\n  throw new Error(\"Customer data not found or invalid format\");\n}\n\nconst customers = Array.isArray(data) ? data : [data];\n\nreturn customers\n  .filter((customer) => {\n    const totalSpent = parseFloat(customer.total_spent || '0.00');\n    return totalSpent > 0;\n  })\n  .map((customer) => {\n    const address = customer.default_address || {};\n\n    return {\n      json: {\n        customerId: customer.id || null,\n        customerName: `${customer.first_name || ''} ${customer.last_name || ''}`.trim(),\n        email: customer.email || 'N/A',\n        phone: customer.phone || address.phone || 'N/A',\n        totalSpent: customer.total_spent || '0.00',\n        ordersCount: customer.orders_count || 0,\n        address: address.address1 || 'N/A',\n        city: address.city || 'N/A',\n        country: address.country || 'N/A',\n        createdAt: customer.created_at || null,\n        updatedAt: customer.updated_at || null,\n        state: customer.state || 'N/A'\n      }\n    };\n  });\n"
      },
      "typeVersion": 2
    },
    {
      "id": "1cd4b46f-8bcb-4524-9d86-b3c822a3fad7",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -1424,
        32
      ],
      "parameters": {
        "rule": {
          "interval": [
            {}
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "029cce6b-37c6-44d7-9340-f8141ad9f549",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "position": [
        464,
        48
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "fd4cdb60-0120-4928-86b2-22d8ca3bea02",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.exists }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "fd47c64f-60d9-4b9f-92e9-9014a2d83b3d",
      "name": "Rapiwa (verify number)",
      "type": "n8n-nodes-rapiwa.rapiwa",
      "position": [
        288,
        48
      ],
      "parameters": {
        "number": "={{ $json.phone }}",
        "operation": "verifyWhatsAppNumber"
      },
      "credentials": {
        "rapiwaApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "8e53fb88-71f4-43ed-845e-08c1fce09bcd",
      "name": "Rapiwa (sent message)",
      "type": "n8n-nodes-rapiwa.rapiwa",
      "position": [
        688,
        -80
      ],
      "parameters": {
        "number": "={{ $json.jid }}",
        "message": "=Hi {{ $('Get Product name with re-order llink').item.json.name }}\n\nWe noticed you cancelled your recent order!\nIf you\u2019d like to try again, you can reorder the same product using the link below:\n\n\ud83d\udc49 {{ $('Get Product name with re-order llink').item.json.re_order_link }}\n\nUse code *REORDER5* to get *5%* off your next purchase \ud83c\udf81\n\nThank you for shopping with us!\n\u2014SpaGreen Creative Team\n"
      },
      "credentials": {
        "rapiwaApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "fac6b716-c4c2-4f8c-afd4-c231bc7acb0e",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        224,
        -320
      ],
      "parameters": {
        "width": 848,
        "height": 640,
        "content": "### 1. Check valid whatsapp number Using Rapiwa\n- Purpose: Verify WhatsApp registration via Rapiwa API.\n### 3. Rapiwa Sender\n- Purpose: Send personalized WhatsApp messages for verified numbers.\n### 4. Store State of Rows in Verified & Sent\n- Document: references a Google spreadsheet ID (example ID in the node configuration).\n### 5. Store State of Rows in Unverified & Not Sent\n- Purpose: Write a row for numbers that are not registered on WhatsApp.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "887aef29-03ac-47c3-bdec-58766185a08e",
      "name": "Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        928,
        144
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "c326b1bf-5eb5-492d-8f6e-aa297c9caf8b",
      "name": "Store State of Rows in Verified & Sent",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        928,
        -80
      ],
      "parameters": {
        "columns": {
          "value": {
            "name": "={{ $('Get Product name with re-order llink').item.json.name }}",
            "coupon": "REORDER5",
            "number": "={{ $('Get Product name with re-order llink').item.json.phone }}",
            "status": "sent",
            "validity": "verified",
            "item link": "={{ $('Get Product name with re-order llink').item.json.re_order_link }}",
            "item name": "={{ $('Get Product name with re-order llink').item.json.productsBought[0].productName }}"
          },
          "schema": [
            {
              "id": "name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "number",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "item name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "item name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "coupon",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "coupon",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "item link",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "item link",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "total price",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "total price",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "validity",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "validity",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nvrxINR5Cch5SChGydf62W6PcDdTmdDnlqBif6iTgLU/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1nvrxINR5Cch5SChGydf62W6PcDdTmdDnlqBif6iTgLU",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nvrxINR5Cch5SChGydf62W6PcDdTmdDnlqBif6iTgLU/edit?usp=drivesdk",
          "cachedResultName": "Shopify - Send WhatsApp upsell recommendation"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "59c8637b-478b-424f-863d-f72ea2fe4193",
      "name": "Store State of Rows in Unverified & Not Sent",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        688,
        144
      ],
      "parameters": {
        "columns": {
          "value": {
            "name": "={{ $('Get Product name with re-order llink').item.json.name }}",
            "coupon": "REORDER5",
            "number": "={{ $('Get Product name with re-order llink').item.json.phone }}",
            "status": "sent",
            "validity": "unverified",
            "item link": "={{ $('Get Product name with re-order llink').item.json.re_order_link }}",
            "item name": "={{ $('Get Product name with re-order llink').item.json.productsBought[0].productName }}"
          },
          "schema": [
            {
              "id": "name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "number",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "item name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "item name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "coupon",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "coupon",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "item link",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "item link",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "total price",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "total price",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "validity",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "validity",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nvrxINR5Cch5SChGydf62W6PcDdTmdDnlqBif6iTgLU/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1nvrxINR5Cch5SChGydf62W6PcDdTmdDnlqBif6iTgLU",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nvrxINR5Cch5SChGydf62W6PcDdTmdDnlqBif6iTgLU/edit?usp=drivesdk",
          "cachedResultName": "Shopify - Send WhatsApp upsell recommendation"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "adb18364-ac4b-406e-a038-388155b29102",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1424,
        320
      ],
      "parameters": {
        "width": 1072,
        "height": 752,
        "content": "# Send WhatsApp Upsell Recommendation Message for Shopify\n## Overview\n- **Fetch cancelled orders** on a schedule\n- **Collect customer and product details**\n- **Verify the customer\u2019s WhatsApp number** using Rapiwa\n- **Send a personalized recovery message** with a discount code\n- **Save on Google sheet** (sent, delivered, not sent, not verified)\n\n\n## Workflow Steps\n### 2. Get All Cancelled Order\n- **Purpose**: Retrieves all cancelled orders from Shopify\n- **API Endpoint**: `https://your_shopify_domain/admin/api/2025-07/orders.json?customer_id=&status=cancelled`\n### 5. Get Specific Customer Data\n- **Purpose**: Retrieves all orders for each customer\n- **API Endpoint**: `https://your_shopify_domain/admin/api/2025-07/orders.json?customer_id={{ $json.customer.id }}&status=any`\n### 6. Get Customer Info\n- **Purpose**: Fetches detailed customer profile information\n- **API Endpoint**: `https://your_shopify_domain/admin/api/2025-07/customers/{{ $json.customerId }}.json`\n\n## Google Sheet Required Columns\n**A Google Sheet formatted like this** \u27a4 [sample](https://docs.google.com/spreadsheets/d/1nvrxINR5Cch5SChGydf62W6PcDdTmdDnlqBif6iTgLU/edit?usp=sharing)\n\n## Useful Links\n- **Install Rapiwa**: [How to install Rapiwa](https://www.npmjs.com/package/n8n-nodes-rapiwa)\n- **Shopify API Documentation**: [https://shopify.dev/docs/admin-api](https://shopify.dev/docs/admin-api)\n- **Rapiwa Dashboard**: [https://app.rapiwa.com](https://app.rapiwa.com/login)\n- **Rapiwa Documentation**: [https://docs.rapiwa.com](https://docs.rapiwa.com/)\n"
      },
      "typeVersion": 1
    },
    {
      "id": "cf075f6e-2fe0-4e07-acfd-d95957d35af7",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1296,
        -112
      ],
      "parameters": {
        "width": 208,
        "height": 304,
        "content": "### **Get All Cancelled Order**\n* **Purpose:** Fetches all cancelled orders from the Shopify store.\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "9e179cf5-7d2b-4809-b4b6-eb6abc94d97e",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -96,
        -80
      ],
      "parameters": {
        "width": 304,
        "height": 272,
        "content": "### **Get Customer Total Spent**\n**Purpose:** Filters out customers who have **spent 0** (keeps only those who made purchases).\n\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "5ae248e2-ef7d-4b0b-8ddd-9b8a5d16e9bc",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -704,
        -144
      ],
      "parameters": {
        "width": 432,
        "height": 336,
        "content": "### 5. **Get Specific Customer Data**\n* **Purpose:** Fetches all orders for a **specific customer** using their `customer_id`.\n\n\n### 6. **Get Product Name with Re-order Link**\n* **Purpose:** classify orders for each customer and extracts useful details.\n"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "If": {
      "main": [
        [
          {
            "node": "Rapiwa (sent message)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Store State of Rows in Unverified & Not Sent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [],
        [
          {
            "node": "Get Specific Customer Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get All Cancelled Order",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get customer info": {
      "main": [
        [
          {
            "node": "Get Customer Total Spent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rapiwa (sent message)": {
      "main": [
        [
          {
            "node": "Store State of Rows in Verified & Sent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rapiwa (verify number)": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get All Cancelled Order": {
      "main": [
        [
          {
            "node": "Split Out",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Customer Total Spent": {
      "main": [
        [
          {
            "node": "Rapiwa (verify number)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Specific Customer Data": {
      "main": [
        [
          {
            "node": "Get Product name with re-order llink",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Product name with re-order llink": {
      "main": [
        [
          {
            "node": "Get customer info",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store State of Rows in Verified & Sent": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store State of Rows in Unverified & Not Sent": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}