{
  "nodes": [
    {
      "id": "ea23c39f-bcd5-4473-8e15-7312be7c69e2",
      "name": "Sticky Note - Main",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1392,
        128
      ],
      "parameters": {
        "width": 400,
        "height": 740,
        "content": "## \ud83c\udfaf Hiring Signal Monitor & AI Sales Lead Generator\n\nThis workflow automatically monitors job postings across Google Jobs, LinkedIn, and Indeed to identify sales opportunities based on hiring signals.\n\n### What it does:\n1. **Daily Scraping** - Collects job postings from 3 major job boards\n2. **Smart Filtering** - Filters by your target keywords\n3. **AI Analysis** - GPT-4o analyzes each opportunity and drafts personalized emails\n4. **Notifications** - Sends Slack alerts with actionable insights\n5. **Weekly Reports** - AI-generated trend analysis every Monday\n\n### Setup Requirements:\n- Apify account (for job scraping actors)\n- OpenAI API key (GPT-4o)\n- Google Sheets (for data storage)\n- Slack workspace (for notifications)\n- Gmail (for weekly email reports)\n\n### Configuration:\n1. Create Google Sheets with 4 tabs: Target Companies, Raw Jobs, Qualified Leads, Weekly Reports\n2. Add your Apify Actor IDs for each job board\n3. Connect all credentials\n4. Update placeholder values"
      },
      "typeVersion": 1
    },
    {
      "id": "ad6e3f89-7f83-49b3-a0aa-beeffcf52b59",
      "name": "Sticky Note - Daily Flow",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -976,
        64
      ],
      "parameters": {
        "color": 7,
        "width": 280,
        "height": 244,
        "content": "### \ud83d\udcc5 Daily Scraping Flow\n\nTriggers daily at 9 AM to:\n1. Load target companies from Google Sheets\n2. Scrape jobs from 3 sources in parallel\n3. Normalize and deduplicate data\n4. Filter by target keywords\n5. Generate AI sales insights"
      },
      "typeVersion": 1
    },
    {
      "id": "ec2764b8-ad6c-4b83-a81a-821e0d39b9ee",
      "name": "Sticky Note - Normalize",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        0
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 192,
        "content": "### \ud83d\udd04 Data Normalization\n\nEach source returns different field names.\nThese Code nodes standardize all data into a unified schema:\n- source, title, company, location\n- description, postedAt, applyLink\n- salary, targetCompany, jobId"
      },
      "typeVersion": 1
    },
    {
      "id": "cf376121-1022-4ac7-8600-7579fbc1a3b2",
      "name": "Sticky Note - AI Analysis",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1424,
        64
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 224,
        "content": "### \ud83e\udd16 AI Analysis\n\nGPT-4o analyzes each qualified job posting to generate:\n- **Pain Point**: Inferred business challenge\n- **Hook Angle**: Strategic approach for outreach\n- **Urgency Score**: 1-10 rating\n- **Email Draft**: Ready-to-send cold email"
      },
      "typeVersion": 1
    },
    {
      "id": "6a68dfca-a45d-45b4-b647-eb2c0e95573d",
      "name": "Sticky Note - Weekly",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -896,
        880
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 228,
        "content": "### \ud83d\udcca Weekly Reports Flow\n\nTriggers every Monday at 8 AM to:\n1. Aggregate weekly statistics\n2. Generate AI trend analysis\n3. Send formatted reports to Slack & Email\n\nIncludes: conversion rates, top keywords, hot opportunities, and forecasts"
      },
      "typeVersion": 1
    },
    {
      "id": "a3e6c35a-5ada-428d-b53b-96881d898e96",
      "name": "Sticky Note - Config",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -624,
        96
      ],
      "parameters": {
        "color": 7,
        "height": 220,
        "content": "### \u2699\ufe0f Configuration\n\nUpdate these values:\n- `daysToCheck`: How many days back to filter jobs\n- `maxJobsPerSource`: Max jobs per scraping source\n- `targetIndustry`: Your target industry"
      },
      "typeVersion": 1
    },
    {
      "id": "83478bb1-63cb-4f9b-a33d-9cf91fc09dd4",
      "name": "Schedule Trigger - Daily 9AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -784,
        336
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "377da9c1-267b-4996-9faa-09f19119291c",
      "name": "Apify - Scrape Google Jobs",
      "type": "@apify/n8n-nodes-apify.apify",
      "position": [
        -96,
        224
      ],
      "parameters": {
        "actorId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $vars.APIFY_GOOGLE_JOBS_ACTOR_ID }}"
        },
        "timeout": {},
        "operation": "Run actor and get dataset",
        "customBody": "={{ JSON.stringify({\n  query: $json['Company Name'] + ' jobs',\n  maxItems: $('Set Configuration').first().json.maxJobsPerSource,\n  countryCode: 'us'\n}) }}"
      },
      "typeVersion": 1
    },
    {
      "id": "2f2862a3-a8ad-4e3b-9009-254654504fdd",
      "name": "Apify - Scrape LinkedIn Jobs",
      "type": "@apify/n8n-nodes-apify.apify",
      "position": [
        -96,
        336
      ],
      "parameters": {
        "actorId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $vars.APIFY_LINKEDIN_JOBS_ACTOR_ID }}"
        },
        "timeout": {},
        "operation": "Run actor and get dataset",
        "customBody": "={{ JSON.stringify({\n  searchUrl: 'https://www.linkedin.com/jobs/search/?keywords=' + encodeURIComponent($json['Company Name']),\n  maxItems: $('Set Configuration').first().json.maxJobsPerSource\n}) }}"
      },
      "typeVersion": 1
    },
    {
      "id": "6a9374d2-4473-4761-8199-349a5873cf8e",
      "name": "Apify - Scrape Indeed Jobs",
      "type": "@apify/n8n-nodes-apify.apify",
      "position": [
        -96,
        464
      ],
      "parameters": {
        "actorId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $vars.APIFY_INDEED_ACTOR_ID }}"
        },
        "timeout": {},
        "operation": "Run actor and get dataset",
        "customBody": "={{ JSON.stringify({\n  queries: $json['Company Name'],\n  maxItems: $('Set Configuration').first().json.maxJobsPerSource,\n  country: 'US'\n}) }}"
      },
      "typeVersion": 1
    },
    {
      "id": "058aec9b-f084-4cae-98b9-f4079a4caf1d",
      "name": "Code - Normalize Google Jobs",
      "type": "n8n-nodes-base.code",
      "position": [
        112,
        224
      ],
      "parameters": {
        "jsCode": "// Normalize Google Jobs data\nconst jobs = $input.all();\nconst companyData = $('Google Sheets - Get Target Companies').first().json;\nconst runDate = $('Set Configuration').first().json.runDate;\n\nreturn jobs.map(job => ({\n  json: {\n    source: 'Google Jobs',\n    title: job.json.title || '',\n    company: job.json.companyName || job.json.company || '',\n    location: job.json.location || '',\n    description: job.json.description || '',\n    postedAt: job.json.postedAt || job.json.datePosted || '',\n    applyLink: job.json.applyLink || job.json.link || '',\n    salary: job.json.salary || '',\n    targetCompany: companyData['Company Name'] || '',\n    targetKeywords: companyData['Target Keywords'] || '',\n    mySolution: companyData['My Solution'] || '',\n    scrapedDate: runDate,\n    jobId: `google-${job.json.title}-${job.json.companyName}`.replace(/\\s+/g, '-').toLowerCase().substring(0, 100)\n  }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "87af7bb7-48f7-4472-b7ff-35c5ab4a6cc3",
      "name": "Code - Normalize LinkedIn Jobs",
      "type": "n8n-nodes-base.code",
      "position": [
        112,
        336
      ],
      "parameters": {
        "jsCode": "// Normalize LinkedIn Jobs data\nconst jobs = $input.all();\nconst companyData = $('Google Sheets - Get Target Companies').first().json;\nconst runDate = $('Set Configuration').first().json.runDate;\n\nreturn jobs.map(job => ({\n  json: {\n    source: 'LinkedIn',\n    title: job.json.title || '',\n    company: job.json.companyName || job.json.company || '',\n    location: job.json.location || '',\n    description: job.json.description || '',\n    postedAt: job.json.postedAt || job.json.listedAt || '',\n    applyLink: job.json.applyUrl || job.json.link || '',\n    salary: job.json.salary || '',\n    targetCompany: companyData['Company Name'] || '',\n    targetKeywords: companyData['Target Keywords'] || '',\n    mySolution: companyData['My Solution'] || '',\n    scrapedDate: runDate,\n    jobId: `linkedin-${job.json.title}-${job.json.companyName}`.replace(/\\s+/g, '-').toLowerCase().substring(0, 100)\n  }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "5eccde3c-bb49-4db2-8350-2b6044a7e936",
      "name": "Code - Normalize Indeed Jobs",
      "type": "n8n-nodes-base.code",
      "position": [
        112,
        464
      ],
      "parameters": {
        "jsCode": "// Normalize Indeed Jobs data\nconst jobs = $input.all();\nconst companyData = $('Google Sheets - Get Target Companies').first().json;\nconst runDate = $('Set Configuration').first().json.runDate;\n\nreturn jobs.map(job => ({\n  json: {\n    source: 'Indeed',\n    title: job.json.title || job.json.positionName || '',\n    company: job.json.company || job.json.companyName || '',\n    location: job.json.location || '',\n    description: job.json.description || '',\n    postedAt: job.json.postedAt || job.json.postingDate || '',\n    applyLink: job.json.url || job.json.link || '',\n    salary: job.json.salary || '',\n    targetCompany: companyData['Company Name'] || '',\n    targetKeywords: companyData['Target Keywords'] || '',\n    mySolution: companyData['My Solution'] || '',\n    scrapedDate: runDate,\n    jobId: `indeed-${job.json.title}-${job.json.company}`.replace(/\\s+/g, '-').toLowerCase().substring(0, 100)\n  }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "68cd8635-a309-4038-8c95-035e1a34bd9c",
      "name": "Merge - Combine All Sources",
      "type": "n8n-nodes-base.merge",
      "position": [
        336,
        336
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineAll"
      },
      "typeVersion": 3
    },
    {
      "id": "ee5fa01d-016f-49e3-981b-45986a19a661",
      "name": "Code - Deduplicate and Filter by Date",
      "type": "n8n-nodes-base.code",
      "position": [
        560,
        336
      ],
      "parameters": {
        "jsCode": "// Deduplicate jobs by jobId and filter recent jobs\nconst jobs = $input.all();\nconst daysThreshold = $('Set Configuration').first().json.daysToCheck || 7;\nconst now = new Date();\nconst thresholdDate = new Date(now.getTime() - (daysThreshold * 24 * 60 * 60 * 1000));\n\n// Helper function to parse relative dates\nfunction parseDaysAgo(dateString) {\n  if (!dateString) return new Date();\n  \n  const daysAgoMatch = dateString.match(/(\\d+)\\s*days?\\s*ago/i);\n  if (daysAgoMatch) {\n    return new Date(now.getTime() - (parseInt(daysAgoMatch[1]) * 24 * 60 * 60 * 1000));\n  }\n  \n  const hoursAgoMatch = dateString.match(/(\\d+)\\s*hours?\\s*ago/i);\n  if (hoursAgoMatch) {\n    return new Date(now.getTime() - (parseInt(hoursAgoMatch[1]) * 60 * 60 * 1000));\n  }\n  \n  if (dateString.toLowerCase().includes('today') || dateString.toLowerCase().includes('just posted')) {\n    return new Date();\n  }\n  if (dateString.toLowerCase().includes('yesterday')) {\n    return new Date(now.getTime() - (24 * 60 * 60 * 1000));\n  }\n  \n  const parsedDate = new Date(dateString);\n  return !isNaN(parsedDate.getTime()) ? parsedDate : new Date();\n}\n\n// Deduplicate by jobId\nconst seen = new Set();\nconst uniqueJobs = [];\n\nfor (const job of jobs) {\n  const jobId = job.json.jobId;\n  if (!seen.has(jobId)) {\n    seen.add(jobId);\n    \n    // Check if job is within threshold\n    const jobDate = parseDaysAgo(job.json.postedAt);\n    if (jobDate >= thresholdDate) {\n      uniqueJobs.push(job);\n    }\n  }\n}\n\nreturn uniqueJobs;"
      },
      "typeVersion": 2
    },
    {
      "id": "16d6a4ab-7c49-42cc-96e6-6bfda5d05db1",
      "name": "Google Sheets - Save Raw Jobs",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        784,
        336
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Raw Jobs"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $vars.GOOGLE_SHEETS_ID }}"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "cd3e473b-6c31-4166-8b7c-be4ea212ac80",
      "name": "Code - Filter by Target Keywords",
      "type": "n8n-nodes-base.code",
      "position": [
        1008,
        336
      ],
      "parameters": {
        "jsCode": "// Filter jobs by target keywords and add matched keyword info\nconst jobs = $input.all();\nconst filteredJobs = [];\n\nfor (const job of jobs) {\n  const targetKeywords = (job.json.targetKeywords || '').split(',').map(k => k.trim().toLowerCase()).filter(k => k);\n  const combinedText = (job.json.title + ' ' + job.json.description).toLowerCase();\n  \n  let matchedKeyword = null;\n  for (const keyword of targetKeywords) {\n    if (combinedText.includes(keyword)) {\n      matchedKeyword = keyword;\n      break;\n    }\n  }\n  \n  if (matchedKeyword) {\n    filteredJobs.push({\n      json: {\n        ...job.json,\n        matchedKeyword: matchedKeyword\n      }\n    });\n  }\n}\n\nreturn filteredJobs.length > 0 ? filteredJobs : [{ json: { noMatches: true } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "3aa7482e-6806-4fab-b940-670ba3f375af",
      "name": "IF - Check Matches Found",
      "type": "n8n-nodes-base.if",
      "position": [
        1232,
        336
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-1",
              "operator": {
                "type": "boolean",
                "operation": "notTrue"
              },
              "leftValue": "={{ $json.noMatches }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "d2fd3e5d-7962-4127-8d09-b761852461ef",
      "name": "Google Sheets - Save Qualified Leads",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1728,
        320
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Qualified Leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $vars.GOOGLE_SHEETS_ID }}"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "124c8b65-6c67-43f7-ae44-bf92d9bbf6b6",
      "name": "Schedule Trigger - Weekly Monday 8AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -784,
        688
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 8
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "98d382d0-3b75-4c36-8a21-1bf16694f880",
      "name": "Set - Weekly Report Config",
      "type": "n8n-nodes-base.set",
      "position": [
        -560,
        688
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "week-config-1",
              "name": "reportWeekStart",
              "type": "string",
              "value": "={{ $now.minus({days: 7}).format('yyyy-MM-dd') }}"
            },
            {
              "id": "week-config-2",
              "name": "reportWeekEnd",
              "type": "string",
              "value": "={{ $now.format('yyyy-MM-dd') }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "33a4fe1b-d6e0-4789-ab7e-6ae34cb0e1e8",
      "name": "Google Sheets - Get Raw Jobs (Weekly)",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -336,
        592
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Raw Jobs"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $vars.GOOGLE_SHEETS_ID }}"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "b424e08f-47b0-46d1-98b3-0c4a67558a1d",
      "name": "Google Sheets - Get Qualified Leads (Weekly)",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -336,
        768
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Qualified Leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $vars.GOOGLE_SHEETS_ID }}"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "6ba80098-c778-45ce-bd9a-7289aa92a462",
      "name": "Merge - Weekly Data",
      "type": "n8n-nodes-base.merge",
      "position": [
        -112,
        688
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineAll"
      },
      "typeVersion": 3
    },
    {
      "id": "91dc2eaa-aee1-4cf9-8cf4-784c40ab52b0",
      "name": "Code - Aggregate Weekly Statistics",
      "type": "n8n-nodes-base.code",
      "position": [
        112,
        688
      ],
      "parameters": {
        "jsCode": "// Aggregate weekly statistics\nconst rawJobs = $('Google Sheets - Get Raw Jobs (Weekly)').all();\nconst qualifiedLeads = $('Google Sheets - Get Qualified Leads (Weekly)').all();\nconst weekStart = $('Set - Weekly Report Config').first().json.reportWeekStart;\nconst weekEnd = $('Set - Weekly Report Config').first().json.reportWeekEnd;\n\n// Filter to this week's data\nconst thisWeekJobs = rawJobs.filter(job => {\n  const jobDate = job.json.scrapedDate;\n  return jobDate >= weekStart && jobDate <= weekEnd;\n});\n\nconst thisWeekLeads = qualifiedLeads.filter(lead => {\n  const leadDate = lead.json.scrapedDate || lead.json.date;\n  return leadDate >= weekStart && leadDate <= weekEnd;\n});\n\n// Calculate statistics\nconst stats = {\n  totalJobs: thisWeekJobs.length,\n  totalQualifiedLeads: thisWeekLeads.length,\n  conversionRate: thisWeekJobs.length > 0 ? ((thisWeekLeads.length / thisWeekJobs.length) * 100).toFixed(1) : 0,\n  bySource: {},\n  byKeyword: {},\n  topCompanies: {},\n  highUrgency: 0,\n  mediumUrgency: 0,\n  lowUrgency: 0,\n  avgUrgency: 0\n};\n\n// Count by source\nfor (const job of thisWeekJobs) {\n  const source = job.json.source || 'Unknown';\n  stats.bySource[source] = (stats.bySource[source] || 0) + 1;\n}\n\n// Count by matched keyword and companies from leads\nlet totalUrgency = 0;\nfor (const lead of thisWeekLeads) {\n  const keyword = lead.json.matchedKeyword || 'Unknown';\n  stats.byKeyword[keyword] = (stats.byKeyword[keyword] || 0) + 1;\n  \n  const company = lead.json.company || 'Unknown';\n  stats.topCompanies[company] = (stats.topCompanies[company] || 0) + 1;\n  \n  const urgency = parseInt(lead.json.urgencyScore) || 5;\n  totalUrgency += urgency;\n  \n  if (urgency >= 8) stats.highUrgency++;\n  else if (urgency >= 5) stats.mediumUrgency++;\n  else stats.lowUrgency++;\n}\n\nstats.avgUrgency = thisWeekLeads.length > 0 ? (totalUrgency / thisWeekLeads.length).toFixed(1) : 0;\n\nconst sortedKeywords = Object.entries(stats.byKeyword).sort((a, b) => b[1] - a[1]).slice(0, 5);\nconst sortedCompanies = Object.entries(stats.topCompanies).sort((a, b) => b[1] - a[1]).slice(0, 5);\n\nreturn [{\n  json: {\n    weekStart,\n    weekEnd,\n    stats,\n    topKeywords: sortedKeywords,\n    topCompanies: sortedCompanies,\n    rawJobsData: thisWeekJobs.map(j => j.json),\n    leadsData: thisWeekLeads.map(l => l.json)\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "515d9f74-1118-442c-867b-0e4a28ee8ebd",
      "name": "AI Agent - Weekly Trend Analysis",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        336,
        688
      ],
      "parameters": {
        "text": "=Generate a comprehensive weekly hiring signal analysis report based on this data:\n\n**Report Period:** {{ $json.weekStart }} to {{ $json.weekEnd }}\n\n**Statistics:**\n- Total Jobs Scraped: {{ $json.stats.totalJobs }}\n- Qualified Leads: {{ $json.stats.totalQualifiedLeads }}\n- Conversion Rate: {{ $json.stats.conversionRate }}%\n- Average Urgency Score: {{ $json.stats.avgUrgency }}/10\n\n**By Source:**\n{{ JSON.stringify($json.stats.bySource) }}\n\n**Top Keywords:**\n{{ JSON.stringify($json.topKeywords) }}\n\n**Top Companies:**\n{{ JSON.stringify($json.topCompanies) }}\n\n**Urgency Distribution:**\n- High (8-10): {{ $json.stats.highUrgency }}\n- Medium (5-7): {{ $json.stats.mediumUrgency }}\n- Low (1-4): {{ $json.stats.lowUrgency }}\n\n**Sample Leads Data (for context):**\n{{ JSON.stringify($json.leadsData.slice(0, 5)) }}",
        "options": {
          "systemMessage": "You are a sales intelligence analyst. Generate a professional weekly report analyzing hiring signals and sales opportunities.\n\nYour report should include:\n\n1. **executive_summary**: 2-3 sentence overview of the week's highlights and key takeaways\n\n2. **trend_analysis**: Analysis of:\n   - Which sources are most productive\n   - Which keywords are trending up/down\n   - Industry patterns observed\n\n3. **hot_opportunities**: Top 3-5 companies to prioritize this week with reasoning\n\n4. **recommended_actions**: Specific action items for the sales team\n\n5. **forecast**: Prediction for next week based on current trends\n\n6. **report_html**: A beautifully formatted HTML report suitable for email/Slack that includes all the above sections with proper styling (use inline CSS, modern design, emojis for visual appeal)\n\nRespond in JSON format. Make insights actionable and specific."
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 3
    },
    {
      "id": "043da99d-83ad-47d7-bc63-d52b40adee67",
      "name": "OpenAI Chat Model (Weekly)",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        336,
        912
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "id",
          "value": "gpt-4o"
        },
        "options": {},
        "builtInTools": {}
      },
      "typeVersion": 1.3
    },
    {
      "id": "b9d7838d-9e4a-4991-9b6e-057e3212b48e",
      "name": "Structured Output Parser (Weekly)",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        480,
        912
      ],
      "parameters": {
        "schemaType": "manual",
        "inputSchema": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"executive_summary\": {\n      \"type\": \"string\",\n      \"description\": \"2-3 sentence overview of key highlights\"\n    },\n    \"trend_analysis\": {\n      \"type\": \"string\",\n      \"description\": \"Analysis of sources, keywords, and industry patterns\"\n    },\n    \"hot_opportunities\": {\n      \"type\": \"string\",\n      \"description\": \"Top companies to prioritize with reasoning\"\n    },\n    \"recommended_actions\": {\n      \"type\": \"string\",\n      \"description\": \"Specific action items for sales team\"\n    },\n    \"forecast\": {\n      \"type\": \"string\",\n      \"description\": \"Prediction for next week\"\n    },\n    \"report_html\": {\n      \"type\": \"string\",\n      \"description\": \"Beautifully formatted HTML report\"\n    }\n  },\n  \"required\": [\"executive_summary\", \"trend_analysis\", \"hot_opportunities\", \"recommended_actions\", \"forecast\", \"report_html\"]\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "b591c4dd-4d4f-4b70-997d-3e03635b5070",
      "name": "Google Sheets - Save Weekly Report",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        576,
        688
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Weekly Reports"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $vars.GOOGLE_SHEETS_ID }}"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "757a1331-60ca-4569-a7bb-d1da568bf9a8",
      "name": "Slack - Send Weekly Report",
      "type": "n8n-nodes-base.slack",
      "position": [
        784,
        592
      ],
      "parameters": {
        "select": "channel",
        "blocksUi": {
          "blocksValues": [
            {
              "text": {
                "text": "\ud83d\udcca Weekly Hiring Signal Report",
                "type": "plain_text",
                "emoji": true
              },
              "type": "header"
            },
            {
              "text": {
                "text": "={{ '*Period:* ' + $('Code - Aggregate Weekly Statistics').first().json.weekStart + ' to ' + $('Code - Aggregate Weekly Statistics').first().json.weekEnd }}",
                "type": "mrkdwn"
              },
              "type": "section"
            },
            {
              "type": "divider"
            },
            {
              "text": {
                "text": "=*\ud83d\udcc8 Executive Summary*\n{{ $json.output.executive_summary }}",
                "type": "mrkdwn"
              },
              "type": "section"
            },
            {
              "type": "section",
              "fields": [
                {
                  "text": "=*Total Jobs:*\n{{ $('Code - Aggregate Weekly Statistics').first().json.stats.totalJobs }}",
                  "type": "mrkdwn"
                },
                {
                  "text": "=*Qualified Leads:*\n{{ $('Code - Aggregate Weekly Statistics').first().json.stats.totalQualifiedLeads }}",
                  "type": "mrkdwn"
                },
                {
                  "text": "=*Conversion Rate:*\n{{ $('Code - Aggregate Weekly Statistics').first().json.stats.conversionRate }}%",
                  "type": "mrkdwn"
                },
                {
                  "text": "=*Avg Urgency:*\n{{ $('Code - Aggregate Weekly Statistics').first().json.stats.avgUrgency }}/10",
                  "type": "mrkdwn"
                }
              ]
            },
            {
              "type": "divider"
            },
            {
              "text": {
                "text": "=*\ud83d\udd25 Hot Opportunities*\n{{ $json.output.hot_opportunities }}",
                "type": "mrkdwn"
              },
              "type": "section"
            },
            {
              "text": {
                "text": "=*\u2705 Recommended Actions*\n{{ $json.output.recommended_actions }}",
                "type": "mrkdwn"
              },
              "type": "section"
            },
            {
              "text": {
                "text": "=*\ud83d\udd2e Forecast*\n{{ $json.output.forecast }}",
                "type": "mrkdwn"
              },
              "type": "section"
            }
          ]
        },
        "channelId": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $vars.SLACK_CHANNEL }}"
        },
        "messageType": "block",
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "typeVersion": 2.3
    },
    {
      "id": "148147a7-4beb-41c0-98e8-7215e2109eb7",
      "name": "Gmail - Send Weekly Report",
      "type": "n8n-nodes-base.gmail",
      "position": [
        784,
        768
      ],
      "parameters": {
        "sendTo": "={{ $vars.NOTIFICATION_EMAIL }}",
        "message": "={{ $json.output.report_html }}",
        "options": {},
        "subject": "=\ud83d\udcca Weekly Hiring Signal Report ({{ $('Code - Aggregate Weekly Statistics').first().json.weekStart }} - {{ $('Code - Aggregate Weekly Statistics').first().json.weekEnd }})"
      },
      "typeVersion": 2.1
    },
    {
      "id": "173e04a7-0060-4e4b-ada7-0d997946e079",
      "name": "Set Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        -560,
        336
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "config-1",
              "name": "daysToCheck",
              "type": "number",
              "value": 7
            },
            {
              "id": "config-2",
              "name": "maxJobsPerSource",
              "type": "number",
              "value": 50
            },
            {
              "id": "config-3",
              "name": "targetIndustry",
              "type": "string",
              "value": "Technology"
            },
            {
              "id": "config-4",
              "name": "runDate",
              "type": "string",
              "value": "={{ $now.format('yyyy-MM-dd') }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "6c9d5f0f-4ef7-4d83-afd0-4753afd3757c",
      "name": "Google Sheets - Get Target Companies",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -336,
        336
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Target Companies"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $vars.GOOGLE_SHEETS_ID }}"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "bb9fbda4-8267-46ca-bbf1-f863a44bcded",
      "name": "AI Agent - Analyze and Generate Email",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1456,
        320
      ],
      "parameters": {
        "text": "=Analyze this job posting and generate a B2B sales approach:\n\n\u3010Target Company\u3011: {{ $json.company }}\n\u3010Job Title\u3011: {{ $json.title }}\n\u3010Job Description\u3011: {{ $json.description }}\n\u3010Location\u3011: {{ $json.location }}\n\u3010Source\u3011: {{ $json.source }}\n\n\u3010Our Solution\u3011: {{ $json.mySolution }}\n\u3010Matched Keyword\u3011: {{ $json.matchedKeyword }}",
        "options": {
          "systemMessage": "You are a B2B sales strategist. Analyze job postings to identify business challenges and create targeted sales outreach.\n\nBased on the job posting, provide:\n\n1. **pain_point**: Infer the underlying business challenge from the hiring signal (e.g., \"Rapid scaling requiring data infrastructure expertise\", \"Digital transformation initiative lacking internal resources\")\n\n2. **hook_angle**: A strategic approach angle that positions our solution as addressing their need (not just \"we can help\" but a specific value proposition)\n\n3. **urgency_score**: Rate 1-10 how urgent this opportunity is based on:\n   - Job posting recency\n   - Role seniority\n   - Expansion signals in description\n   - Technology stack alignment\n\n4. **email_draft**: A complete, ready-to-send cold email including:\n   - Compelling subject line\n   - Personalized opening referencing their hiring\n   - Value proposition tied to their specific situation\n   - Soft CTA (not pushy)\n   - Professional signature placeholder\n\nRespond in JSON format. Write emails in the same language as the job posting."
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 3
    },
    {
      "id": "0569741a-4e50-4666-979b-a513a62de280",
      "name": "OpenAI Chat Model (Daily)",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        1472,
        544
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "id",
          "value": "gpt-4o"
        },
        "options": {},
        "builtInTools": {}
      },
      "typeVersion": 1.3
    },
    {
      "id": "2a683860-a6cd-4176-8265-eb7d31697a96",
      "name": "Structured Output Parser (Daily)",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        1616,
        544
      ],
      "parameters": {
        "schemaType": "manual",
        "inputSchema": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"pain_point\": {\n      \"type\": \"string\",\n      \"description\": \"Inferred business challenge from hiring signal\"\n    },\n    \"hook_angle\": {\n      \"type\": \"string\",\n      \"description\": \"Strategic approach angle for sales outreach\"\n    },\n    \"urgency_score\": {\n      \"type\": \"number\",\n      \"description\": \"Opportunity urgency rating 1-10\"\n    },\n    \"email_draft\": {\n      \"type\": \"string\",\n      \"description\": \"Complete ready-to-send sales email with subject line\"\n    }\n  },\n  \"required\": [\"pain_point\", \"hook_angle\", \"urgency_score\", \"email_draft\"]\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "eb19d958-e8ea-4995-a9aa-a57b438e59b5",
      "name": "Slack - Send Lead Alert",
      "type": "n8n-nodes-base.slack",
      "position": [
        1936,
        320
      ],
      "parameters": {
        "text": "=\ud83d\udea8 *Hiring Signal Detected*\n\n*Company:* {{ $('Code - Filter by Target Keywords').item.json.company }}\n*Position:* {{ $('Code - Filter by Target Keywords').item.json.title }}\n*Source:* {{ $('Code - Filter by Target Keywords').item.json.source }}\n*Matched Keyword:* `{{ $('Code - Filter by Target Keywords').item.json.matchedKeyword }}`\n*Urgency Score:* {{ $json.output.urgency_score }}/10\n\n\ud83d\udca1 *Pain Point:* {{ $json.output.pain_point }}\n\n\ud83c\udfaf *Approach Angle:* {{ $json.output.hook_angle }}\n\n\u2709\ufe0f *Draft Email:*\n```\n{{ $json.output.email_draft }}\n```\n\n\ud83d\udd17 <{{ $('Code - Filter by Target Keywords').item.json.applyLink }}|View Job Posting>",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $vars.SLACK_CHANNEL }}"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "typeVersion": 2.3
    }
  ],
  "connections": {
    "Set Configuration": {
      "main": [
        [
          {
            "node": "Google Sheets - Get Target Companies",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge - Weekly Data": {
      "main": [
        [
          {
            "node": "Code - Aggregate Weekly Statistics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF - Check Matches Found": {
      "main": [
        [
          {
            "node": "AI Agent - Analyze and Generate Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model (Daily)": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent - Analyze and Generate Email",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Apify - Scrape Google Jobs": {
      "main": [
        [
          {
            "node": "Code - Normalize Google Jobs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Apify - Scrape Indeed Jobs": {
      "main": [
        [
          {
            "node": "Code - Normalize Indeed Jobs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model (Weekly)": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent - Weekly Trend Analysis",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Set - Weekly Report Config": {
      "main": [
        [
          {
            "node": "Google Sheets - Get Raw Jobs (Weekly)",
            "type": "main",
            "index": 0
          },
          {
            "node": "Google Sheets - Get Qualified Leads (Weekly)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge - Combine All Sources": {
      "main": [
        [
          {
            "node": "Code - Deduplicate and Filter by Date",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Apify - Scrape LinkedIn Jobs": {
      "main": [
        [
          {
            "node": "Code - Normalize LinkedIn Jobs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code - Normalize Google Jobs": {
      "main": [
        [
          {
            "node": "Merge - Combine All Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code - Normalize Indeed Jobs": {
      "main": [
        [
          {
            "node": "Merge - Combine All Sources",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Schedule Trigger - Daily 9AM": {
      "main": [
        [
          {
            "node": "Set Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets - Save Raw Jobs": {
      "main": [
        [
          {
            "node": "Code - Filter by Target Keywords",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code - Normalize LinkedIn Jobs": {
      "main": [
        [
          {
            "node": "Merge - Combine All Sources",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "AI Agent - Weekly Trend Analysis": {
      "main": [
        [
          {
            "node": "Google Sheets - Save Weekly Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code - Filter by Target Keywords": {
      "main": [
        [
          {
            "node": "IF - Check Matches Found",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser (Daily)": {
      "ai_outputParser": [
        [
          {
            "node": "AI Agent - Analyze and Generate Email",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser (Weekly)": {
      "ai_outputParser": [
        [
          {
            "node": "AI Agent - Weekly Trend Analysis",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Code - Aggregate Weekly Statistics": {
      "main": [
        [
          {
            "node": "AI Agent - Weekly Trend Analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets - Save Weekly Report": {
      "main": [
        [
          {
            "node": "Slack - Send Weekly Report",
            "type": "main",
            "index": 0
          },
          {
            "node": "Gmail - Send Weekly Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets - Get Target Companies": {
      "main": [
        [
          {
            "node": "Apify - Scrape Google Jobs",
            "type": "main",
            "index": 0
          },
          {
            "node": "Apify - Scrape LinkedIn Jobs",
            "type": "main",
            "index": 0
          },
          {
            "node": "Apify - Scrape Indeed Jobs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets - Save Qualified Leads": {
      "main": [
        [
          {
            "node": "Slack - Send Lead Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger - Weekly Monday 8AM": {
      "main": [
        [
          {
            "node": "Set - Weekly Report Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent - Analyze and Generate Email": {
      "main": [
        [
          {
            "node": "Google Sheets - Save Qualified Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code - Deduplicate and Filter by Date": {
      "main": [
        [
          {
            "node": "Google Sheets - Save Raw Jobs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets - Get Raw Jobs (Weekly)": {
      "main": [
        [
          {
            "node": "Merge - Weekly Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets - Get Qualified Leads (Weekly)": {
      "main": [
        [
          {
            "node": "Merge - Weekly Data",
            "type": "main",
            "index": 1
          }
        ]
      ]
    }
  }
}