This workflow corresponds to n8n.io template #11574 — we link there as the canonical source.
This workflow follows the Agent → 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 →
{
"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
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This template is designed for B2B sales teams, recruiters, and business development professionals who want to identify sales opportunities by monitoring hiring signals from target companies. It's particularly useful for: Sales teams selling HR tech, recruitment software, or…
Source: https://n8n.io/workflows/11574/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Created by: Peyton Leveillee Last updated: October 2025
This workflow automates end-to-end ESG (Environmental, Social, and Governance) sustainability reporting for enterprise sustainability teams, compliance officers, and green governance leads. It solves
Automates sales data analysis and strategic insight generation for sales managers and strategists needing actionable intelligence. Fetches multi-source data from sales, marketing, and financial system
This workflow automates energy portfolio governance for energy managers, sustainability teams, and policy compliance officers. It eliminates the manual effort of aggregating multi-source energy data,
Stay ahead in your job search with this Automated Job Intelligence System! This workflow scans company career pages daily for new job listings, uses AI to analyze job relevance and seniority levels, a