{
  "updatedAt": "2025-12-25T09:49:10.572Z",
  "createdAt": "2025-12-24T16:11:25.919Z",
  "id": "1y6kw0xFymo0y0wu",
  "name": "02_HUNTER",
  "active": false,
  "isArchived": false,
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "part1",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        672,
        64
      ],
      "id": "d1572427-4dd4-4c64-869b-bc4c862d9b23",
      "name": "Webhook"
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        656,
        480
      ],
      "id": "6cd52e52-cb8a-4915-b8a0-a73508e4a591",
      "name": "When clicking \u2018Execute workflow\u2019"
    },
    {
      "parameters": {
        "mode": "raw",
        "jsonOutput": "{\n  \"term\": \"Dentists in Miami\",\n  \"user_id\": \"33333333-3333-3333-3333-333333333333\",\n  \"batch_id\": \"22222222-2222-2222-2222-222222222222\"\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        896,
        272
      ],
      "id": "049d6eb3-8ba2-4f25-baa5-b9fef2793dee",
      "name": "Map Webhook Data1"
    },
    {
      "parameters": {
        "jsCode": "// Get input from Webhook or the previous node\nconst input = $input.first().json;\n\n// Check for required technical IDs\nif (!input.user_id || !input.batch_id) {\n  throw new Error('Missing required fields: user_id and batch_id');\n}\n\n// Extract term and location logic\nlet rawSearch = input.term || \"\"; \nlet term = rawSearch;\nlet location = input.location || \"\";\n\n// Split logic if phrase contains \"in\" or \"at\"\nconst splitMatch = rawSearch.match(/(.+)\\s+(?:in|at)\\s+(.+)/i);\n\nif (splitMatch) {\n    term = splitMatch[1].trim();\n    location = splitMatch[2].trim();\n}\n\nreturn [{\n  json: {\n    term: term,\n    location: location,\n    search_query: `${term} ${location}`.trim(),\n    batch_id: input.batch_id,\n    user_id: input.user_id,\n    started_at: new Date().toISOString()\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1104,
        272
      ],
      "id": "ab6aefa5-ac91-4868-a652-b38ab6c281af",
      "name": "Input Validation1"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.apify.com/v2/acts/nwua9Gu5YrADL7ZDj/runs",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "token",
              "value": "REDACTED_HEADER_VALUE"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"searchStringsArray\": [\n    \"{{ $json.search_query }}\"\n  ],\n  \"maxCrawledPlacesPerSearch\": 5,\n  \"includeWebsites\": true,\n  \"language\": \"en\"\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        1312,
        272
      ],
      "id": "ee87c166-373d-4815-93bd-55c9d556754c",
      "name": "Start Apify Run1"
    },
    {
      "parameters": {
        "jsCode": "const runId = $input.first().json.data.id;\nconst originalInput = $('Input Validation1').item.json;\nconst apifyToken = 'REDACTED_BY_REGEX'; \nconst maxRetries = 20; \nconst pollInterval = 5000; \n\nfor (let i = 0; i < maxRetries; i++) {\n  const response = await this.helpers.httpRequest({\n    method: 'GET',\n    url: `https://api.apify.com/v2/actor-runs/${runId}?token=${apifyToken}`\n  });\n\n  const status = response.data.status;\n\n  if (status === 'SUCCEEDED') {\n    return [{\n      json: {\n        run_id: runId,\n        dataset_id: response.data.defaultDatasetId,\n        status: 'completed',\n        ...originalInput\n      }\n    }];\n  }\n\n  if (status === 'FAILED' || status === 'ABORTED' || status === 'TIMED-OUT') {\n    throw new Error(`Apify Run failed with status: ${status}`);\n  }\n\n  await new Promise(resolve => setTimeout(resolve, pollInterval));\n}\n\nthrow new Error('Apify timed out.');"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1520,
        272
      ],
      "id": "c2aba354-ff9c-4469-bd66-d470fc1ce486",
      "name": "Poll Status1"
    },
    {
      "parameters": {
        "url": "=https://api.apify.com/v2/datasets/{{ $json.dataset_id }}/items",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "token",
              "value": "REDACTED_HEADER_VALUE"
            },
            {
              "name": "clean",
              "value": "true"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        1712,
        272
      ],
      "id": "54373c3f-afcb-46fd-868e-b1ba30beab98",
      "name": "Fetch Results1"
    },
    {
      "parameters": {
        "jsCode": "const inputData = $input.all();\nconst originalInput = $node[\"Input Validation1\"].json;\nconst places = inputData.map(item => item.json);\nconst leads = [];\n\nfor (const place of places) {\n  // Extract Domain\n  let domain = null;\n  if (place.website) {\n    try {\n      const url = new URL(place.website.startsWith('http') ? place.website : 'https://' + place.website);\n      domain = url.hostname.replace(/^www\\./, '');\n    } catch (e) {\n      domain = null; \n    }\n  }\n  \n  // Clean Phone\n  let cleanPhone = place.phoneUnformatted || place.phone || null;\n\n  leads.push({\n    json: {\n        user_id: originalInput.user_id,\n        batch_id: originalInput.batch_id,\n        name: place.title || 'Unknown',\n        domain: domain,\n        phone: cleanPhone,\n        address: place.address || null,\n        \n        // ADDED THESE TWO LINES\n        rating: place.totalScore || 0,        // Maps from Apify totalScore\n        reviews_count: place.reviewsCount || 0, // Maps from Apify reviewsCount\n        \n        category: place.categoryName || 'Dentist',\n        status: 'raw',\n        created_at: new Date().toISOString()\n    }\n  });\n}\n\nreturn leads;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1936,
        272
      ],
      "id": "861e8cee-4d82-465c-9916-307624451be6",
      "name": "Transform & Validate Leads"
    },
    {
      "parameters": {
        "operation": "create",
        "base": {
          "__rl": true,
          "value": "appOiHgTrwpTaLJYN",
          "mode": "list",
          "cachedResultName": "Untitled Base",
          "cachedResultUrl": "https://airtable.com/appOiHgTrwpTaLJYN"
        },
        "table": {
          "__rl": true,
          "value": "tblQwFiY5yufTsiJh",
          "mode": "list",
          "cachedResultName": "Leads",
          "cachedResultUrl": "https://airtable.com/appOiHgTrwpTaLJYN/tblQwFiY5yufTsiJh"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "name": "={{ $json.name }}",
            "batch_id": "={{ $json.batch_id }}",
            "domain": "={{ $json.domain }}",
            "phone": "={{ $json.phone }}",
            "address": "={{ $json.address }}",
            "category": "={{ $json.category }}",
            "status": "={{ $json.status }}",
            "rating": "={{ $json.rating }}",
            "reviews_count": "={{ $json.reviews_count }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "name",
              "displayName": "name",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "batch_id",
              "displayName": "batch_id",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "domain",
              "displayName": "domain",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "phone",
              "displayName": "phone",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "address",
              "displayName": "address",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "rating",
              "displayName": "rating",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "reviews_count",
              "displayName": "reviews_count",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "category",
              "displayName": "category",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "status",
              "displayName": "status",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.airtable",
      "typeVersion": 2.1,
      "position": [
        2144,
        272
      ],
      "id": "ae7d1f82-0434-4724-9908-8035688f3ed9",
      "name": "Create a record",
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// 1. Get all leads processed in this run\nconst insertedLeads = $input.all();\n\n// 2. FIXED: Reference the correct node name 'Input Validation1'\nconst originalInput = $node[\"Input Validation1\"].json;\n\n// 3. Create the summary object for Airtable update\nconst summary = {\n  batch_id: originalInput.batch_id,\n  user_id: originalInput.user_id,\n  total_leads: insertedLeads.length,\n  started_at: originalInput.started_at,\n  completed_at: new Date().toISOString(),\n  search_query: originalInput.search_query,\n  status: 'completed'\n};\n\nreturn [{ json: summary }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2352,
        272
      ],
      "id": "3acb703e-87e0-4dfb-a5f7-8c118d289ec8",
      "name": "Code in JavaScript1"
    },
    {
      "parameters": {
        "operation": "upsert",
        "base": {
          "__rl": true,
          "value": "appOiHgTrwpTaLJYN",
          "mode": "list",
          "cachedResultName": "Untitled Base",
          "cachedResultUrl": "https://airtable.com/appOiHgTrwpTaLJYN"
        },
        "table": {
          "__rl": true,
          "value": "tbljbw1L2Z2zpD02x",
          "mode": "list",
          "cachedResultName": "Batches",
          "cachedResultUrl": "https://airtable.com/appOiHgTrwpTaLJYN/tbljbw1L2Z2zpD02x"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "batch_id": "={{ $json.batch_id }}",
            "user_id": "={{ $json.user_id }}",
            "search_query": "={{ $json.search_query }}",
            "started_at": "={{ $json.started_at }}",
            "total_leads": "={{ $json.total_leads }}",
            "status": "={{ $json.status }}",
            "id": "={{ $json.user_id }}",
            "Leads": "{{ $node[\"Create a record\"].json.id }}"
          },
          "matchingColumns": [
            "id"
          ],
          "schema": [
            {
              "id": "id",
              "displayName": "id",
              "required": false,
              "defaultMatch": true,
              "display": true,
              "type": "string",
              "readOnly": true,
              "removed": false
            },
            {
              "id": "batch_id",
              "displayName": "batch_id",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "user_id",
              "displayName": "user_id",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "search_query",
              "displayName": "search_query",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "status",
              "displayName": "status",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "total_leads",
              "displayName": "total_leads",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "started_at",
              "displayName": "started_at",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "dateTime",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "Leads",
              "displayName": "Leads",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.airtable",
      "typeVersion": 2.1,
      "position": [
        2560,
        272
      ],
      "id": "add72797-fa7e-4b2d-9367-01b95cdb00c0",
      "name": "Create or update a record",
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// 1. Access the data inside the 'fields' object\nconst item = $input.first().json;\nconst data = item.fields || {}; \n\n// 2. Calculate duration using the correct fields\n// Note: If completed_at isn't in 'fields', we use the current time\nconst completedAt = data.completed_at || new Date().toISOString();\nconst startedAt = data.started_at;\n\nconst duration = new Date(completedAt) - new Date(startedAt);\nconst durationSeconds = Math.round(duration / 1000) || 0;\n\n// 3. Return the corrected summary\nreturn [{\n  json: {\n    workflow: '02_HUNTER',\n    status: 'success',\n    batch_id: data.batch_id,\n    total_leads: data.total_leads || 0, // FIXED: Now pulls from fields\n    search_query: data.search_query,\n    duration_seconds: durationSeconds,\n    message: `Successfully found ${data.total_leads || 0} leads in ${durationSeconds} seconds`\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2800,
        272
      ],
      "id": "eb2b8e59-e1a6-4832-a457-72b2cefcf341",
      "name": "Success Message1"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "bb37c2f9-6c87-471e-871c-c1c7a0f24b2c",
              "name": "batch_id",
              "value": "={{ $json.body.batch_id }}",
              "type": "string"
            },
            {
              "id": "1fce52f9-5a3b-41f1-a4b8-388e97f8c331",
              "name": "user_id",
              "value": "={{ $json.body.user_id }}",
              "type": "string"
            },
            {
              "id": "cfd877ea-34fe-43a4-9cd2-e79ca2ea7b85",
              "name": "industry",
              "value": "={{ $json.body.industry }}",
              "type": "string"
            },
            {
              "id": "ff9f7a37-3078-4d81-839d-0b8cd15f01b9",
              "name": "location",
              "value": "={{ $json.body.location }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        896,
        688
      ],
      "id": "9649e381-b6b9-4fd9-a051-c7664ccb6e81",
      "name": "Edit Fields1"
    },
    {
      "parameters": {
        "mode": "raw",
        "jsonOutput": "{\n  \"batch_id\": \"a9f9e451-edeb-4d3e-9f5b-4d8535a8bf76\",\n  \"user_id\": \"f1229eb0-7283-4f13-a917-fe1d41e516fd\",\n  \"industry\": \"Restaurants\",\n  \"location\": \"Casablanca\",\n  \"status\": \"raw\",\n  \"started_at\": \"{{ new Date().toISOString() }}\"\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        896,
        480
      ],
      "id": "63f7853d-61e0-4adb-8ebe-5a66074e4fad",
      "name": "TEST JSON"
    },
    {
      "parameters": {
        "jsCode": "// 1. Get the data from the Webhook 'body'\nconst input = $input.first().json.body || $input.first().json;\n\n// 2. Check for required technical IDs\nif (!input.user_id || !input.batch_id) {\n  throw new Error('Missing required fields: user_id and batch_id');\n}\n\n// 3. Extract industry and location from the new input structure\n// Industry replaces 'term' in your new schema\nlet industry = input.industry || \"\"; \nlet location = input.location || \"\";\n\n// 4. Return the formatted data for the next nodes\nreturn [{\n  json: {\n    term: industry,\n    location: location,\n    // Creates the string for Google Maps search, e.g., \"Restaurants Casablanca\"\n    search_query: `${industry} ${location}`.trim(), \n    batch_id: input.batch_id,\n    user_id: input.user_id,\n    started_at: new Date().toISOString(),\n    status: 'raw' // Helpful for your Airtable status column\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1104,
        480
      ],
      "id": "712803fc-0a8c-4b6e-b429-51a2d7ccb6ea",
      "name": "Code in JavaScript"
    },
    {
      "parameters": {
        "jsCode": "// 1. Get the data from the Webhook 'body'\nconst input = $input.first().json.body || $input.first().json;\n\n// 2. Check for required technical IDs\nif (!input.user_id || !input.batch_id) {\n  throw new Error('Missing required fields: user_id and batch_id');\n}\n\n// 3. Extract industry and location from the new input structure\n// Industry replaces 'term' in your new schema\nlet industry = input.industry || \"\"; \nlet location = input.location || \"\";\n\n// 4. Return the formatted data for the next nodes\nreturn [{\n  json: {\n    term: industry,\n    location: location,\n    // Creates the string for Google Maps search, e.g., \"Restaurants Casablanca\"\n    search_query: `${industry} ${location}`.trim(), \n    batch_id: input.batch_id,\n    user_id: input.user_id,\n    started_at: new Date().toISOString(),\n    status: 'raw' // Helpful for your Airtable status column\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1104,
        688
      ],
      "id": "b26c6616-acdf-448c-8f2d-b8d0ea668cf6",
      "name": "Code in JavaScript2"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.apify.com/v2/acts/nwua9Gu5YrADL7ZDj/runs",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "token",
              "value": "REDACTED_HEADER_VALUE"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"searchStringsArray\": [\n    \"{{ $json.search_query }}\"\n  ],\n  \"includeWebsites\": true,\n  \"language\": \"en\"\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        1344,
        480
      ],
      "id": "2132a935-0f30-48c5-b64b-f063c01fed2d",
      "name": "Start Apify Run"
    },
    {
      "parameters": {
        "jsCode": "// 1. Get the runId from the Apify \"Start Run\" node output\nconst inputJson = $input.first().json;\nconst runId = inputJson.data ? inputJson.data.id : inputJson.id;\n\n// 2. FIXED: Reference the Validation node with a fallback\nlet originalInput;\ntry {\n  originalInput = $node[\"Input Validation1\"].json;\n} catch (e) {\n  // Use fallback data for manual testing purposes\n  originalInput = {\n    batch_id: \"testing-fallback-id\",\n    user_id: \"testing-user-id\",\n    industry: \"Restaurants\",\n    location: \"Casablanca\"\n  };\n}\n\nconst apifyToken = 'REDACTED_BY_REGEX'; \nconst maxRetries = 20; \nconst pollInterval = 5000; \n\nfor (let i = 0; i < maxRetries; i++) {\n  const response = await this.helpers.httpRequest({\n    method: 'GET',\n    url: `https://api.apify.com/v2/actor-runs/${runId}?token=${apifyToken}`\n  });\n\n  const status = response.data.status;\n\n  if (status === 'SUCCEEDED') {\n    return [{\n      json: {\n        run_id: runId,\n        dataset_id: response.data.defaultDatasetId,\n        status: 'completed',\n        ...originalInput // Spreads the industry and location\n      }\n    }];\n  }\n\n  if (status === 'FAILED' || status === 'ABORTED' || status === 'TIMED-OUT') {\n    throw new Error(`Apify Run failed with status: ${status}`);\n  }\n\n  await new Promise(resolve => setTimeout(resolve, pollInterval));\n}\n\nthrow new Error('Apify timed out.');"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1552,
        480
      ],
      "id": "a42f8278-1036-4c44-94fe-1b4f1a5603d0",
      "name": "Poll Status"
    },
    {
      "parameters": {
        "url": "=https://api.apify.com/v2/datasets/{{ $json.dataset_id }}/items",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "token",
              "value": "REDACTED_HEADER_VALUE"
            },
            {
              "name": "clean",
              "value": "true"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        1744,
        480
      ],
      "id": "b6a11b8a-1d86-4c61-bf93-a4cef11985e1",
      "name": "Fetch Results"
    },
    {
      "parameters": {
        "jsCode": "// 1. Get all raw results from the Apify Dataset node\nconst inputData = $input.all();\n\n// 2. FIXED: Reference the Validation node with a fallback to prevent \"Not Executed\" errors\nlet originalInput;\ntry {\n  originalInput = $node[\"Input Validation1\"].json;\n} catch (e) {\n  // Use fallback data for testing if the previous node hasn't run\n  originalInput = {\n    user_id: \"test-user\",\n    batch_id: \"test-batch\",\n    industry: \"Restaurants\" \n  };\n}\n\nconst places = inputData.map(item => item.json);\nconst leads = [];\n\nfor (const place of places) {\n  // Extract Domain\n  let domain = null;\n  if (place.website) {\n    try {\n      const url = new URL(place.website.startsWith('http') ? place.website : 'https://' + place.website);\n      domain = url.hostname.replace(/^www\\./, '');\n    } catch (e) {\n      domain = null; \n    }\n  }\n  \n  // Clean Phone\n  let cleanPhone = place.phoneUnformatted || place.phone || null;\n\n  leads.push({\n    json: {\n        user_id: originalInput.user_id,\n        batch_id: originalInput.batch_id,\n        name: place.title || 'Unknown',\n        domain: domain,\n        phone: cleanPhone,\n        address: place.address || null,\n        \n        // Performance Metrics\n        rating: place.totalScore || 0,\n        reviews_count: place.reviewsCount || 0,\n        \n        // Updated to use the industry from your new webhook\n        category: originalInput.industry || place.categoryName || 'General',\n        \n        status: 'raw',\n        created_at: new Date().toISOString()\n    }\n  });\n}\n\nreturn leads;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1968,
        480
      ],
      "id": "a3608bcf-989b-4b88-a6b3-1436281aa12e",
      "name": "Transform & Validate Leads1"
    },
    {
      "parameters": {
        "operation": "create",
        "base": {
          "__rl": true,
          "value": "appOiHgTrwpTaLJYN",
          "mode": "list",
          "cachedResultName": "Untitled Base",
          "cachedResultUrl": "https://airtable.com/appOiHgTrwpTaLJYN"
        },
        "table": {
          "__rl": true,
          "value": "tblQwFiY5yufTsiJh",
          "mode": "list",
          "cachedResultName": "Leads",
          "cachedResultUrl": "https://airtable.com/appOiHgTrwpTaLJYN/tblQwFiY5yufTsiJh"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "name": "={{ $json.name }}",
            "batch_id": "={{ $json.batch_id }}",
            "domain": "={{ $json.domain }}",
            "phone": "={{ $json.phone }}",
            "address": "={{ $json.address }}",
            "category": "={{ $json.category }}",
            "status": "={{ $json.status }}",
            "rating": "={{ $json.rating }}",
            "reviews_count": "={{ $json.reviews_count }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "name",
              "displayName": "name",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "batch_id",
              "displayName": "batch_id",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "domain",
              "displayName": "domain",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "phone",
              "displayName": "phone",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "address",
              "displayName": "address",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "rating",
              "displayName": "rating",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "reviews_count",
              "displayName": "reviews_count",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "category",
              "displayName": "category",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "status",
              "displayName": "status",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.airtable",
      "typeVersion": 2.1,
      "position": [
        2176,
        480
      ],
      "id": "9dabbe42-9a5f-47c3-a5b6-b9d62679608c",
      "name": "Create a record1",
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// 1. Get all leads processed in this run\nconst insertedLeads = $input.all();\n\n// 2. FIXED: Reference the correct node name with a fallback\nlet originalInput;\ntry {\n  originalInput = $node[\"Input Validation1\"].json;\n} catch (e) {\n  // Fallback for testing individual nodes\n  originalInput = {\n    batch_id: \"test-batch\",\n    user_id: \"test-user\",\n    started_at: new Date().toISOString(),\n    search_query: \"Restaurants Casablanca\"\n  };\n}\n\n// 3. Create the summary object for Airtable update\nconst summary = {\n  batch_id: originalInput.batch_id,\n  user_id: originalInput.user_id,\n  total_leads: insertedLeads.length, // Counts the found restaurants\n  started_at: originalInput.started_at,\n  completed_at: new Date().toISOString(),\n  search_query: originalInput.search_query,\n  status: 'completed'\n};\n\n// 4. Return as a single item for the final Airtable Update node\nreturn [{ json: summary }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2384,
        480
      ],
      "id": "9d7ba053-a0b3-449f-8210-28c8beeafcf5",
      "name": "Code in JavaScript3"
    },
    {
      "parameters": {
        "operation": "upsert",
        "base": {
          "__rl": true,
          "value": "appOiHgTrwpTaLJYN",
          "mode": "list",
          "cachedResultName": "Untitled Base",
          "cachedResultUrl": "https://airtable.com/appOiHgTrwpTaLJYN"
        },
        "table": {
          "__rl": true,
          "value": "tbljbw1L2Z2zpD02x",
          "mode": "list",
          "cachedResultName": "Batches",
          "cachedResultUrl": "https://airtable.com/appOiHgTrwpTaLJYN/tbljbw1L2Z2zpD02x"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "batch_id": "={{ $json.batch_id }}",
            "user_id": "={{ $json.user_id }}",
            "search_query": "={{ $json.search_query }}",
            "started_at": "={{ $json.started_at }}",
            "total_leads": "={{ $json.total_leads }}",
            "status": "={{ $json.status }}",
            "id": "={{ $json.user_id }}",
            "Leads": "{{ $node[\"Create a record\"].json.id }}"
          },
          "matchingColumns": [
            "id"
          ],
          "schema": [
            {
              "id": "id",
              "displayName": "id",
              "required": false,
              "defaultMatch": true,
              "display": true,
              "type": "string",
              "readOnly": true,
              "removed": false
            },
            {
              "id": "batch_id",
              "displayName": "batch_id",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "user_id",
              "displayName": "user_id",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "search_query",
              "displayName": "search_query",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "status",
              "displayName": "status",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "total_leads",
              "displayName": "total_leads",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "started_at",
              "displayName": "started_at",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "dateTime",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "Leads",
              "displayName": "Leads",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.airtable",
      "typeVersion": 2.1,
      "position": [
        2592,
        480
      ],
      "id": "8a90db5c-9e5a-45ca-b915-e41749c30d80",
      "name": "Create or update a record1",
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// 1. Access the data inside the 'fields' object\nconst item = $input.first().json;\nconst data = item.fields || {}; \n\n// 2. Calculate duration using the correct fields\n// Note: If completed_at isn't in 'fields', we use the current time\nconst completedAt = data.completed_at || new Date().toISOString();\nconst startedAt = data.started_at;\n\nconst duration = new Date(completedAt) - new Date(startedAt);\nconst durationSeconds = Math.round(duration / 1000) || 0;\n\n// 3. Return the corrected summary\nreturn [{\n  json: {\n    workflow: '02_HUNTER',\n    status: 'success',\n    batch_id: data.batch_id,\n    total_leads: data.total_leads || 0, // FIXED: Now pulls from fields\n    search_query: data.search_query,\n    duration_seconds: durationSeconds,\n    message: `Successfully found ${data.total_leads || 0} leads in ${durationSeconds} seconds`\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2832,
        480
      ],
      "id": "f86945c7-89c6-4715-b516-995286cff7f3",
      "name": "Success Message"
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Map Webhook Data1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "Map Webhook Data1",
            "type": "main",
            "index": 0
          },
          {
            "node": "TEST JSON",
            "type": "main",
            "index": 0
          },
          {
            "node": "Edit Fields1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Map Webhook Data1": {
      "main": [
        [
          {
            "node": "Input Validation1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Input Validation1": {
      "main": [
        [
          {
            "node": "Start Apify Run1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Start Apify Run1": {
      "main": [
        [
          {
            "node": "Poll Status1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Poll Status1": {
      "main": [
        [
          {
            "node": "Fetch Results1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Results1": {
      "main": [
        [
          {
            "node": "Transform & Validate Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Transform & Validate Leads": {
      "main": [
        [
          {
            "node": "Create a record",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create a record": {
      "main": [
        [
          {
            "node": "Code in JavaScript1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript1": {
      "main": [
        [
          {
            "node": "Create or update a record",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create or update a record": {
      "main": [
        [
          {
            "node": "Success Message1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "TEST JSON": {
      "main": [
        [
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields1": {
      "main": [
        [
          {
            "node": "Code in JavaScript2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Start Apify Run": {
      "main": [
        [
          {
            "node": "Poll Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Poll Status": {
      "main": [
        [
          {
            "node": "Fetch Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Results": {
      "main": [
        [
          {
            "node": "Transform & Validate Leads1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Transform & Validate Leads1": {
      "main": [
        [
          {
            "node": "Create a record1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create a record1": {
      "main": [
        [
          {
            "node": "Code in JavaScript3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript3": {
      "main": [
        [
          {
            "node": "Create or update a record1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create or update a record1": {
      "main": [
        [
          {
            "node": "Success Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript": {
      "main": [
        [
          {
            "node": "Start Apify Run",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null,
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "versionId": "c5272d17-4257-4717-9a66-38141f2cc23e",
  "activeVersionId": null,
  "triggerCount": 0,
  "shared": [
    {
      "updatedAt": "2025-12-24T16:11:25.942Z",
      "createdAt": "2025-12-24T16:11:25.942Z",
      "role": "workflow:owner",
      "workflowId": "1y6kw0xFymo0y0wu",
      "projectId": "HHopAZ4lOFgjhBzT"
    }
  ],
  "activeVersion": null,
  "tags": []
}