AutomationFlowsAI & RAG › Score and Assign Wealth Advisory Leads with Google Sheets, Gmail, and Google…

Score and Assign Wealth Advisory Leads with Google Sheets, Gmail, and Google…

Original n8n title: Score and Assign Wealth Advisory Leads with Google Sheets, Gmail, and Google Gemini

ByWeblineIndia @weblineindia on n8n.io

This workflow captures advisor and lead submissions via webhooks, scores and enriches them with Google Gemini, and stores them in Google Sheets as a lightweight CRM. It auto-assigns leads to available advisors, sends Gmail notifications, runs daily follow-up reminders, and…

Cron / scheduled trigger★★★★★ complexityAI-powered59 nodesGoogle SheetsChain LlmGmailGoogle Gemini Chat
AI & RAG Trigger: Cron / scheduled Nodes: 59 Complexity: ★★★★★ AI nodes: yes Added:

This workflow corresponds to n8n.io template #18085 — we link there as the canonical source.

This workflow follows the Chainllm → Gmail recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "mBDfDFwGwOWas8kN",
  "name": "Webhook Lead Capture to AI-Based Wealth Scoring & CRM Logging (AI + Sheets + Follow-Up System)",
  "tags": [],
  "nodes": [
    {
      "id": "b9ad480d-daef-441c-9fe8-4fbe5297d787",
      "name": "Daily Follow-Up Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -16,
        2224
      ],
      "parameters": {
        "rule": {
          "interval": [
            {}
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "2eec60f2-8aa6-4bc2-9fc0-04e8692c1847",
      "name": "Set Incoming Fields",
      "type": "n8n-nodes-base.set",
      "position": [
        272,
        592
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "name": "record_type",
              "type": "string",
              "value": "={{ ($json.body?.record_type || $json.record_type || '').toString().trim().toLowerCase() }}"
            },
            {
              "name": "source",
              "type": "string",
              "value": "={{ $json.body?.source || $json.body?.lead_source || $json.body?.advisor_source || 'webhook' }}"
            },
            {
              "name": "received_at",
              "type": "string",
              "value": "={{ $now }}"
            },
            {
              "name": "raw_payload",
              "type": "string",
              "value": "={{ JSON.stringify($json.body || $json) }}"
            },
            {
              "name": "advisor_name",
              "type": "string",
              "value": "={{ $json.body?.advisor_name || '' }}"
            },
            {
              "name": "advisor_email",
              "type": "string",
              "value": "={{ $json.body?.advisor_email || '' }}"
            },
            {
              "name": "advisor_phone",
              "type": "string",
              "value": "={{ $json.body?.advisor_phone || '' }}"
            },
            {
              "name": "advisor_type",
              "type": "string",
              "value": "={{ $json.body?.advisor_type || '' }}"
            },
            {
              "name": "specialization",
              "type": "string",
              "value": "={{ Array.isArray($json.body?.specialization) ? $json.body.specialization.join(', ') : ($json.body?.specialization || '') }}"
            },
            {
              "name": "experience_years",
              "type": "string",
              "value": "={{ $json.body?.experience_years || 0 }}"
            },
            {
              "name": "aum_range",
              "type": "string",
              "value": "={{ $json.body?.aum_range || '' }}"
            },
            {
              "name": "client_type",
              "type": "string",
              "value": "={{ $json.body?.client_type || '' }}"
            },
            {
              "name": "city",
              "type": "string",
              "value": "={{ $json.body?.city || '' }}"
            },
            {
              "name": "lead_name",
              "type": "string",
              "value": "={{ $json.body?.lead_name || $json.body?.full_name || '' }}"
            },
            {
              "name": "lead_email",
              "type": "string",
              "value": "={{ $json.body?.lead_email || $json.body?.email || '' }}"
            },
            {
              "name": "lead_phone",
              "type": "string",
              "value": "={{ $json.body?.lead_phone || $json.body?.phone || '' }}"
            },
            {
              "name": "interest_area",
              "type": "string",
              "value": "={{ $json.body?.interest_area || '' }}"
            },
            {
              "name": "budget_range",
              "type": "string",
              "value": "={{ $json.body?.budget_range || '' }}"
            },
            {
              "name": "notes",
              "type": "string",
              "value": "={{ $json.body?.notes || '' }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "9ac03512-544c-4c10-bf65-4bee09e29ea0",
      "name": "Normalize Payload",
      "type": "n8n-nodes-base.code",
      "position": [
        464,
        592
      ],
      "parameters": {
        "jsCode": "const item = $input.first().json;\n\nconst cleanEmail = (v) => (v || '').toString().trim().toLowerCase();\nconst cleanPhone = (v) => (v || '').toString().replace(/\\D/g, '');\nconst cleanText = (v) => (v || '').toString().trim();\n\nconst recordType = cleanText(item.record_type).toLowerCase();\nconst now = new Date().toISOString();\n\nconst out = {\n  ...item,\n  record_type: recordType,\n  source: cleanText(item.source) || 'webhook',\n  city: cleanText(item.city),\n  created_at: item.created_at || now,\n  updated_at: now,\n\n  // define these upfront so n8n type checker does not complain\n  advisor_id: cleanText(item.advisor_id),\n  lead_id: cleanText(item.lead_id),\n\n  // keep as string because incoming schema treats it as string\n  experience_years: cleanText(item.experience_years),\n};\n\nif (recordType === 'advisor') {\n  out.advisor_name = cleanText(item.advisor_name);\n  out.advisor_email = cleanEmail(item.advisor_email);\n  out.advisor_phone = cleanPhone(item.advisor_phone);\n  out.advisor_type = cleanText(item.advisor_type);\n  out.specialization = cleanText(item.specialization);\n  out.experience_years = cleanText(item.experience_years || '0');\n  out.aum_range = cleanText(item.aum_range);\n  out.client_type = cleanText(item.client_type);\n  out.advisor_id = cleanText(item.advisor_id) || `ADV-${Date.now()}`;\n}\n\nif (recordType === 'lead') {\n  out.lead_name = cleanText(item.lead_name);\n  out.lead_email = cleanEmail(item.lead_email);\n  out.lead_phone = cleanPhone(item.lead_phone);\n  out.interest_area = cleanText(item.interest_area);\n  out.budget_range = cleanText(item.budget_range);\n  out.lead_id = cleanText(item.lead_id) || `LED-${Date.now()}`;\n}\n\nreturn [{ json: out }];"
      },
      "typeVersion": 2
    },
    {
      "id": "ea436ca6-9d50-420a-9b9c-de3736772698",
      "name": "Route By Record Type",
      "type": "n8n-nodes-base.switch",
      "position": [
        656,
        592
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "06cdd138-2739-4551-b508-895133b9a583",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.record_type }}",
                    "rightValue": "advisor"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "c9ccf3d4-947a-492e-b741-382e819455fc",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.record_type }}",
                    "rightValue": "lead"
                  }
                ]
              }
            }
          ]
        },
        "options": {},
        "looseTypeValidation": true
      },
      "typeVersion": 3.2
    },
    {
      "id": "3ebd5337-6614-49de-8350-5e703e5f2153",
      "name": "Read Advisors CRM For Advisor",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1216,
        208
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1q31d9V8kC7FRc43n7Q7ST9YunSuQrLZ1DyCIpnLnshM/edit#gid=0",
          "cachedResultName": "Advisors_CRM"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1q31d9V8kC7FRc43n7Q7ST9YunSuQrLZ1DyCIpnLnshM",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1q31d9V8kC7FRc43n7Q7ST9YunSuQrLZ1DyCIpnLnshM/edit?usp=drivesdk",
          "cachedResultName": "Lead & Advisor CRM"
        }
      },
      "typeVersion": 4.5,
      "alwaysOutputData": true
    },
    {
      "id": "9d51d741-cdc1-44df-995e-e48192d7c900",
      "name": "Check Duplicate And Score Advisor",
      "type": "n8n-nodes-base.code",
      "position": [
        1424,
        208
      ],
      "parameters": {
        "jsCode": "const incoming = $('Normalize Payload').first().json;\nconst rows = $input.all().map(i => i.json).filter(r => Object.keys(r || {}).length > 0);\n\nconst norm = (v) => String(v || '').trim().toLowerCase();\nconst toNum = (v, fallback = 0) => {\n  const n = Number(v);\n  return Number.isFinite(n) ? n : fallback;\n};\n\nlet match = null;\nfor (const row of rows) {\n  const rowEmail = norm(row.advisor_email);\n  const rowPhone = String(row.advisor_phone || '').replace(/\\D/g, '');\n  const emailMatch = incoming.advisor_email && rowEmail && norm(incoming.advisor_email) === rowEmail;\n  const phoneMatch = incoming.advisor_phone && rowPhone && String(incoming.advisor_phone || '').replace(/\\D/g, '') === rowPhone;\n  if (emailMatch || phoneMatch) {\n    match = row;\n    break;\n  }\n}\n\nconst advisorType = norm(incoming.advisor_type);\nconst specializationText = norm(incoming.specialization);\nconst clientType = norm(incoming.client_type);\nconst aumRange = norm(incoming.aum_range);\nconst experience = toNum(incoming.experience_years, 0);\n\nconst specializationList = specializationText\n  .split(',')\n  .map(s => s.trim())\n  .filter(Boolean);\n\nlet score = 0;\n\n// Role strength\nif (advisorType.includes('wealth')) score += 18;\nelse if (advisorType.includes('investment')) score += 14;\nelse if (advisorType.includes('mutual')) score += 12;\nelse if (advisorType.includes('tax')) score += 10;\nelse score += 6;\n\n// Specialization breadth and relevance\nscore += Math.min(specializationList.length, 4) * 6;\nif (specializationText.includes('retirement')) score += 8;\nif (specializationText.includes('sip')) score += 6;\nif (specializationText.includes('tax')) score += 6;\nif (specializationText.includes('wealth')) score += 8;\nif (specializationText.includes('portfolio')) score += 6;\n\n// Experience quality\nif (experience >= 3) score += 8;\nif (experience >= 5) score += 8;\nif (experience >= 10) score += 8;\nif (experience >= 15) score += 4;\n\n// Client segment and AUM fit\nif (clientType.includes('hni')) score += 10;\nelse if (clientType.includes('mixed')) score += 8;\nelse if (clientType.includes('retail')) score += 6;\n\nif (aumRange.includes('10cr')) score += 16;\nelse if (aumRange.includes('5cr')) score += 14;\nelse if (aumRange.includes('1cr') || aumRange.includes('crore')) score += 12;\nelse if (aumRange.includes('50l') || aumRange.includes('25l')) score += 8;\n\n// Data quality / operational readiness\nif (incoming.city) score += 4;\nif (incoming.advisor_email) score += 4;\nif (incoming.advisor_phone) score += 4;\nif (incoming.advisor_email && incoming.advisor_phone) score += 4;\n\nscore = Math.max(0, Math.min(100, Math.round(score)));\n\nlet advisor_grade = 'C';\nlet priority = 'Low';\nlet status = 'needs_review';\n\nif (score >= 80) {\n  advisor_grade = 'A';\n  priority = 'High';\n  status = 'high_value';\n} else if (score >= 60) {\n  advisor_grade = 'B';\n  priority = 'Medium';\n  status = 'qualified';\n} else {\n  advisor_grade = 'C';\n  priority = 'Low';\n  status = 'needs_review';\n}\n\nreturn [{\n  json: {\n    ...incoming,\n    is_duplicate: !!match,\n    duplicate_flag: !!match ? 'yes' : 'no',\n    existing_advisor_id: match?.advisor_id || null,\n    existing_created_at: match?.created_at || incoming.created_at || null,\n    active_leads_count: Number(match?.active_leads_count || 0),\n    max_capacity: Number(match?.max_capacity || 10),\n    availability_status: match?.availability_status || 'available',\n    wealth_score: score,\n    advisor_grade,\n    priority,\n    status\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "9c8c5390-fd9b-47d4-8593-7df466f01f9d",
      "name": "AI Summarize Advisor",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        1616,
        176
      ],
      "parameters": {
        "text": "=You are enriching an advisor CRM record for assignment in a wealth advisory workflow.\n\nReturn valid JSON only with exactly these keys:\nadvisor_category, normalized_specializations, client_segment_focus, service_tier, ideal_budget_band, service_regions, intake_quality, ai_summary, next_action\n\nAdvisor data:\n{{ JSON.stringify($json) }}\n\nRules:\n- advisor_category must be one of: Wealth Advisor, Retirement Advisor, Mutual Fund Advisor, Tax Advisor, Investment Advisor, Insurance Advisor, General Advisor\n- normalized_specializations must be an array using only these values when relevant:\n  [\"wealth_management\",\"retirement_planning\",\"sip\",\"mutual_funds\",\"tax_planning\",\"portfolio_management\",\"stock_investing\",\"insurance\",\"goal_planning\",\"estate_planning\"]\n- client_segment_focus must be one of: HNI, Retail, Mixed\n- service_tier must be one of: Premium, Standard, Entry\n- ideal_budget_band must be one of: High, Mid, Standard\n- service_regions must be an array of city/region strings and can include the advisor city\n- intake_quality must be one of: Strong, Moderate, Basic\n- ai_summary must be 2 short sentences\n- next_action must be one clear CRM action\n- infer from the advisor data only\n- return JSON only",
        "batching": {},
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "609c2ee0-24ec-4d4f-9b9b-4ed49f97cae0",
      "name": "Finalize Advisor Record",
      "type": "n8n-nodes-base.code",
      "position": [
        1904,
        208
      ],
      "parameters": {
        "jsCode": "const base = $('Check Duplicate And Score Advisor').first().json;\n\nlet raw =\n  $input.first().json.text ||\n  $input.first().json.response ||\n  $input.first().json.content ||\n  '{}';\n\nif (typeof raw === 'string') {\n  raw = raw.trim();\n  raw = raw.replace(/^```json\\s*/i, '');\n  raw = raw.replace(/^```/, '');\n  raw = raw.replace(/```$/, '');\n  raw = raw.trim();\n}\n\nlet parsed = {};\ntry {\n  parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;\n} catch (e) {\n  parsed = {};\n}\n\nconst norm = (v) => String(v || '').trim();\nconst normLower = (v) => String(v || '').trim().toLowerCase();\nconst uniq = (arr) => [...new Set(arr.filter(Boolean))];\n\nconst keywordMap = [\n  ['retirement', 'retirement_planning'],\n  ['sip', 'sip'],\n  ['mutual', 'mutual_funds'],\n  ['tax', 'tax_planning'],\n  ['wealth', 'wealth_management'],\n  ['portfolio', 'portfolio_management'],\n  ['stock', 'stock_investing'],\n  ['equity', 'stock_investing'],\n  ['insurance', 'insurance'],\n  ['goal', 'goal_planning'],\n  ['estate', 'estate_planning']\n];\n\nconst toSpecializations = (value) => {\n  if (Array.isArray(value)) return uniq(value.map(v => normLower(v)));\n  const text = normLower(value);\n  if (!text) return [];\n  const tags = [];\n  for (const [needle, tag] of keywordMap) {\n    if (text.includes(needle)) tags.push(tag);\n  }\n  return uniq(tags.length ? tags : text.split(',').map(s => s.trim()).filter(Boolean));\n};\n\nconst rawSpecs = toSpecializations(parsed.normalized_specializations);\nconst fallbackSpecs = toSpecializations(base.specialization);\nconst normalizedSpecializations = uniq(rawSpecs.length ? rawSpecs : fallbackSpecs);\n\nconst advisorCategory = norm(parsed.advisor_category) || (\n  normLower(base.advisor_type).includes('retirement') ? 'Retirement Advisor' :\n  normLower(base.advisor_type).includes('mutual') ? 'Mutual Fund Advisor' :\n  normLower(base.advisor_type).includes('tax') ? 'Tax Advisor' :\n  normLower(base.advisor_type).includes('investment') ? 'Investment Advisor' :\n  normLower(base.advisor_type).includes('insurance') ? 'Insurance Advisor' :\n  normLower(base.advisor_type).includes('wealth') ? 'Wealth Advisor' :\n  'General Advisor'\n);\n\nconst clientSegmentFocus = ['HNI', 'Retail', 'Mixed'].includes(parsed.client_segment_focus)\n  ? parsed.client_segment_focus\n  : (normLower(base.client_type).includes('hni') ? 'HNI' : normLower(base.client_type).includes('retail') ? 'Retail' : 'Mixed');\n\nconst serviceTier = ['Premium', 'Standard', 'Entry'].includes(parsed.service_tier)\n  ? parsed.service_tier\n  : (Number(base.wealth_score || 0) >= 80 ? 'Premium' : Number(base.wealth_score || 0) >= 60 ? 'Standard' : 'Entry');\n\nconst idealBudgetBand = ['High', 'Mid', 'Standard'].includes(parsed.ideal_budget_band)\n  ? parsed.ideal_budget_band\n  : (\n      normLower(base.aum_range).includes('10cr') || normLower(base.aum_range).includes('5cr') || normLower(base.client_type).includes('hni')\n        ? 'High'\n        : normLower(base.aum_range).includes('1cr') || normLower(base.aum_range).includes('50l')\n        ? 'Mid'\n        : 'Standard'\n    );\n\nconst serviceRegions = uniq(\n  (Array.isArray(parsed.service_regions) ? parsed.service_regions : [parsed.service_regions, base.city])\n    .map(v => norm(v))\n    .filter(Boolean)\n);\n\nconst profile = {\n  advisor_category: advisorCategory,\n  normalized_specializations: normalizedSpecializations,\n  client_segment_focus: clientSegmentFocus,\n  service_tier: serviceTier,\n  ideal_budget_band: idealBudgetBand,\n  service_regions: serviceRegions,\n  intake_quality: ['Strong', 'Moderate', 'Basic'].includes(parsed.intake_quality) ? parsed.intake_quality : (Number(base.wealth_score || 0) >= 75 ? 'Strong' : Number(base.wealth_score || 0) >= 55 ? 'Moderate' : 'Basic')\n};\n\nconst originalNotes = norm(base.notes);\nconst profileBlock = `PROFILE::${JSON.stringify(profile)}`;\nconst notes = originalNotes\n  ? (originalNotes.includes('PROFILE::') ? originalNotes.replace(/PROFILE::\\{.*\\}$/s, profileBlock) : `${originalNotes}\\n${profileBlock}`)\n  : profileBlock;\n\nreturn [{\n  json: {\n    advisor_id: base.existing_advisor_id || base.advisor_id,\n    advisor_name: base.advisor_name,\n    advisor_email: base.advisor_email,\n    advisor_phone: base.advisor_phone,\n    city: base.city,\n    advisor_type: advisorCategory,\n    specialization: normalizedSpecializations.join(', '),\n    experience_years: Number(base.experience_years || 0),\n    aum_range: base.aum_range,\n    client_type: clientSegmentFocus,\n    wealth_score: Number(base.wealth_score || 0),\n    advisor_grade: base.advisor_grade,\n    priority: base.priority,\n    status: base.status,\n    ai_summary: parsed.ai_summary || 'Advisor profile standardized and ready for lead assignment.',\n    next_action: parsed.next_action || 'Review advisor capacity and activate for matching.',\n    source: base.source,\n    created_at: base.existing_created_at || base.created_at || new Date().toISOString(),\n    updated_at: new Date().toISOString(),\n    duplicate_flag: base.duplicate_flag,\n    notes,\n    active_leads_count: Number(base.active_leads_count || 0),\n    max_capacity: Number(base.max_capacity || 10),\n    availability_status: base.availability_status || 'available'\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "207d7802-9d11-4709-9510-dabc4bc4df51",
      "name": "Save Advisor Record",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2192,
        208
      ],
      "parameters": {
        "columns": {
          "value": {
            "city": "={{ $json.city }}",
            "notes": "={{ $json.notes }}",
            "source": "={{ $json.source }}",
            "status": "={{ $json.status }}",
            "priority": "={{ $json.priority }}",
            "aum_range": "={{ $json.aum_range }}",
            "advisor_id": "={{ $json.advisor_id }}",
            "ai_summary": "={{ $json.ai_summary }}",
            "created_at": "={{ $json.created_at }}",
            "updated_at": "={{ $json.updated_at }}",
            "client_type": "={{ $json.client_type }}",
            "next_action": "={{ $json.next_action }}",
            "advisor_name": "={{ $json.advisor_name }}",
            "advisor_type": "={{ $json.advisor_type }}",
            "max_capacity": "={{ $json.max_capacity }}",
            "wealth_score": "={{ $json.wealth_score }}",
            "advisor_email": "={{ $json.advisor_email }}",
            "advisor_grade": "={{ $json.advisor_grade }}",
            "advisor_phone": "={{ $json.advisor_phone }}",
            "duplicate_flag": "={{ $json.duplicate_flag }}",
            "specialization": "={{ $json.specialization }}",
            "experience_years": "={{ $json.experience_years }}",
            "active_leads_count": "={{ $json.active_leads_count }}",
            "availability_status": "={{ $json.availability_status }}"
          },
          "schema": [
            {
              "id": "advisor_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "advisor_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "advisor_name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "advisor_name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "advisor_email",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "advisor_email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "advisor_phone",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "advisor_phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "city",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "city",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "advisor_type",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "advisor_type",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "specialization",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "specialization",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "experience_years",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "experience_years",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "aum_range",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "aum_range",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "client_type",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "client_type",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "wealth_score",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "wealth_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "advisor_grade",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "advisor_grade",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "priority",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "priority",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ai_summary",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "ai_summary",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "next_action",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "next_action",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "source",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "source",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "created_at",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "created_at",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "updated_at",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "updated_at",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "duplicate_flag",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "duplicate_flag",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "notes",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "notes",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "active_leads_count",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "active_leads_count",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "max_capacity",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "max_capacity",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "availability_status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "availability_status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "advisor_email"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1q31d9V8kC7FRc43n7Q7ST9YunSuQrLZ1DyCIpnLnshM/edit#gid=0",
          "cachedResultName": "Advisors_CRM"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1q31d9V8kC7FRc43n7Q7ST9YunSuQrLZ1DyCIpnLnshM",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1q31d9V8kC7FRc43n7Q7ST9YunSuQrLZ1DyCIpnLnshM/edit?usp=drivesdk",
          "cachedResultName": "Lead & Advisor CRM"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "486bd067-ff42-4ccb-9972-0ffe27460642",
      "name": "Append Advisor Activity Log",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2640,
        208
      ],
      "parameters": {
        "columns": {
          "value": {
            "log_id": "={{ $json.log_id }}",
            "record_id": "={{ $json.record_id }}",
            "timestamp": "={{ $json.timestamp }}",
            "action_type": "={{ $json.action_type }}",
            "record_type": "={{ $json.record_type }}",
            "workflow_name": "={{ $json.workflow_name }}",
            "action_details": "={{ $json.action_details }}"
          },
          "schema": [
            {
              "id": "log_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "log_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "record_type",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "record_type",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "record_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "record_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "action_type",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "action_type",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "action_details",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "action_details",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "workflow_name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "workflow_name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 829720620,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1q31d9V8kC7FRc43n7Q7ST9YunSuQrLZ1DyCIpnLnshM/edit#gid=829720620",
          "cachedResultName": "Activity_Log"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1q31d9V8kC7FRc43n7Q7ST9YunSuQrLZ1DyCIpnLnshM",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1q31d9V8kC7FRc43n7Q7ST9YunSuQrLZ1DyCIpnLnshM/edit?usp=drivesdk",
          "cachedResultName": "Lead & Advisor CRM"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "dfb022d7-e897-4d83-aff4-092c3deda652",
      "name": "Read Leads CRM",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        976,
        800
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 127192940,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1q31d9V8kC7FRc43n7Q7ST9YunSuQrLZ1DyCIpnLnshM/edit#gid=127192940",
          "cachedResultName": "Leads_CRM"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1q31d9V8kC7FRc43n7Q7ST9YunSuQrLZ1DyCIpnLnshM",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1q31d9V8kC7FRc43n7Q7ST9YunSuQrLZ1DyCIpnLnshM/edit?usp=drivesdk",
          "cachedResultName": "Lead & Advisor CRM"
        }
      },
      "typeVersion": 4.5,
      "alwaysOutputData": true
    },
    {
      "id": "22ff3f8b-c711-44b8-b054-6ed7f7f552ae",
      "name": "Check Duplicate And Score Lead",
      "type": "n8n-nodes-base.code",
      "position": [
        1152,
        800
      ],
      "parameters": {
        "jsCode": "const incoming = $('Normalize Payload').first().json;\nconst rows = $input.all().map(i => i.json).filter(r => Object.keys(r || {}).length > 0);\n\nconst norm = (v) => String(v || '').trim().toLowerCase();\n\nlet match = null;\nfor (const row of rows) {\n  const rowEmail = norm(row.lead_email);\n  const rowPhone = String(row.lead_phone || '').replace(/\\D/g, '');\n  const emailMatch = incoming.lead_email && rowEmail && norm(incoming.lead_email) === rowEmail;\n  const phoneMatch = incoming.lead_phone && rowPhone && String(incoming.lead_phone || '').replace(/\\D/g, '') === rowPhone;\n  if (emailMatch || phoneMatch) {\n    match = row;\n    break;\n  }\n}\n\nconst interest = norm(incoming.interest_area);\nconst budget = norm(incoming.budget_range);\nconst source = norm(incoming.source);\n\nlet score = 0;\n\n// Intent richness\nif (interest.includes('retirement')) score += 18;\nif (interest.includes('sip')) score += 12;\nif (interest.includes('wealth')) score += 14;\nif (interest.includes('tax')) score += 10;\nif (interest.includes('portfolio')) score += 10;\nif (interest.includes('investment')) score += 12;\n\n// Buying power / potential\nif (budget.includes('1cr') || budget.includes('crore')) score += 30;\nelse if (budget.includes('50l')) score += 24;\nelse if (budget.includes('25l')) score += 18;\nelse if (budget.includes('10l')) score += 12;\n\n// Data quality\nif (incoming.city) score += 8;\nif (incoming.lead_email) score += 8;\nif (incoming.lead_phone) score += 8;\nif (incoming.lead_email && incoming.lead_phone) score += 6;\n\n// Source quality\nif (source.includes('referral')) score += 14;\nelse if (source.includes('campaign')) score += 10;\nelse if (source.includes('website')) score += 6;\n\nscore = Math.max(0, Math.min(100, Math.round(score)));\n\nlet lead_grade = 'C';\nlet priority = 'Low';\nlet status = 'needs_review';\n\nif (score >= 75) {\n  lead_grade = 'A';\n  priority = 'High';\n  status = 'qualified';\n} else if (score >= 50) {\n  lead_grade = 'B';\n  priority = 'Medium';\n  status = 'qualified';\n}\n\nreturn [{\n  json: {\n    ...incoming,\n    is_duplicate: !!match,\n    duplicate_flag: !!match ? 'yes' : 'no',\n    existing_lead_id: match?.lead_id || null,\n    existing_created_at: match?.created_at || incoming.created_at || null,\n    interaction_count: Number(match?.interaction_count || 0) + 1,\n    lead_score: score,\n    lead_grade,\n    priority,\n    status\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "26b11c16-6549-4c62-9baa-406dfeec22bd",
      "name": "AI Summarize Lead",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        1328,
        800
      ],
      "parameters": {
        "text": "=You are enriching a lead CRM record for a wealth advisory workflow.\n\nReturn valid JSON only with exactly these keys:\nwealth_score, investment_potential, urgency, primary_need, secondary_needs, lead_segment, budget_band, ai_summary, next_action\n\nLead data:\n{{ JSON.stringify($json) }}\n\nRules:\n- wealth_score must be a number from 0 to 100\n- investment_potential must be one of: High, Medium, Low\n- urgency must be one of: High, Medium, Low\n- primary_need must be one of:\n  retirement_planning, wealth_management, sip, mutual_funds, tax_planning, portfolio_management, stock_investing, insurance, goal_planning, general_investment\n- secondary_needs must be an array using only the same allowed values\n- lead_segment must be one of: HNI, Affluent, Retail\n- budget_band must be one of: High, Mid, Standard\n- ai_summary must be 2 short sentences\n- next_action must be one clear CRM action\n- infer from the lead data only\n- return JSON only",
        "batching": {},
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "faa7c462-b3eb-4922-b6b1-aa046c1c43ed",
      "name": "Read Advisors For Assignment",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1616,
        800
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1q31d9V8kC7FRc43n7Q7ST9YunSuQrLZ1DyCIpnLnshM/edit#gid=0",
          "cachedResultName": "Advisors_CRM"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1q31d9V8kC7FRc43n7Q7ST9YunSuQrLZ1DyCIpnLnshM",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1q31d9V8kC7FRc43n7Q7ST9YunSuQrLZ1DyCIpnLnshM/edit?usp=drivesdk",
          "cachedResultName": "Lead & Advisor CRM"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "14d0049c-d334-4917-9c23-ae07e114b963",
      "name": "Assign Advisor Finalize Lead",
      "type": "n8n-nodes-base.code",
      "position": [
        2144,
        832
      ],
      "parameters": {
        "jsCode": "const base = $('Check Duplicate And Score Lead').first().json;\nconst advisors = $('Read Advisors For Assignment').all().map(i => i.json).filter(r => Object.keys(r || {}).length > 0);\nconst aiNode = $('AI Summarize Lead').first().json;\n\nlet raw = aiNode.text || aiNode.response || aiNode.content || '{}';\nif (typeof raw === 'string') {\n  raw = raw.trim();\n  raw = raw.replace(/^```json\\s*/i, '');\n  raw = raw.replace(/^```/, '');\n  raw = raw.replace(/```$/, '');\n  raw = raw.trim();\n}\n\nlet parsed = {};\ntry {\n  parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;\n} catch (e) {\n  parsed = {};\n}\n\nconst norm = (v) => String(v || '').trim().toLowerCase();\nconst toNum = (v, fallback = 0) => {\n  const n = Number(v);\n  return Number.isFinite(n) ? n : fallback;\n};\nconst uniq = (arr) => [...new Set(arr.filter(Boolean))];\n\nconst keywordMap = [\n  ['retirement', 'retirement_planning'],\n  ['sip', 'sip'],\n  ['mutual', 'mutual_funds'],\n  ['tax', 'tax_planning'],\n  ['wealth', 'wealth_management'],\n  ['portfolio', 'portfolio_management'],\n  ['stock', 'stock_investing'],\n  ['equity', 'stock_investing'],\n  ['insurance', 'insurance'],\n  ['goal', 'goal_planning'],\n  ['investment', 'general_investment']\n];\n\nconst normalizeNeed = (value) => {\n  const text = norm(value);\n  if (!text) return [];\n  const tags = [];\n  for (const [needle, tag] of keywordMap) {\n    if (text.includes(needle)) tags.push(tag);\n  }\n  return uniq(tags);\n};\n\nconst parseProfileFromNotes = (notes) => {\n  const text = String(notes || '');\n  const match = text.match(/PROFILE::(\\{.*\\})/s);\n  if (!match) return {};\n  try {\n    return JSON.parse(match[1]);\n  } catch (e) {\n    return {};\n  }\n};\n\nconst primaryNeed = String(parsed.primary_need || '').trim() || (normalizeNeed(base.interest_area)[0] || 'general_investment');\nconst secondaryNeeds = Array.isArray(parsed.secondary_needs) ? parsed.secondary_needs.map(v => String(v).trim()).filter(Boolean) : [];\nconst leadNeeds = uniq([primaryNeed, ...secondaryNeeds, ...normalizeNeed(base.interest_area)]);\nconst leadCity = norm(base.city);\nconst budgetBand = ['High', 'Mid', 'Standard'].includes(parsed.budget_band)\n  ? parsed.budget_band\n  : (\n      norm(base.budget_range).includes('1cr') || norm(base.budget_range).includes('crore')\n        ? 'High'\n        : norm(base.budget_range).includes('50l') || norm(base.budget_range).includes('25l')\n        ? 'Mid'\n        : 'Standard'\n    );\n\nconst leadSegment = ['HNI', 'Affluent', 'Retail'].includes(parsed.lead_segment)\n  ? parsed.lead_segment\n  : (budgetBand === 'High' ? 'HNI' : budgetBand === 'Mid' ? 'Affluent' : 'Retail');\n\nconst allowedStatuses = ['qualified', 'high_value', 'active', 'approved'];\nconst scoredCandidates = [];\nconst fallbackCandidates = [];\n\nfor (const adv of advisors) {\n  const status = norm(adv.status || 'active');\n  const availability = norm(adv.availability_status || 'available');\n  const activeLeads = toNum(adv.active_leads_count, 0);\n  const maxCapacity = Math.max(toNum(adv.max_capacity, 10), 1);\n\n  if (availability !== 'available') continue;\n  if (status && !allowedStatuses.includes(status)) continue;\n  if (activeLeads >= maxCapacity) continue;\n\n  const profile = parseProfileFromNotes(adv.notes);\n  const advisorCategory = String(profile.advisor_category || adv.advisor_type || '').trim();\n  const specializationTags = uniq(\n    Array.isArray(profile.normalized_specializations)\n      ? profile.normalized_specializations.map(v => String(v).trim()).filter(Boolean)\n      : String(adv.specialization || '')\n          .split(',')\n          .map(s => s.trim())\n          .filter(Boolean)\n          .flatMap(normalizeNeed)\n  );\n  const clientSegmentFocus = String(profile.client_segment_focus || adv.client_type || 'Mixed').trim();\n  const serviceTier = String(profile.service_tier || '').trim() || (toNum(adv.wealth_score) >= 80 ? 'Premium' : toNum(adv.wealth_score) >= 60 ? 'Standard' : 'Entry');\n  const idealBudgetBand = String(profile.ideal_budget_band || '').trim() || (clientSegmentFocus === 'HNI' ? 'High' : clientSegmentFocus === 'Mixed' ? 'Mid' : 'Standard');\n  const advisorCity = norm(adv.city);\n  const utilization = activeLeads / maxCapacity;\n  const advisorScore = Math.max(0, Math.min(100, toNum(adv.wealth_score)));\n\n  let semanticScore = 0;\n  const reasons = [];\n  const overlap = leadNeeds.filter(tag => specializationTags.includes(tag));\n\n  if (overlap.length) {\n    semanticScore += overlap.includes(primaryNeed) ? 40 : 0;\n    semanticScore += overlap.filter(tag => tag !== primaryNeed).length * 12;\n    reasons.push(`Need match: ${overlap.join(', ')}`);\n  }\n\n  const categoryNeedMap = {\n    'Wealth Advisor': ['wealth_management', 'general_investment', 'goal_planning', 'portfolio_management'],\n    'Retirement Advisor': ['retirement_planning', 'goal_planning', 'wealth_management'],\n    'Mutual Fund Advisor': ['mutual_funds', 'sip', 'general_investment'],\n    'Tax Advisor': ['tax_planning'],\n    'Investment Advisor': ['stock_investing', 'portfolio_management', 'general_investment'],\n    'Insurance Advisor': ['insurance', 'goal_planning'],\n    'General Advisor': ['general_investment', 'goal_planning']\n  };\n\n  if ((categoryNeedMap[advisorCategory] || []).includes(primaryNeed)) {\n    semanticScore += 18;\n    reasons.push(`Advisor category fit: ${advisorCategory}`);\n  }\n\n  let geographyScore = 0;\n  if (leadCity && advisorCity && leadCity === advisorCity) {\n    geographyScore += 12;\n    reasons.push('City match');\n  } else if (!advisorCity) {\n    geographyScore += 4;\n  }\n\n  let segmentScore = 0;\n  if (leadSegment === 'HNI' && clientSegmentFocus === 'HNI') {\n    segmentScore += 18;\n    reasons.push('HNI segment fit');\n  } else if (leadSegment === 'HNI' && clientSegmentFocus === 'Mixed') {\n    segmentScore += 10;\n  } else if (leadSegment === 'Affluent' && ['Mixed', 'HNI', 'Retail'].includes(clientSegmentFocus)) {\n    segmentScore += clientSegmentFocus === 'Mixed' ? 14 : 10;\n    reasons.push('Affluent segment fit');\n  } else if (leadSegment === 'Retail' && ['Retail', 'Mixed'].includes(clientSegmentFocus)) {\n    segmentScore += clientSegmentFocus === 'Retail' ? 14 : 10;\n    reasons.push('Retail segment fit');\n  }\n\n  let budgetScore = 0;\n  if (budgetBand === idealBudgetBand) {\n    budgetScore += 14;\n    reasons.push(`Budget fit: ${budgetBand}`);\n  } else if (budgetBand === 'High' && serviceTier === 'Premium') {\n    budgetScore += 12;\n  } else if (budgetBand === 'Mid' && ['Premium', 'Standard'].includes(serviceTier)) {\n    budgetScore += 10;\n  } else if (budgetBand === 'Standard') {\n    budgetScore += 8;\n  }\n\n  const experienceScore = Math.min(toNum(adv.experience_years, 0), 15) * 0.5;\n  const qualityScore = advisorScore * 0.08;\n  const loadScore = Math.max(0, (1 - utilization) * 10);\n\n  const totalScore = semanticScore + geographyScore + segmentScore + budgetScore + experienceScore + qualityScore + loadScore;\n\n  const candidate = {\n    advisor: adv,\n    totalScore,\n    semanticScore,\n    reasons,\n    activeLeads,\n    maxCapacity\n  };\n\n  if (semanticScore > 0 || segmentScore >= 10) {\n    scoredCandidates.push(candidate);\n  } else {\n    fallbackCandidates.push(candidate);\n  }\n}\n\nconst orderedCandidates = (scoredCandidates.length ? scoredCandidates : fallbackCandidates)\n  .sort((a, b) => b.totalScore - a.totalScore || a.activeLeads - b.activeLeads || b.advisor.wealth_score - a.advisor.wealth_score);\n\nconst selected = orderedCandidates[0] || null;\nconst best = selected?.advisor || null;\nconst bestScore = selected ? Math.round(selected.totalScore) : 0;\nconst matchReason = selected?.reasons?.length ? selected.reasons.join(' | ') : (best ? 'Assigned using fallback availability and quality rules' : 'No eligible advisor available');\n\nconst followUpDate = new Date();\nconst effectiveUrgency = ['High', 'Medium', 'Low'].includes(parsed.urgency)\n  ? parsed.urgency\n  : (base.priority === 'High' ? 'High' : base.priority === 'Medium' ? 'Medium' : 'Low');\n\nif (effectiveUrgency === 'High') followUpDate.setDate(followUpDate.getDate() + 1);\nelse if (effectiveUrgency === 'Medium') followUpDate.setDate(followUpDate.getDate() + 2);\nelse followUpDate.setDate(followUpDate.getDate() + 4);\n\nconst wealthScore = Math.max(0, Math.min(100, toNum(parsed.wealth_score ?? base.lead_score, 0)));\nconst investmentPotential = ['High', 'Medium', 'Low'].includes(parsed.investment_potential)\n  ? parsed.investment_potential\n  : (base.priority === 'High' ? 'High' : base.priority === 'Medium' ? 'Medium' : 'Low');\n\nreturn [{\n  json: {\n    lead_id: base.existing_lead_id || base.lead_id,\n    lead_name: base.lead_name,\n    lead_email: base.lead_email,\n    lead_phone: base.lead_phone,\n    city: base.city,\n    interest_area: base.interest_area,\n    budget_range: base.budget_range,\n    lead_source: base.source,\n    lead_score: Number(base.lead_score || 0),\n    wealth_score: wealthScore,\n    investment_potential: investmentPotential,\n    lead_grade: base.lead_grade,\n    priority: base.priority,\n    priority_queue_rank: base.priority === 'High' ? 1 : base.priority === 'Medium' ? 2 : 3,\n    status: best ? 'assigned' : base.status,\n    assigned_advisor_id: best?.advisor_id || '',\n    assigned_advisor_name: best?.advisor_name || '',\n    assigned_advisor_email: best?.advisor_email || '',\n    advisor_match_score: bestScore,\n    urgency: effectiveUrgency,\n    intent: primaryNeed,\n    ai_summary: parsed.ai_summary || 'Lead scored successfully and is ready for advisor review.',\n    next_action: parsed.next_action || (best ? 'Assigned to the best-fit available advisor based on need, segment, capacity, and city.' : 'No eligible advisor found. Review manually and assign.'),\n    next_action_date: followUpDate.toISOString().slice(0, 10),\n    follow_up_required: 'yes',\n    last_contacted_at: '',\n    created_at: base.existing_created_at || base.created_at || new Date().toISOString(),\n    updated_at: new Date().toISOString(),\n    duplicate_flag: base.duplicate_flag,\n    notes: [base.notes, `ASSIGNMENT_REASON::${matchReason}`].filter(Boolean).join('\\n'),\n    interaction_count: Number(base.interaction_count || 1),\n    assigned_advisor_reason: matchReason\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "dbb42561-afda-4fb7-98e5-0400adc19f63",
      "name": "Save Lead Record",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2320,
        832
      ],
      "parameters": {
        "columns": {
          "value": {
            "city": "={{ $json.city }}",
            "notes": "={{ $json.notes }}",
            "intent": "={{ $json.intent }}",
            "status": "={{ $json.status }}",
            "lead_id": "={{ $json.lead_id }}",
            "urgency": "={{ $json.urgency }}",
            "priority": "={{ $json.priority }}",
            "lead_name": "={{ $json.lead_name }}",
            "ai_summary": "={{ $json.ai_summary }}",
            "created_at": "={{ $json.created_at }}",
            "lead_email": "={{ $json.lead_email }}",
            "lead_grade": "={{ $json.lead_grade }}",
            "lead_phone": "={{ $json.lead_phone }}",
            "lead_score": "={{ $json.lead_score }}",
            "updated_at": "={{ $json.updated_at }}",
            "lead_source": "={{ $json.lead_source }}",
            "next_action": "={{ $json.next_action }}",
            "budget_range": "={{ $json.budget_range }}",
            "wealth_score": "={{ $json.wealth_score }}",
            "interest_area": "={{ $json.interest_area }}",
            "duplicate_flag": "={{ $json.duplicate_flag }}",
            "next_action_date": "={{ $json.next_action_date }}",
            "interaction_count": "={{ $json.interaction_count }}",
            "last_contacted_at": "={{ $json.last_contacted_at }}",
            "follow_up_required": "={{ $json.follow_up_required }}",
            "advisor_match_score": "={{ $json.advisor_match_score }}",
            "assigned_advisor_id": "={{ $json.assigned_advisor_id }}",
            "priority_queue_rank": "={{ $json.priority_queue_rank }}",
            "investment_potential": "={{ $json.investment_potential }}",
            "assigned_advisor_name": "={{ $json.assigned_advisor_name }}",
            "assigned_advisor_email": "={{ $json.assigned_advisor_email }}"
          },
          "schema": [
            {
              "id": "lead_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "lead_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lead_name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "lead_name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lead_email",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "lead_email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lead_phone",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "lead_phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "city",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "city",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "interest_area",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "interest_area",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "budget_range",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "budget_range",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lead_source",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "lead_source",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lead_score",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "lead_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lead_grade",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "lead_grade",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "priority",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "priority",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "priority_queue_rank",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "priority_queue_rank",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "assigned_advisor_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "assigned_advisor_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "assigned_advisor_name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "assigned_advisor_name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "advisor_match_score",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "advisor_match_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "urgency",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "urgency",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "intent",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "intent",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ai_summary",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "ai_summary",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "next_action",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "next_action",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "next_action_date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "next_action_date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "follow_up_required",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "follow_up_required",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "last_contacted_at",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "last_contacted_at",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "created_at",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "created_at",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "updated_at",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "updated_at",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "duplicate_flag",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "duplicate_flag",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "notes",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "notes",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "interaction_count",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "interaction_count",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "wealth_score",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "wealth_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "investment_potential",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "investment_potential",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "assigned_advisor_email",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "assigned_advisor_email",
              
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow captures advisor and lead submissions via webhooks, scores and enriches them with Google Gemini, and stores them in Google Sheets as a lightweight CRM. It auto-assigns leads to available advisors, sends Gmail notifications, runs daily follow-up reminders, and…

Source: https://n8n.io/workflows/18085/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

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

AI & RAG

This n8n template demonstrates how to build a complete AI-powered outbound email system using Google Sheets, Gmail, Gemini, and website scraping. The workflow is designed to help you move from basic l

Google Sheets, N8N Nodes Puppeteer, Chain Llm +4
AI & RAG

Categories Content Creation AI Automation Publishing Social Media

Google Docs, HTTP Request, Slack +7
AI & RAG

Automatically identifies overdue sales leads and generates personalized follow-up emails using AI. Runs every weekday Reads leads from Google Sheets Filters leads with no contact for 5+ days Downloads

Google Sheets, Chain Llm, Google Gemini Chat +3
AI & RAG

This workflow runs weekly to find cross-sell white space in Salesforce enterprise accounts by comparing Closed Won products to an ERP product catalog, then uses Google Gemini to generate the top oppor

Salesforce, HTTP Request, Google Sheets +3
AI & RAG

Automating YouTube Metadata Ai Agent. Uses lmChatGoogleGemini, chainLlm, agent, gmail. Scheduled trigger; 22 nodes.

Google Gemini Chat, Chain Llm, Agent +3