{
  "id": "nd7DATAOGIH9jAzL",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Google Maps Lead Gen",
  "tags": [],
  "nodes": [
    {
      "id": "e35a6669-bc13-4094-8d7d-7ed1fc3792fe",
      "name": "Split Out",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        -64,
        -336
      ],
      "parameters": {
        "include": "allOtherFields",
        "options": {},
        "fieldToSplitOut": "results"
      },
      "typeVersion": 1
    },
    {
      "id": "c61ec1f0-288e-4f57-8ea2-bb5ca9fd18a9",
      "name": "Aggregate",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        496,
        -384
      ],
      "parameters": {
        "include": "specifiedFields",
        "options": {},
        "aggregate": "aggregateAllItemData"
      },
      "typeVersion": 1
    },
    {
      "id": "78b79be3-6be6-4dbf-aeb0-b6b47331db5d",
      "name": "Forms Trigger",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        -576,
        -416
      ],
      "parameters": {
        "options": {},
        "formTitle": "SDR",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Tipo de Neg\u00f3cio"
            },
            {
              "fieldLabel": "Cidade"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "c3c7a4ff-5c82-45ee-92d7-46540b25b7c4",
      "name": "Format Search Query",
      "type": "n8n-nodes-base.set",
      "position": [
        -400,
        -416
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "8a6cac17-a468-4d0f-bc8d-c14c6094a983",
              "name": "tipoDeNegocio",
              "type": "string",
              "value": "={{ $json[\"Tipo de Neg\u00f3cio\"] }}"
            },
            {
              "id": "8c5d94b6-c6b6-47c4-8e27-99affc159ade",
              "name": "cidade",
              "type": "string",
              "value": "={{ $json.Cidade }}"
            },
            {
              "id": "09df1334-704f-41de-89e0-257e115903b8",
              "name": "api_key",
              "type": "string",
              "value": "YOUR_API_KEY_HERE"
            }
          ]
        }
      },
      "typeVersion": 3.4,
      "alwaysOutputData": true
    },
    {
      "id": "9bc4171b-fe83-40cc-9de1-4d3772d8b7ce",
      "name": "Google Maps Text Search",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "20 leads",
      "position": [
        -192,
        -416
      ],
      "parameters": {
        "url": "https://maps.googleapis.com/maps/api/place/textsearch/json",
        "options": {
          "pagination": {
            "pagination": {
              "parameters": {
                "parameters": [
                  {
                    "name": "pagetoken",
                    "value": "={{ $response.body.next_page_token }}"
                  }
                ]
              },
              "requestInterval": 5000,
              "completeExpression": "={{ !$response.body.next_page_token }}",
              "paginationCompleteWhen": "other"
            }
          }
        },
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "query",
              "value": "={{ $json.tipoDeNegocio }} em {{ $json.cidade }}"
            },
            {
              "name": "key",
              "value": "={{ $('Format Search Query').item.json.api_key }}"
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.3
    },
    {
      "id": "931de6a9-1f41-4a41-942a-64681747bc2c",
      "name": "Fetch Place Details",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        112,
        -336
      ],
      "parameters": {
        "url": "https://maps.googleapis.com/maps/api/place/details/json",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "place_id",
              "value": "={{ $json.results.place_id }}"
            },
            {
              "name": "fields",
              "value": "=name,formatted_phone_number,website"
            },
            {
              "name": "key",
              "value": "={{ $('Format Search Query').item.json.api_key }}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "c4cb172b-e4c2-4944-bbb1-dd6f1a3a7009",
      "name": "Merge Original & Details",
      "type": "n8n-nodes-base.merge",
      "position": [
        320,
        -384
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "271e2365-e5c8-4822-9dd2-38250cba7be6",
      "name": "Data Cleaning Logic",
      "type": "n8n-nodes-base.code",
      "position": [
        640,
        -384
      ],
      "parameters": {
        "jsCode": "const baseRaw = $items('Google Maps Text Search') || [];\n\n// base\nconst base = baseRaw\n  .map(i => i.json?.data || i.json)\n  .flat()\n  .flatMap(d => d.results || []);\n\n// fun\u00e7\u00e3o pra normalizar texto\nconst normalizar = (str) =>\n  (str || '')\n    .toLowerCase()\n    .normalize('NFD')\n    .replace(/[\\u0300-\\u036f]/g, '') // remove acento\n    .replace(/[^a-z0-9]/g, ''); // limpa tudo\n\n// cria mapa com chave composta (nome + endere\u00e7o)\nconst detailsMap = {};\n\n($json.data || []).forEach(d => {\n  const r = d.result || {};\n  if (!r.name) return;\n\n  const chave = normalizar(r.name);\n\n  // guarda se tiver telefone ou site\n  if (!detailsMap[chave] || r.formatted_phone_number || r.website) {\n    detailsMap[chave] = r;\n  }\n});\n\n// merge\nconst resultado = base.map(item => {\n  const chave = normalizar(item.name);\n  const det = detailsMap[chave] || {};\n\n  return {\n    nome: item?.name,\n    tipo: item?.types?.[0],\n    endereco: item?.formatted_address,\n    latitude: item?.geometry?.location?.lat,\n    longitude: item?.geometry?.location?.lng,\n    aberto_agora: item?.opening_hours?.open_now ?? false,\n    status: item?.business_status,\n    avaliacao: item?.rating,\n    total_avaliacoes: item?.user_ratings_total,\n\n    telefone: det?.formatted_phone_number || null,\n    site: det?.website || null\n  };\n});\n\nreturn resultado.map(r => ({ json: r }));"
      },
      "typeVersion": 2
    },
    {
      "id": "51ccbb16-4105-4a2a-9dfa-a213c09527c5",
      "name": "Validate Lead Quality",
      "type": "n8n-nodes-base.if",
      "position": [
        848,
        -384
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "c0e46eb5-5cf6-4d84-b513-38961334cc9c",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $json.telefone }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "a9048e2a-a723-4716-8d37-2c4d9b825ef8",
      "name": "Log Lead to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1072,
        -384
      ],
      "parameters": {
        "columns": {
          "value": {
            "name": "={{ $json.nome }}",
            "site": "={{ $json.site }}",
            "types": "={{ $json.tipo }}",
            "rating": "={{ $json.avaliacao }}",
            "business_status": "={{ $json.status }}",
            "formatted_address": "={{ $json.endereco }}",
            "location.lat / lng": "={{ $json.latitude }},{{ $json.longitude }}",
            "user_ratings_total": "={{ $json.total_avaliacoes }}",
            "formatted_phone_number": "={{ $json.telefone }}",
            "opening_hours.open_now": "={{ $json.aberto_agora }}"
          },
          "schema": [
            {
              "id": "types",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "types",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "formatted_phone_number",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "formatted_phone_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "formatted_address",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "formatted_address",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "location.lat / lng",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "location.lat / lng",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "opening_hours.open_now",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "opening_hours.open_now",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "business_status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "business_status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "rating",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "rating",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "user_ratings_total",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "user_ratings_total",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "site",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "site",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 228523750,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1jDB07Io7dyxGkBbvNbGfyLdpNSxSAx_uVKbdifTc9BA/edit#gid=228523750",
          "cachedResultName": "SRD"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1jDB07Io7dyxGkBbvNbGfyLdpNSxSAx_uVKbdifTc9BA",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1jDB07Io7dyxGkBbvNbGfyLdpNSxSAx_uVKbdifTc9BA/edit?usp=drivesdk",
          "cachedResultName": "base"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "dd11a58f-f69a-4cfe-8114-7d23c3107daa",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -608,
        -464
      ],
      "parameters": {
        "color": 5,
        "width": 352,
        "height": 256,
        "content": "## TRIGGER & INPUT PREP"
      },
      "typeVersion": 1
    },
    {
      "id": "fb0b7f70-8788-408a-bf16-065ec5ca3755",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -240,
        -464
      ],
      "parameters": {
        "width": 496,
        "height": 272,
        "content": "## GOOGLE MAPS ENRICHMENT"
      },
      "typeVersion": 1
    },
    {
      "id": "b3374948-b2a2-43e2-ae93-1b85aa181ae5",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        272,
        -464
      ],
      "parameters": {
        "width": 512,
        "height": 256,
        "content": "## DATA PROCESSING & LOGIC"
      },
      "typeVersion": 1
    },
    {
      "id": "cd48214e-31f9-4999-9f83-5e711a2c2460",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        800,
        -464
      ],
      "parameters": {
        "color": 4,
        "width": 464,
        "height": 256,
        "content": "## VALIDATION & STORAGE"
      },
      "typeVersion": 1
    },
    {
      "id": "53cf972c-96b8-4a2a-8608-e0f8ce32af00",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1072,
        -816
      ],
      "parameters": {
        "width": 432,
        "height": 1008,
        "content": "# SDR Google Maps Lead Pipeline\n\n###  Objective\nThis workflow is a high-efficiency **B2B Lead Generation engine** built for SDR teams. It automates the process of scouting, enriching, and validating business prospects from **Google Maps** based on custom search criteria (Category + City) and stores them in a structured database for outreach.\n\n---\n\n###  How it Works\n\n* **Trigger & Prep**: Starts via an **n8n Form**. A `Set` node cleans the input and prepares the environment variables.\n* **Discovery & Enrichment**: Uses **Google Maps Text Search** with optimized **auto-pagination** (fetching results as long as a `next_page_token` exists).\n* **Granular Fetch**: A `Split Out` node breaks the list into individual items to trigger **Fetch Place Details**, capturing high-value data like `phone_number` and `website`.\n* **Data Intelligence**: A custom **JavaScript Code** node normalizes strings, removes duplicates based on a composite key (Name + Address), and prepares the JSON schema.\n* **Quality Gate**: An `If` node ensures only leads with a **valid phone number** are passed to the final destination.\n\n---\n\n###  Tech Stack\n* **Source**: Google Places API (Text Search & Details).\n* **Engine**: n8n Automation.\n* **Storage**: Google Sheets.\n\n---\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "timezone": "America/Sao_Paulo",
    "callerPolicy": "workflowsFromSameOwner",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "89b5a198-5357-4b54-a0de-e16c57315fa5",
  "connections": {
    "Aggregate": {
      "main": [
        [
          {
            "node": "Data Cleaning Logic",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out": {
      "main": [
        [
          {
            "node": "Fetch Place Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Forms Trigger": {
      "main": [
        [
          {
            "node": "Format Search Query",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Data Cleaning Logic": {
      "main": [
        [
          {
            "node": "Validate Lead Quality",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Place Details": {
      "main": [
        [
          {
            "node": "Merge Original & Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Search Query": {
      "main": [
        [
          {
            "node": "Google Maps Text Search",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Lead Quality": {
      "main": [
        [
          {
            "node": "Log Lead to Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Maps Text Search": {
      "main": [
        [
          {
            "node": "Merge Original & Details",
            "type": "main",
            "index": 0
          },
          {
            "node": "Split Out",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Original & Details": {
      "main": [
        [
          {
            "node": "Aggregate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}