{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Scrape Shopify store leads and push verified contacts into HubSpot CRM",
  "tags": [],
  "nodes": [
    {
      "id": "34a65a33-8a1c-46b4-a706-041ed35c9fbb",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2848,
        304
      ],
      "parameters": {
        "width": 480,
        "height": 896,
        "content": "## Scrape Shopify store leads and push verified contacts into HubSpot CRM\n\n### How it works\n\n1. The workflow is triggered manually and configures search parameters (country, lead count, contact types) before initiating a Shopify store scrape via the ScraperCity API.\n2. It polls the scrape job in a loop, waiting 60 seconds between retries, until the job completes and the scraped lead data can be downloaded.\n3. The downloaded CSV is parsed, filtered to records containing emails, and deduplicated to produce a clean lead list.\n4. All collected emails are batched and submitted to ScraperCity's email validation API, with a separate polling loop checking for completion.\n5. Once validation results are downloaded, they are merged back with the original lead records and filtered to retain only verified, valid emails.\n6. Each verified lead is upserted as a contact in HubSpot CRM using the HubSpot node.\n\n### Setup steps\n\n- [ ] Create a ScraperCity account and obtain your API key for the scrape and validation endpoints\n- [ ] Add your ScraperCity API key as an n8n credential and reference it in the HTTP Request nodes\n- [ ] Connect your HubSpot account in n8n via the HubSpot credential (OAuth2 or API key)\n- [ ] Update the 'Configure Search Parameters' node with your desired countryCode, totalLeads, includeEmails, and includePhones values\n- [ ] Review the polling wait times in 'Wait Before First Poll' and 'Wait 60 Seconds Before Retry' to match your expected scrape duration\n- [ ] Map the correct HubSpot contact fields in the 'Create or Update HubSpot Contact' node to match your lead data structure\n\n### Customization\n\nAdjust the countryCode and totalLeads parameters to target different markets or volumes. The polling intervals (60s for scraping, 30s for validation) can be tuned based on typical job completion times. The email filter and deduplication steps can be extended to also filter by phone availability or store category. Additional HubSpot properties (e.g., lead source, country) can be mapped in the final upsert node."
      },
      "typeVersion": 1
    },
    {
      "id": "876619d3-57f8-4c21-ac3e-f8d6f7a1f42b",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3408,
        384
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 320,
        "content": "## Workflow trigger and config\n\nManually triggers the workflow and sets the core search parameters \u2014 country code, lead count, and contact type flags \u2014 that drive the scrape job."
      },
      "typeVersion": 1
    },
    {
      "id": "491bb140-8547-4f25-a7bb-ab55ceeccf4b",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3904,
        416
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 304,
        "content": "## Initiate scrape job\n\nSubmits the scrape request to ScraperCity and stores the returned run ID for subsequent polling."
      },
      "typeVersion": 1
    },
    {
      "id": "1ad0b98d-1271-4625-8354-035a5eb22597",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4400,
        416
      ],
      "parameters": {
        "color": 7,
        "width": 944,
        "height": 496,
        "content": "## Scrape job polling loop\n\nWaits before the first status check, then polls the scrape job status in a loop \u2014 retrying every 60 seconds \u2014 until the scrape is reported as complete."
      },
      "typeVersion": 1
    },
    {
      "id": "2d1f400c-7215-441f-b045-dc16d0d78520",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5408,
        384
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 320,
        "content": "## Download and parse leads\n\nDownloads the completed scrape results as a CSV file and parses it into structured lead records ready for filtering."
      },
      "typeVersion": 1
    },
    {
      "id": "cc80e249-6932-4521-a056-0a0bc493ca76",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5904,
        384
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 320,
        "content": "## Filter and deduplicate leads\n\nRemoves records without email addresses and eliminates duplicate leads to produce a clean, unique list for validation."
      },
      "typeVersion": 1
    },
    {
      "id": "fb4b8957-00cc-4d26-b2c8-bda6cc712bc2",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6400,
        416
      ],
      "parameters": {
        "color": 7,
        "width": 704,
        "height": 304,
        "content": "## Initiate email validation\n\nAggregates all lead emails into a single bulk array and submits them to ScraperCity's email validation API, storing the returned validation run ID."
      },
      "typeVersion": 1
    },
    {
      "id": "899d27df-58c9-44df-a781-524d431ba612",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5408,
        768
      ],
      "parameters": {
        "color": 7,
        "width": 2688,
        "height": 464,
        "content": "## Validation job polling loop\n\nWaits before the first validation status check, then polls in a loop \u2014 retrying every 30 seconds \u2014 until the email validation job is complete."
      },
      "typeVersion": 1
    },
    {
      "id": "db56a23c-30cf-4d1a-a4fd-10242d397d00",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        8160,
        416
      ],
      "parameters": {
        "color": 7,
        "width": 688,
        "height": 304,
        "content": "## Download and merge validation results\n\nDownloads the validation results, merges them back with the original lead records by email, and filters out any leads with invalid or unverified emails."
      },
      "typeVersion": 1
    },
    {
      "id": "4ea0efcd-21c9-42cc-937f-941369629097",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        8912,
        304
      ],
      "parameters": {
        "color": 7,
        "width": 240,
        "height": 400,
        "content": "## Push contacts to HubSpot\n\nUpserts each verified lead as a contact in HubSpot CRM, creating new records or updating existing ones based on email address."
      },
      "typeVersion": 1
    },
    {
      "id": "c6eaf85b-cd1f-4f20-b6f1-2d5f045459f9",
      "name": "When clicking Execute workflow",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        3456,
        544
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "ad359671-3d54-49e1-bfbb-f510dabb699c",
      "name": "Configure Search Parameters",
      "type": "n8n-nodes-base.set",
      "position": [
        3712,
        544
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "cfg-001",
              "name": "countryCode",
              "type": "string",
              "value": "US"
            },
            {
              "id": "cfg-002",
              "name": "totalLeads",
              "type": "number",
              "value": 200
            },
            {
              "id": "cfg-003",
              "name": "includeEmails",
              "type": "boolean",
              "value": true
            },
            {
              "id": "cfg-004",
              "name": "includePhones",
              "type": "boolean",
              "value": true
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "f38ce3ef-03fe-492c-bf39-67b9378b95bc",
      "name": "Start Shopify Store Scrape",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3952,
        544
      ],
      "parameters": {
        "url": "https://app.scrapercity.com/api/v1/scrape/store-leads",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"platform\": \"shopify\",\n  \"countryCode\": \"{{ $json.countryCode }}\",\n  \"emails\": {{ $json.includeEmails }},\n  \"phones\": {{ $json.includePhones }},\n  \"totalLeads\": {{ $json.totalLeads }}\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "typeVersion": 4.2
    },
    {
      "id": "5371c2d4-13da-4fd7-aa10-0e9588a38f75",
      "name": "Store Scrape Run ID",
      "type": "n8n-nodes-base.set",
      "position": [
        4208,
        544
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "runid-001",
              "name": "runId",
              "type": "string",
              "value": "={{ $json.runId }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "54499141-ae82-4735-9fd5-6d0e0e3f94a7",
      "name": "Wait Before First Poll",
      "type": "n8n-nodes-base.wait",
      "position": [
        4448,
        544
      ],
      "parameters": {
        "amount": 60
      },
      "typeVersion": 1.1
    },
    {
      "id": "9b420bbc-b100-4fb6-aa71-c2c22618f6b6",
      "name": "Poll Loop Controller",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        4704,
        544
      ],
      "parameters": {
        "options": {
          "reset": false
        }
      },
      "typeVersion": 3
    },
    {
      "id": "f60b557b-a0fc-448c-9b4a-1743b59b38d1",
      "name": "Check Scrape Status",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        4960,
        544
      ],
      "parameters": {
        "url": "=https://app.scrapercity.com/api/v1/scrape/status/{{ $('Store Scrape Run ID').item.json.runId }}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "typeVersion": 4.2
    },
    {
      "id": "adfb84f3-a7a7-4c79-bf52-d4a34a90544c",
      "name": "Is Scrape Complete?",
      "type": "n8n-nodes-base.if",
      "position": [
        5200,
        544
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-001",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.status }}",
              "rightValue": "SUCCEEDED"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "b9515500-d474-43d3-a88d-af0f37fb22ee",
      "name": "Wait 60 Seconds Before Retry",
      "type": "n8n-nodes-base.wait",
      "position": [
        5200,
        736
      ],
      "parameters": {
        "amount": 60
      },
      "typeVersion": 1.1
    },
    {
      "id": "b08b2d19-14e2-453c-9852-5fbaf456d50d",
      "name": "Download Scraped Store Leads",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        5456,
        544
      ],
      "parameters": {
        "url": "=https://app.scrapercity.com/api/downloads/{{ $('Store Scrape Run ID').item.json.runId }}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "typeVersion": 4.2
    },
    {
      "id": "f487c233-40ca-469b-b504-2a8ae9794c1a",
      "name": "Parse CSV Lead Data",
      "type": "n8n-nodes-base.code",
      "position": [
        5712,
        544
      ],
      "parameters": {
        "jsCode": "// Parse CSV response from ScraperCity store-leads download\n// Handles both JSON array and CSV string responses\n\nconst raw = $input.first().json;\n\n// If the API already returns parsed JSON array\nif (Array.isArray(raw)) {\n  return raw.map(item => ({ json: item }));\n}\n\n// If the response has a data property\nif (raw.data && Array.isArray(raw.data)) {\n  return raw.data.map(item => ({ json: item }));\n}\n\n// If it's a CSV string in the body\nconst csvText = typeof raw === 'string' ? raw : (raw.body || raw.csv || '');\n\nif (!csvText) {\n  return [{ json: { error: 'No parseable data returned', raw: JSON.stringify(raw).slice(0, 200) } }];\n}\n\nconst lines = csvText.trim().split('\\n');\nconst headers = lines[0].split(',').map(h => h.trim().replace(/^\"|\"$/g, ''));\n\nconst results = [];\nfor (let i = 1; i < lines.length; i++) {\n  const line = lines[i];\n  if (!line.trim()) continue;\n  // Basic CSV split (handles quoted fields)\n  const cols = [];\n  let current = '';\n  let inQuotes = false;\n  for (const ch of line) {\n    if (ch === '\"') { inQuotes = !inQuotes; continue; }\n    if (ch === ',' && !inQuotes) { cols.push(current.trim()); current = ''; continue; }\n    current += ch;\n  }\n  cols.push(current.trim());\n  const obj = {};\n  headers.forEach((h, idx) => { obj[h] = cols[idx] || ''; });\n  results.push({ json: obj });\n}\n\nreturn results;"
      },
      "typeVersion": 2
    },
    {
      "id": "1e7d44e3-3337-4e7c-92d9-4d4988f3169f",
      "name": "Filter Records with Emails",
      "type": "n8n-nodes-base.filter",
      "position": [
        5952,
        544
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "filt-001",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.email }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "7fe3e255-5b06-4bc7-93a8-abb3de94d0b3",
      "name": "Remove Duplicate Leads",
      "type": "n8n-nodes-base.removeDuplicates",
      "position": [
        6208,
        544
      ],
      "parameters": {
        "compare": "selectedFields",
        "options": {},
        "fieldsToCompare": {
          "fields": [
            {
              "fieldName": "email"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "c9f8806c-beb2-4899-93d7-d93541e5caac",
      "name": "Collect Emails for Validation",
      "type": "n8n-nodes-base.code",
      "position": [
        6448,
        544
      ],
      "parameters": {
        "jsCode": "// Aggregate all emails into a single array for bulk validation\nconst emails = $input.all().map(item => item.json.email).filter(Boolean);\n\n// Store all original items so we can merge back after validation\nconst leads = $input.all().map(item => item.json);\n\nreturn [{ json: { emails, leads } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "f071514c-848c-43b9-bedc-fe5640ee90f8",
      "name": "Start Email Validation",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        6704,
        544
      ],
      "parameters": {
        "url": "https://app.scrapercity.com/api/v1/scrape/email-validator",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"emails\": {{ JSON.stringify($json.emails) }}\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "typeVersion": 4.2
    },
    {
      "id": "6cc4b9d4-5ad1-4c27-b944-3a486661f02c",
      "name": "Store Validation Run ID",
      "type": "n8n-nodes-base.set",
      "position": [
        6960,
        544
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "vrunid-001",
              "name": "validationRunId",
              "type": "string",
              "value": "={{ $json.runId }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "d7000e9a-944b-4660-aa0d-97f317bf237b",
      "name": "Wait Before Validation Poll",
      "type": "n8n-nodes-base.wait",
      "position": [
        7200,
        880
      ],
      "parameters": {
        "amount": 30
      },
      "typeVersion": 1.1
    },
    {
      "id": "5047f64c-e930-4b90-9f5c-e56c9110e48e",
      "name": "Validation Poll Loop Controller",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        7456,
        880
      ],
      "parameters": {
        "options": {
          "reset": false
        }
      },
      "typeVersion": 3
    },
    {
      "id": "aaa21dec-06b8-40f4-8700-455e466564b6",
      "name": "Check Validation Status",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        7712,
        880
      ],
      "parameters": {
        "url": "=https://app.scrapercity.com/api/v1/scrape/status/{{ $('Store Validation Run ID').item.json.validationRunId }}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "typeVersion": 4.2
    },
    {
      "id": "7875f14a-f583-4b52-8cf0-cc4a3373c24f",
      "name": "Is Validation Complete?",
      "type": "n8n-nodes-base.if",
      "position": [
        7952,
        880
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-002",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.status }}",
              "rightValue": "SUCCEEDED"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "68995fc8-d154-4ff0-b90e-6c9a6b50ae18",
      "name": "Wait 30 Seconds Before Validation Retry",
      "type": "n8n-nodes-base.wait",
      "position": [
        5456,
        1072
      ],
      "parameters": {
        "amount": 30
      },
      "typeVersion": 1.1
    },
    {
      "id": "ba284973-ef0d-4e37-bb6d-4c698f1c07f8",
      "name": "Download Validation Results",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        8208,
        544
      ],
      "parameters": {
        "url": "=https://app.scrapercity.com/api/downloads/{{ $('Store Validation Run ID').item.json.validationRunId }}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "typeVersion": 4.2
    },
    {
      "id": "64c4058c-327d-45fb-b2a6-902332e27bd9",
      "name": "Merge Leads with Validation Results",
      "type": "n8n-nodes-base.code",
      "position": [
        8448,
        544
      ],
      "parameters": {
        "jsCode": "// Merge original lead data with email validation results\n// Validation results come back as array with email + status fields\n\nconst validationRaw = $input.first().json;\n\n// Resolve validation results array\nlet validationResults = [];\nif (Array.isArray(validationRaw)) {\n  validationResults = validationRaw;\n} else if (validationRaw.data && Array.isArray(validationRaw.data)) {\n  validationResults = validationRaw.data;\n} else if (validationRaw.results && Array.isArray(validationRaw.results)) {\n  validationResults = validationRaw.results;\n}\n\n// Build lookup map: email -> validation result\nconst validationMap = {};\nfor (const v of validationResults) {\n  const email = (v.email || v.address || '').toLowerCase().trim();\n  if (email) {\n    validationMap[email] = {\n      emailStatus: v.status || v.email_status || v.result || 'unknown',\n      isCatchAll: v.catch_all || v.is_catch_all || false,\n      mxFound: v.mx_found || v.has_mx || false\n    };\n  }\n}\n\n// Retrieve original leads from the Collect Emails node\nlet leads = [];\ntry {\n  const collectNode = $('Collect Emails for Validation').first();\n  leads = collectNode.json.leads || [];\n} catch(e) {\n  // Fallback: leads array might be empty, continue gracefully\n  leads = [];\n}\n\nif (!leads.length) {\n  // If leads unavailable, just emit validation results directly\n  return validationResults.map(v => ({ json: v }));\n}\n\n// Merge\nconst merged = leads.map(lead => {\n  const email = (lead.email || '').toLowerCase().trim();\n  const validation = validationMap[email] || { emailStatus: 'unknown', isCatchAll: false, mxFound: false };\n  return {\n    json: {\n      ...lead,\n      emailStatus: validation.emailStatus,\n      isCatchAll: validation.isCatchAll,\n      mxFound: validation.mxFound\n    }\n  };\n});\n\nreturn merged;"
      },
      "typeVersion": 2
    },
    {
      "id": "d06ee8f4-10fd-4015-9ad8-fcbb804fb630",
      "name": "Filter Valid Emails Only",
      "type": "n8n-nodes-base.filter",
      "position": [
        8704,
        544
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "filt-002",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.emailStatus }}",
              "rightValue": "valid"
            },
            {
              "id": "filt-003",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.emailStatus }}",
              "rightValue": "catch_all"
            },
            {
              "id": "filt-004",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.emailStatus }}",
              "rightValue": "VALID"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "a5b89a08-b473-4958-a0e8-717b7990f25a",
      "name": "Create or Update HubSpot Contact",
      "type": "n8n-nodes-base.hubspot",
      "position": [
        8960,
        544
      ],
      "parameters": {
        "email": "={{ $json.email }}",
        "options": {},
        "authentication": "appToken",
        "additionalFields": {
          "city": "={{ $json.city || '' }}",
          "country": "={{ $json.country || $json.countryCode || '' }}",
          "message": "=Platform: Shopify\nEmail Status: {{ $json.emailStatus }}\nCatch-All: {{ $json.isCatchAll }}\nMX Found: {{ $json.mxFound }}\nInstagram: {{ $json.instagram || '' }}\nFacebook: {{ $json.facebook || '' }}\nTwitter: {{ $json.twitter || '' }}\nSource: ScraperCity store-leads",
          "firstName": "={{ $json.first_name || $json.firstName || '' }}",
          "websiteUrl": "={{ $json.website || $json.domain || $json.url || '' }}",
          "companyName": "={{ $json.store_name || $json.company || $json.businessName || '' }}"
        }
      },
      "typeVersion": 2
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "",
  "connections": {
    "Check Scrape Status": {
      "main": [
        [
          {
            "node": "Is Scrape Complete?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Scrape Complete?": {
      "main": [
        [
          {
            "node": "Download Scraped Store Leads",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait 60 Seconds Before Retry",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse CSV Lead Data": {
      "main": [
        [
          {
            "node": "Filter Records with Emails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store Scrape Run ID": {
      "main": [
        [
          {
            "node": "Wait Before First Poll",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Poll Loop Controller": {
      "main": [
        [
          {
            "node": "Check Scrape Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Remove Duplicate Leads": {
      "main": [
        [
          {
            "node": "Collect Emails for Validation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Start Email Validation": {
      "main": [
        [
          {
            "node": "Store Validation Run ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait Before First Poll": {
      "main": [
        [
          {
            "node": "Poll Loop Controller",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Validation Status": {
      "main": [
        [
          {
            "node": "Is Validation Complete?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Validation Complete?": {
      "main": [
        [
          {
            "node": "Download Validation Results",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait 30 Seconds Before Validation Retry",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store Validation Run ID": {
      "main": [
        [
          {
            "node": "Wait Before Validation Poll",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Valid Emails Only": {
      "main": [
        [
          {
            "node": "Create or Update HubSpot Contact",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Records with Emails": {
      "main": [
        [
          {
            "node": "Remove Duplicate Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Start Shopify Store Scrape": {
      "main": [
        [
          {
            "node": "Store Scrape Run ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Configure Search Parameters": {
      "main": [
        [
          {
            "node": "Start Shopify Store Scrape",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Validation Results": {
      "main": [
        [
          {
            "node": "Merge Leads with Validation Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait Before Validation Poll": {
      "main": [
        [
          {
            "node": "Validation Poll Loop Controller",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Scraped Store Leads": {
      "main": [
        [
          {
            "node": "Parse CSV Lead Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 60 Seconds Before Retry": {
      "main": [
        [
          {
            "node": "Poll Loop Controller",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Collect Emails for Validation": {
      "main": [
        [
          {
            "node": "Start Email Validation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking Execute workflow": {
      "main": [
        [
          {
            "node": "Configure Search Parameters",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validation Poll Loop Controller": {
      "main": [
        [
          {
            "node": "Check Validation Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Leads with Validation Results": {
      "main": [
        [
          {
            "node": "Filter Valid Emails Only",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 30 Seconds Before Validation Retry": {
      "main": [
        [
          {
            "node": "Validation Poll Loop Controller",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}