{
  "nodes": [
    {
      "id": "33341b07-0972-4005-abb9-cff3c6d1284c",
      "name": "Daily Prospect Research",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        496,
        944
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "9710e056-4917-4ec9-8f7e-e847c666f9e9",
      "name": "Read Upcoming Calls Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "onError": "continueRegularOutput",
      "position": [
        752,
        944
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "id",
          "value": "upcoming_calls"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_SPREADSHEET_ID"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "d071b882-237d-460e-b54e-1a4e983b3861",
      "name": "Scrape LinkedIn Profile",
      "type": "n8n-nodes-base.httpRequest",
      "maxTries": 3,
      "position": [
        1024,
        944
      ],
      "parameters": {
        "url": "=https://api.brightdata.com/datasets/v3/scrape?dataset_id=gd_l1viktl72bvl7bjuj0&format=json",
        "method": "POST",
        "options": {
          "timeout": 90000
        },
        "jsonBody": "={{ JSON.stringify({ input: [{ url: $json.url }] }) }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "waitBetweenTries": 5000
    },
    {
      "id": "28ded147-d306-4e78-9ed5-191e05fa1a2d",
      "name": "Validate BD Response",
      "type": "n8n-nodes-base.code",
      "position": [
        1280,
        944
      ],
      "parameters": {
        "jsCode": "const input = $input.first().json;\n\n// Handle array response (synchronous success)\nif (Array.isArray(input)) {\n  if (input.length === 0) {\n    return [{ json: { error: 'Bright Data returned empty results', status: 'no_data' } }];\n  }\n  return input.map(item => ({ json: { ...item, status: 'ok' } }));\n}\n\n// Handle object response\nif (input.snapshot_id) {\n  return [{ json: { error: 'Async response - snapshot not ready', snapshot_id: input.snapshot_id, status: 'async_pending' } }];\n}\n\nif (input.error || input.message) {\n  return [{ json: { error: input.error || input.message, status: 'api_error' } }];\n}\n\n// Pass through valid single-object response\nreturn [{ json: { ...input, status: 'ok' } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "cca84cfe-122d-44d5-b847-239f5c607092",
      "name": "Build Call Brief",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "onError": "continueErrorOutput",
      "position": [
        2064,
        944
      ],
      "parameters": {
        "text": "=Build a pre-call intelligence brief for this prospect. Our product: {{ $json.our_product }}. Meeting date: {{ $json.meeting_date }}\n\n{{ JSON.stringify($json) }}",
        "options": {
          "systemMessage": "You are a sales intelligence analyst. You receive scraped LinkedIn profile data for an upcoming sales call prospect.\n\nYour job is to build a comprehensive pre-call intelligence brief.\n\nAnalyze the profile and produce:\n- career_trajectory: string, summarize their career path and what it tells you about their priorities.\n- recent_activity: string, summarize their recent LinkedIn activity (posts, comments, shares).\n- talking_points: array of exactly 5 strings, specific conversation starters based on their profile.\n  Each should reference something concrete from their profile or activity.\n- potential_pain_points: array of strings, likely business challenges based on their role, company, and industry.\n- readiness_score: integer 0-100, how ready this prospect is for a productive sales conversation.\n  - 80-100: Strong buying signals, active pain points, clear decision-making authority.\n  - 60-80: Good fit, some engagement signals, likely has budget authority.\n  - 40-60: Moderate fit, unclear signals, may need nurturing.\n  - 0-40: Weak fit, minimal engagement, possibly wrong contact.\n\nAlso include:\n- prospect_name: string\n- current_role: string\n- company: string\n- connection_points: string, any mutual connections, shared interests, or common ground.\n- recommended_approach: string, specific strategy for this call.\n- risk_factors: string, things that could derail the conversation.\n\nCRITICAL OUTPUT RULES:\n- Return ONLY a raw JSON object. Nothing else.\n- No markdown. No code fences. No explanations before or after.\n\nSELF-EVALUATION (mandatory):\nInclude an \"eval\" object in your response with:\n- confidence: float 0.0-1.0, your confidence in the analysis quality\n- reasoning: 1 sentence explaining your confidence level\n- data_quality: \"high\", \"medium\", or \"low\" based on input completeness\n- evidence_count: integer, number of data points you based the analysis on"
        },
        "promptType": "define"
      },
      "typeVersion": 3
    },
    {
      "id": "05a921a3-979f-40a2-abd3-cc22ab392c1e",
      "name": "GPT-5.5 Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        2064,
        1184
      ],
      "parameters": {
        "model": "gpt-5.5",
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "51c8d253-573c-4a55-8ce2-ba5a9a6662eb",
      "name": "Parse AI Output",
      "type": "n8n-nodes-base.code",
      "position": [
        2320,
        944
      ],
      "parameters": {
        "jsCode": "const raw = $input.first().json.output || $input.first().json.text || '';\nconst clean = raw.replace(/```json\\n?/g, '').replace(/```\\n?/g, '').trim();\n\nlet parsed;\ntry {\n  parsed = JSON.parse(clean);\n} catch (e) {\n  parsed = {\n    error: 'Failed to parse AI response',\n    parse_error: e.message,\n    raw_preview: raw.substring(0, 200)\n  };\n}\nconst original = $(\"Read Upcoming Calls Sheet\").first().json;\n\nreturn [{ json: { ...original, ...parsed, processed_at: new Date().toISOString() } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "bf13e930-e188-4bbd-9d42-5964eaf66868",
      "name": "IF Confidence >= 0.7",
      "type": "n8n-nodes-base.if",
      "onError": "continueErrorOutput",
      "position": [
        2576,
        944
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "8807c067-d1cc-484b-9448-da98606ac280",
              "operator": {
                "type": "number",
                "operation": "gte"
              },
              "leftValue": "={{ $json.eval.confidence }}",
              "rightValue": 0.7
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "ca395b2b-a283-4db3-ba45-84f9ac983e4b",
      "name": "IF Readiness Score >= 70",
      "type": "n8n-nodes-base.if",
      "onError": "continueErrorOutput",
      "position": [
        2832,
        816
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "26a6177f-03be-4ce5-8290-24f05a916033",
              "operator": {
                "type": "number",
                "operation": "gte"
              },
              "leftValue": "={{ $json.readiness_score }}",
              "rightValue": 70
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "ad10f214-5653-4ce2-8934-a933723e984f",
      "name": "Email Full Brief",
      "type": "n8n-nodes-base.gmail",
      "onError": "continueRegularOutput",
      "position": [
        3216,
        592
      ],
      "parameters": {
        "sendTo": "={{ $json.alert_email || $vars.ALERT_EMAIL || 'alerts@company.com' }}",
        "message": "=Pre-call Intelligence Brief\n\nProspect: {{ $json.prospect_name }}\nRole: {{ $json.current_role }}\nCompany: {{ $json.company }}\nMeeting Date: {{ $json.meeting_date }}\nReadiness Score: {{ $json.readiness_score }}/100\n\nCareer Trajectory:\n{{ $json.career_trajectory }}\n\nRecent Activity:\n{{ $json.recent_activity }}\n\nTalking Points:\n{{ Array.isArray($json.talking_points) ? $json.talking_points.join('\\n') : String($json.talking_points || 'N/A') }}\n\nPotential Pain Points:\n{{ Array.isArray($json.potential_pain_points) ? $json.potential_pain_points.join('\\n') : String($json.potential_pain_points || 'N/A') }}\n\nConnection Points: {{ $json.connection_points }}\nRecommended Approach: {{ $json.recommended_approach }}\nRisk Factors: {{ $json.risk_factors }}",
        "options": {},
        "subject": "=Pre-call brief: {{ $json.prospect_name }} at {{ $json.company }} (Ready: {{ $json.readiness_score }})",
        "emailType": "text"
      },
      "typeVersion": 2.1
    },
    {
      "id": "9501040b-97f6-45ee-99fe-408b5b352581",
      "name": "Append Call Briefs",
      "type": "n8n-nodes-base.googleSheets",
      "onError": "continueRegularOutput",
      "position": [
        3424,
        704
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [],
          "mappingMode": "autoMapInputData",
          "matchingColumns": []
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "id",
          "value": "call_briefs"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_SPREADSHEET_ID"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "eb2b8d3e-52fa-49e8-b933-7311e173e4b1",
      "name": "Email Short Brief",
      "type": "n8n-nodes-base.gmail",
      "onError": "continueRegularOutput",
      "position": [
        3136,
        1200
      ],
      "parameters": {
        "sendTo": "={{ $json.alert_email || $vars.ALERT_EMAIL || 'alerts@company.com' }}",
        "message": "=Pre-call Brief (Limited Data)\n\nProspect: {{ $json.prospect_name }}\nRole: {{ $json.current_role }}\nCompany: {{ $json.company }}\nMeeting Date: {{ $json.meeting_date }}\nReadiness Score: {{ $json.readiness_score }}/100\n\nNote: This prospect scored below 70 on readiness. Consider additional research before the call.\n\nCareer Trajectory:\n{{ $json.career_trajectory }}\n\nRecommended Approach: {{ $json.recommended_approach }}\nRisk Factors: {{ $json.risk_factors }}",
        "options": {},
        "subject": "=Pre-call brief (limited): {{ $json.prospect_name }} at {{ $json.company }}",
        "emailType": "text"
      },
      "typeVersion": 2.1
    },
    {
      "id": "6d93f04e-7e22-4909-888a-62e25b8a42ac",
      "name": "Append Call Briefs Partial",
      "type": "n8n-nodes-base.googleSheets",
      "onError": "continueRegularOutput",
      "position": [
        3488,
        992
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [],
          "mappingMode": "autoMapInputData",
          "matchingColumns": []
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "id",
          "value": "call_briefs"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_SPREADSHEET_ID"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "f43d39e9-d277-4281-8dac-40e7f2bc34e7",
      "name": "Append Low Confidence",
      "type": "n8n-nodes-base.googleSheets",
      "onError": "continueRegularOutput",
      "position": [
        2864,
        1184
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [],
          "mappingMode": "autoMapInputData",
          "matchingColumns": []
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "id",
          "value": "low_confidence_briefs"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_SPREADSHEET_ID"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "80150455-96f0-426d-8dea-d93c04e69d81",
      "name": "Extract Key Talking Points",
      "type": "n8n-nodes-base.code",
      "position": [
        1792,
        944
      ],
      "parameters": {
        "jsCode": "// Extract key talking points and rapport hooks from LinkedIn profile data\nconst items = $input.all();\nconst results = [];\n\nfor (const item of items) {\n  const data = item.json;\n  const profile = data.profile || data;\n  \n  const talking_points = [];\n  const rapport_hooks = [];\n  \n  // Career trajectory analysis\n  const experiences = profile.experiences || profile.experience || [];\n  if (experiences.length >= 2) {\n    const current = experiences[0];\n    const previous = experiences[1];\n    const trajectory = current.title && previous.title\n      ? `Moved from ${previous.title} to ${current.title}`\n      : null;\n    if (trajectory) talking_points.push({ type: 'career_move', detail: trajectory });\n  }\n  \n  // Current role tenure\n  if (experiences.length > 0 && experiences[0].start_date) {\n    const start = new Date(experiences[0].start_date);\n    const months = Math.floor((Date.now() - start.getTime()) / (1000 * 60 * 60 * 24 * 30));\n    if (months < 6) talking_points.push({ type: 'new_in_role', detail: `Started ${months} months ago - likely evaluating tools` });\n    else if (months > 36) talking_points.push({ type: 'established', detail: `${months} months in role - focus on optimization pitch` });\n  }\n  \n  // Education - shared alma mater potential\n  const education = profile.education || [];\n  for (const edu of education) {\n    if (edu.school || edu.institution) {\n      rapport_hooks.push({ type: 'education', school: edu.school || edu.institution, degree: edu.degree || '' });\n    }\n  }\n  \n  // Skills and endorsements - find tech stack overlap\n  const skills = profile.skills || [];\n  const tech_skills = skills.filter(s => {\n    const name = (s.name || s || '').toLowerCase();\n    return ['saas', 'cloud', 'api', 'data', 'analytics', 'automation', 'ai', 'machine learning'].some(k => name.includes(k));\n  });\n  if (tech_skills.length > 0) {\n    talking_points.push({ type: 'tech_affinity', skills: tech_skills.slice(0, 5).map(s => s.name || s) });\n  }\n  \n  // Recent activity themes\n  const posts = profile.recent_posts || profile.posts || [];\n  const post_topics = posts.slice(0, 5).map(p => p.title || p.text || '').filter(Boolean);\n  if (post_topics.length > 0) {\n    rapport_hooks.push({ type: 'recent_interests', topics: post_topics.slice(0, 3) });\n  }\n  \n  // Connections count as social proof indicator\n  const connections = profile.connections_count || profile.connections || 0;\n  const influence_tier = connections > 5000 ? 'high' : connections > 1000 ? 'medium' : 'growing';\n  \n  results.push({\n    json: {\n      ...data,\n      talking_points,\n      rapport_hooks,\n      prospect_profile: {\n        influence_tier,\n        connections_count: connections,\n        talking_point_count: talking_points.length,\n        rapport_hook_count: rapport_hooks.length\n      }\n    }\n  });\n}\n\nreturn results;"
      },
      "typeVersion": 2
    },
    {
      "id": "879566ab-2ac1-4115-982b-b03a385916b7",
      "name": "Filter Valid Data",
      "type": "n8n-nodes-base.if",
      "onError": "continueErrorOutput",
      "position": [
        1536,
        944
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "valid-check",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.status }}",
              "rightValue": "ok"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "50ad6b06-e8aa-47eb-b739-af18b0cb4e1b",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -224,
        352
      ],
      "parameters": {
        "width": 560,
        "height": 720,
        "content": "### How it works\n\nThis automated LinkedIn intelligence pipeline scrapes data via Bright Data, analyzes it with GPT-5.5, and filters results by confidence and domain score before writing to Google Sheets. High-priority items trigger a Gmail alert.\n\n1. A schedule trigger reads URLs from a Google Sheet\n2. Each URL is sent to the Bright Data LinkedIn API\n3. Responses are validated for errors, empty results, and async snapshots\n4. GPT-5.5 analyzes the data and returns structured JSON with scores\n5. A confidence gate (>= 0.7) filters unreliable AI outputs\n6. Results route to: call_briefs, call_briefs, low_confidence_briefs\n\n### Setup\n\n1. Create a Google Sheet with a tab named 'upcoming_calls' and a 'url' column\n2. Add your Bright Data API key (HTTP Header Auth, Bearer token)\n3. Add your OpenAI API key\n4. Connect Google Sheets via OAuth\n5. Connect Gmail via OAuth for alerts\n\nCost: ~$0.01-0.03 per item (Bright Data) + ~$0.005 per analysis (GPT-5.5)"
      },
      "typeVersion": 1
    },
    {
      "id": "06c1a0fb-9cde-42aa-9ce1-4ecca4903cce",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        480,
        784
      ],
      "parameters": {
        "color": 7,
        "height": 260,
        "content": "## 1. Data Input\n\nReads LinkedIn URLs from the 'upcoming_calls' sheet."
      },
      "typeVersion": 1
    },
    {
      "id": "500f2464-5d97-4839-9a58-710729a6d8d9",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        992,
        784
      ],
      "parameters": {
        "color": 7,
        "height": 292,
        "content": "## 2. Data Collection\n\nSends each URL to the Bright Data LinkedIn API and validates the response."
      },
      "typeVersion": 1
    },
    {
      "id": "f52ada1a-970f-4ba0-be52-1cb76e22f819",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2000,
        736
      ],
      "parameters": {
        "color": 7,
        "width": 500,
        "height": 532,
        "content": "## 3. AI Analysis\n\nGPT-5.5 analyzes each item and returns structured JSON with scores and self-evaluation."
      },
      "typeVersion": 1
    },
    {
      "id": "6d7d8edd-4f0d-4c4b-98ec-10f80f6e0819",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2544,
        528
      ],
      "parameters": {
        "color": 7,
        "width": 1636,
        "height": 852,
        "content": "## 4. Quality Gates & Output\n\nConfidence gate (>= 0.7) filters bad output. Domain gate: 'IF Readiness Score >= 70'. Routes to: call_briefs, call_briefs, low_confidence_briefs. Gmail alerts for flagged items."
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "GPT-5.5 Model": {
      "ai_languageModel": [
        [
          {
            "node": "Build Call Brief",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Parse AI Output": {
      "main": [
        [
          {
            "node": "IF Confidence >= 0.7",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Call Brief": {
      "main": [
        [
          {
            "node": "Parse AI Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Valid Data": {
      "main": [
        [
          {
            "node": "Extract Key Talking Points",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF Confidence >= 0.7": {
      "main": [
        [
          {
            "node": "IF Readiness Score >= 70",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Append Low Confidence",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate BD Response": {
      "main": [
        [
          {
            "node": "Filter Valid Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daily Prospect Research": {
      "main": [
        [
          {
            "node": "Read Upcoming Calls Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape LinkedIn Profile": {
      "main": [
        [
          {
            "node": "Validate BD Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF Readiness Score >= 70": {
      "main": [
        [
          {
            "node": "Email Full Brief",
            "type": "main",
            "index": 0
          },
          {
            "node": "Append Call Briefs",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Email Short Brief",
            "type": "main",
            "index": 0
          },
          {
            "node": "Append Call Briefs Partial",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Upcoming Calls Sheet": {
      "main": [
        [
          {
            "node": "Scrape LinkedIn Profile",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Key Talking Points": {
      "main": [
        [
          {
            "node": "Build Call Brief",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}