This workflow follows the Agent → Google Sheets 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 →
{
"name": "LinkedIn Job Search \u2192 Hiring Manager Outreach",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "hours",
"hoursInterval": 6
}
]
}
},
"id": "fabbce39-d87a-4c3a-ae93-c7a1bc6ca5a9",
"name": "\u23f0 Schedule (Every 6 Hours)",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
-4432,
336
]
},
{
"parameters": {},
"id": "cce4f494-ac88-4706-92a3-529e56699961",
"name": "\u25b6\ufe0f Manual Trigger",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
-4432,
144
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.connectsafely.ai/linkedin/search/jobs",
"authentication": "genericCredentialType",
"genericAuthType": "httpBearerAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"keywords\": \"{{ $json.keywords || 'software engineer' }}\",\n \"location\": \"{{ $json.location || 'United States' }}\",\n \"datePosted\": \"{{ $json.datePosted || 'past24Hours' }}\",\n \"jobType\": \"{{ $json.jobType || '' }}\",\n \"experienceLevel\": \"{{ $json.experienceLevel || '' }}\",\n \"workplaceType\": \"{{ $json.workplaceType || '' }}\",\n \"limit\": {{ $json.limit || 25 }}\n}",
"options": {}
},
"id": "d429e118-01aa-4a13-8777-11aee0f37f8e",
"name": "\ud83d\udd0d Search LinkedIn Jobs",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-3984,
240
],
"credentials": {
"httpBearerAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Extract job listings from API response\nconst jobs = $input.first().json.data || $input.first().json.jobs || $input.first().json;\n\nif (!Array.isArray(jobs)) {\n return [{ json: { error: 'No jobs found or invalid response format' } }];\n}\n\n// Map each job to output items\nreturn jobs.map(job => ({\n json: {\n jobId: job.jobId || job.id,\n title: job.title,\n company: job.company?.name || job.companyName,\n companyUrl: job.company?.url || job.companyUrl,\n companyId: job.company?.id || job.companyId,\n location: job.location,\n jobUrl: job.url || job.jobUrl,\n postedDate: job.postedDate || job.listedAt,\n applicantCount: job.applicantCount,\n description: job.description,\n employmentType: job.employmentType,\n experienceLevel: job.experienceLevel,\n industries: job.industries\n }\n}));"
},
"id": "a1fa8e86-9c5b-4b34-8f8a-7b50253a913c",
"name": "\ud83d\udcc4 Parse Job Results",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-3760,
240
]
},
{
"parameters": {
"options": {}
},
"id": "a17f1e67-8f9d-430d-b560-a09e143015f1",
"name": "\ud83d\udd04 Loop Over Jobs",
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"position": [
-3504,
304
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.connectsafely.ai/linkedin/companies/details",
"authentication": "genericCredentialType",
"genericAuthType": "httpBearerAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"companyId\": \"{{ $json.companyId }}\"\n}",
"options": {}
},
"id": "cae465e2-699f-423f-a3cb-c155dedb2a61",
"name": "\ud83c\udfe2 Get Company Details",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-3312,
-112
],
"credentials": {
"httpBearerAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Merge job data with company details\nconst jobData = $('\ud83d\udd04 Loop Over Jobs').first().json;\nconst companyData = $input.first().json.company\n\nreturn [{\n json: {\n ...jobData,\n companyDetails: companyData\n }\n}];"
},
"id": "f9058a55-3e78-402f-a779-47fb2a5b43d4",
"name": "\ud83d\udd17 Merge Company Data",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-3088,
-112
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.connectsafely.ai/linkedin/search/people",
"authentication": "genericCredentialType",
"genericAuthType": "httpBearerAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"keywords\": \"{{ $json.title.includes('Engineer') ? 'Engineering Manager' : ($json.title.includes('Sales') ? 'Sales Director' : ($json.title.includes('Marketing') ? 'Marketing Director' : 'Hiring Manager')) }}\",\n \"count\": 5,\n \"filters\": {\n \"company\": \"{{ $json.companyDetails.name }}\",\n \"title\": \"{{ $json.title.includes('Engineer') ? 'Engineering Manager OR VP Engineering OR Director Engineering OR CTO' : ($json.title.includes('Sales') ? 'Sales Director OR VP Sales OR Head of Sales' : ($json.title.includes('Marketing') ? 'Marketing Director OR VP Marketing OR CMO' : 'HR Director OR Talent Acquisition OR Recruiter')) }}\",\n \"connectionDegree\": [\"S\", \"O\"]\n }\n}",
"options": {}
},
"id": "29734f04-4636-4583-9c48-7cee6791afe9",
"name": "\ud83d\udc65 Search Hiring Managers",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-2864,
-112
],
"credentials": {
"httpBearerAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Parse hiring managers and prepare for outreach\nconst jobData = $('\ud83d\udd17 Merge Company Data').first().json;\nconst peopleResponse = $input.first().json;\nconst people = peopleResponse.data || peopleResponse.people || peopleResponse;\n\nif (!Array.isArray(people) || people.length === 0) {\n return [{\n json: {\n ...jobData,\n hiringManagers: [],\n noHiringManagerFound: true\n }\n }];\n}\n\n// Create output for each hiring manager found\nreturn people.map(person => ({\n json: {\n jobTitle: jobData.title,\n jobUrl: jobData.jobUrl,\n company: jobData.company,\n companyDetails: jobData.companyDetails,\n hiringManager: {\n name: person.name || `${person.firstName} ${person.lastName}`,\n firstName: person.firstName || person.name?.split(' ')[0],\n title: person.title || person.headline,\n profileUrl: person.url || person.profileUrl || person.linkedinUrl,\n location: person.location,\n connectionDegree: person.connectionDegree || person.degree\n }\n }\n}));"
},
"id": "d14b6ec0-8cae-41f6-b9eb-485e21a3584f",
"name": "\ud83d\udccb Parse Hiring Managers",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-2640,
-112
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "has-hiring-manager",
"leftValue": "={{ $json.hiringManager?.profileUrl }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "notEmpty"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "fd936aa1-9f49-4fe7-911c-1f02f71396ea",
"name": "\u2753 Has Valid Profile?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
-2416,
-112
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.connectsafely.ai/linkedin/profile",
"authentication": "genericCredentialType",
"genericAuthType": "httpBearerAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"profileId\": \"{{ $json.hiringManager.profileUrl.split('/in/')?.[1]?.replace('/', ''); }}\"\n}",
"options": {}
},
"id": "f6da01b8-0632-4591-94be-ede3fc277c5a",
"name": "\ud83d\udc64 Fetch Profile Details",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-2192,
-208
],
"credentials": {
"httpBearerAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Enrich hiring manager data with full profile\nconst currentData = $('\u2753 Has Valid Profile?').first().json;\nconst profileData = $input.first().json.data || $input.first().json;\n\nreturn [{\n json: {\n ...currentData,\n hiringManager: {\n ...currentData.hiringManager,\n fullName: profileData.name || profileData.fullName,\n headline: profileData.headline || profileData.title,\n summary: profileData.summary || profileData.about,\n experience: profileData.experience,\n education: profileData.education,\n skills: profileData.skills,\n connectionCount: profileData.connectionCount\n }\n }\n}];"
},
"id": "691adf21-9edb-4b84-bca6-51b217f3b89d",
"name": "\u2728 Enrich Profile Data",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-1968,
-208
]
},
{
"parameters": {
"jsCode": "// Prepare final data for connection request\nconst enrichedData = $('\u2728 Enrich Profile Data').first().json;\nconst aiMessage = $input.first().json.output.text;\n\n// Clean and truncate message to 280 chars\nlet cleanMessage = aiMessage\n .replace(/\\n/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n\nif (cleanMessage.length > 280) {\n cleanMessage = cleanMessage.substring(0, 277) + '...';\n}\n\nreturn [{\n json: {\n profileUrl: enrichedData.hiringManager.profileUrl,\n firstName: enrichedData.hiringManager.firstName,\n fullName: enrichedData.hiringManager.name,\n title: enrichedData.hiringManager.title,\n company: enrichedData.company,\n jobTitle: enrichedData.jobTitle,\n jobUrl: enrichedData.jobUrl,\n connectionMessage: cleanMessage,\n messageLength: cleanMessage.length\n }\n}];"
},
"id": "67aa8108-e26d-487a-8c0a-84272acbdadf",
"name": "\ud83d\udcdd Prepare Connection Request",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-704,
-352
]
},
{
"parameters": {
"url": "=https://api.connectsafely.ai/linkedin/relationship/{{ $json.profileUrl.split('/in/')?.[1]?.replace('/', ''); }}",
"authentication": "genericCredentialType",
"genericAuthType": "httpBearerAuth",
"options": {}
},
"id": "a9a06f33-7fb8-486f-80c8-e9f5106929a9",
"name": "\ud83d\udd17 Check Connection Status",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-496,
-352
],
"credentials": {
"httpBearerAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "not-connected",
"leftValue": "={{ $json.data?.isConnected || $json.isConnected || $json.connected }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "notEquals"
}
},
{
"id": "not-pending",
"leftValue": "={{ $json.data?.isPending || $json.isPending || $json.pending }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "notEquals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "216298ce-60fb-40a2-b75d-e30d124cbec4",
"name": "\u2753 Not Already Connected?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
-272,
-352
]
},
{
"parameters": {},
"id": "d1660c5a-f731-470f-8503-c44140498766",
"name": "\u23f3 Random Delay (1-4 min)",
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [
-48,
-448
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.connectsafely.ai/linkedin/connect",
"authentication": "genericCredentialType",
"genericAuthType": "httpBearerAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"profileId\": \"{{ $('\ud83d\udcdd Prepare Connection Request').first().json.profileUrl.split('/in/')?.[1]?.replace('/', ''); }}\",\n \"customMessage\": \"{{ $('\ud83d\udcdd Prepare Connection Request').first().json.connectionMessage }}\"\n}",
"options": {}
},
"id": "3411caaf-4e0d-4324-a4ad-62324bf5f94a",
"name": "\u2709\ufe0f Send Connection Request",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
176,
-448
],
"credentials": {
"httpBearerAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Log successful connection request\nconst requestData = $('\ud83d\udcdd Prepare Connection Request').first().json;\nconst response = $input.first().json;\n\nreturn [{\n json: {\n status: 'SUCCESS',\n timestamp: new Date().toISOString(),\n targetProfile: {\n name: requestData.fullName,\n title: requestData.title,\n company: requestData.company,\n profileUrl: requestData.profileUrl\n },\n jobContext: {\n jobTitle: requestData.jobTitle,\n jobUrl: requestData.jobUrl\n },\n messageSent: requestData.connectionMessage,\n apiResponse: response\n }\n}];"
},
"id": "f009823e-d857-43c2-ad00-4d82aa47c590",
"name": "\u2705 Log: Success",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
400,
-448
]
},
{
"parameters": {
"jsCode": "// Log skipped (already connected)\nconst requestData = $('\ud83d\udcdd Prepare Connection Request').first().json;\nconst relationshipData = $('\ud83d\udd17 Check Connection Status').first().json;\n\nreturn [{\n json: {\n status: 'SKIPPED',\n reason: 'Already connected or pending',\n timestamp: new Date().toISOString(),\n targetProfile: {\n name: requestData.fullName,\n title: requestData.title,\n company: requestData.company,\n profileUrl: requestData.profileUrl\n },\n connectionStatus: relationshipData\n }\n}];"
},
"id": "1854ccd4-e7aa-4017-bf58-2c196d66d8a5",
"name": "\u23ed\ufe0f Log: Skipped (Already Connected)",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-48,
-208
]
},
{
"parameters": {
"jsCode": "// Log no hiring manager found\nconst jobData = $input.first().json;\n\nreturn [{\n json: {\n status: 'NO_HIRING_MANAGER',\n timestamp: new Date().toISOString(),\n jobTitle: jobData.jobTitle,\n company: jobData.company,\n jobUrl: jobData.jobUrl\n }\n}];"
},
"id": "62a1d62b-c703-4f7d-b015-7f5a32c1440c",
"name": "\ud83d\udcdd Log: No Manager Found",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-2192,
96
]
},
{
"parameters": {
"options": {}
},
"id": "c4d07044-bfc5-4284-a6de-4c8f440310a9",
"name": "\u2699\ufe0f Set Search Parameters",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
-4208,
240
],
"notes": "Configure your job search criteria here:\n- keywords: Job title or skills to search\n- location: Geographic filter\n- datePosted: past24Hours, pastWeek, pastMonth\n- limit: Number of jobs to process (max 100)"
},
{
"parameters": {
"promptType": "define",
"text": "=Write a personalized LinkedIn connection request message for a job seeker reaching out to a hiring manager.\n\n**HIRING MANAGER INFO:**\n- Name: {{ $json.hiringManager.fullName || $json.hiringManager.name }}\n- First Name: {{ $json.hiringManager.firstName }}\n- Title: {{ $json.hiringManager.headline || $json.hiringManager.title }}\n- Company: {{ $json.company }}\n- Summary: {{ $json.hiringManager.summary || 'Not available' }}\n\n**JOB CONTEXT:**\n- Job Title: {{ $json.jobTitle }}\n- Company: {{ $json.company }}\n\n**REQUIREMENTS:**\n1. Start with \"Hi {{ $json.hiringManager.firstName }},\" \n2. Mention the specific job role ({{ $json.jobTitle }}) naturally\n3. Reference something relevant about their background or company if available\n4. Express genuine interest without being pushy\n5. Keep it conversational and human - NO corporate buzzwords\n6. MUST be under 280 characters total (LinkedIn limit)\n7. End with a soft call to action\n\n**TONE:** Warm, professional, authentic. Write like a real person, not a template.\n\n**OUTPUT:** Return ONLY the connection message text, nothing else.",
"hasOutputParser": true,
"options": {
"systemMessage": "You are an expert at writing short, personalized LinkedIn connection messages that feel genuine and human. You never use corporate jargon, buzzwords, or generic phrases like 'synergy', 'leverage', 'touch base', or 'circle back'. Your messages are warm, specific, and always under 280 characters. You write like a thoughtful professional reaching out to another human being."
}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 2.2,
"position": [
-1072,
-448
],
"id": "16eb106f-68be-4d39-9f60-84b81120c206",
"name": "\ud83e\udd16 AI Generate Message"
},
{
"parameters": {
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"typeVersion": 1,
"position": [
-1104,
-240
],
"id": "2f3ef9c4-6e63-4c17-bc9e-63a603a6e576",
"name": "\ud83e\udde0 Google Gemini",
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"schemaType": "manual",
"inputSchema": "{\n\t\"type\": \"object\",\n\t\"properties\": {\n\t\t\"text\": {\n\t\t\t\"type\": \"string\"\n\t\t}\n\t}\n}"
},
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"typeVersion": 1.3,
"position": [
-896,
-240
],
"id": "35944469-0af6-4d3e-b9ca-d076ad466b5c",
"name": "\ud83d\udce4 Structured Output"
},
{
"parameters": {
"documentId": {
"__rl": true,
"value": "1uiPLJuZmpA2I0cKAPuJtVg79yvqcKP9yDiBM8zRps5E",
"mode": "list",
"cachedResultName": "Job Search => Send Connection Request to Hiring Managers (With Tracking)",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1uiPLJuZmpA2I0cKAPuJtVg79yvqcKP9yDiBM8zRps5E/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list",
"cachedResultName": "Sheet1",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1uiPLJuZmpA2I0cKAPuJtVg79yvqcKP9yDiBM8zRps5E/edit#gid=0"
},
"options": {}
},
"id": "32b13ad8-0044-4fe9-9454-05b2ce71919d",
"name": "\ud83d\udcca Get Contacted Profiles",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.5,
"position": [
-1744,
-208
],
"alwaysOutputData": true,
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"notes": "Reads the tracking sheet to get list of already contacted profiles. Set your Google Sheet Document ID here."
},
{
"parameters": {
"jsCode": "// Check if this profile was already contacted\nconst enrichedData = $(\"\u2728 Enrich Profile Data\").first().json;\nconst contactedProfiles = $(\"\ud83d\udcca Get Contacted Profiles\").all();\n\n// Extract profile ID from URL\nconst profileUrl = enrichedData.hiringManager.profileUrl || '';\nconst profileId = profileUrl.split('/in/')?.[1]?.replace('/', '') || '';\n\n// Check if already contacted\nconst alreadyContacted = contactedProfiles.some(row => {\n const rowProfileUrl = row.json.profileUrl || row.json.ProfileUrl || row.json.profile_url || '';\n const rowProfileId = row.json.profileId || row.json.ProfileId || row.json.profile_id || '';\n return rowProfileUrl.includes(profileId) || rowProfileId === profileId;\n});\n\nreturn [{\n json: {\n ...enrichedData,\n profileId: profileId,\n alreadyContacted: alreadyContacted\n }\n}];"
},
"id": "5922b8a4-5189-4d6e-85d5-919e06b53595",
"name": "\ud83d\udd0d Check If Already Contacted",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-1520,
-208
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "not-already-contacted",
"leftValue": "={{ $json.alreadyContacted }}",
"rightValue": false,
"operator": {
"type": "boolean",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "7d0b9354-171c-4cfd-9509-6f4f8eaf4ebd",
"name": "\u23ed\ufe0f Skip If Already Contacted",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
-1296,
-208
]
},
{
"parameters": {
"jsCode": "// Log skipped duplicate\nconst data = $input.first().json;\n\nreturn [{\n json: {\n status: 'SKIPPED_DUPLICATE',\n reason: 'Already contacted this profile previously',\n timestamp: new Date().toISOString(),\n targetProfile: {\n name: data.hiringManager?.fullName || data.hiringManager?.name,\n title: data.hiringManager?.title || data.hiringManager?.headline,\n company: data.company,\n profileUrl: data.hiringManager?.profileUrl\n }\n }\n}];"
},
"id": "338686bd-9cdf-4353-89af-981d4d55eb66",
"name": "\ud83d\udcdd Log: Duplicate Skipped",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-1072,
-48
]
},
{
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "1uiPLJuZmpA2I0cKAPuJtVg79yvqcKP9yDiBM8zRps5E",
"mode": "list",
"cachedResultName": "Job Search => Send Connection Request to Hiring Managers (With Tracking)",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1uiPLJuZmpA2I0cKAPuJtVg79yvqcKP9yDiBM8zRps5E/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"timestamp": "={{ $json.timestamp }}",
"status": "={{ $json.status }}",
"profileId": "={{ $(\"\ud83d\udcdd Prepare Connection Request\").first().json.profileUrl.split(\"/in/\")?.[1]?.replace(\"/\", \"\") }}",
"profileUrl": "={{ $(\"\ud83d\udcdd Prepare Connection Request\").first().json.profileUrl }}",
"fullName": "={{ $(\"\ud83d\udcdd Prepare Connection Request\").first().json.fullName }}",
"title": "={{ $(\"\ud83d\udcdd Prepare Connection Request\").first().json.title }}",
"company": "={{ $(\"\ud83d\udcdd Prepare Connection Request\").first().json.company }}",
"jobTitle": "={{ $(\"\ud83d\udcdd Prepare Connection Request\").first().json.jobTitle }}",
"jobUrl": "={{ $(\"\ud83d\udcdd Prepare Connection Request\").first().json.jobUrl }}",
"messageSent": "={{ $(\"\ud83d\udcdd Prepare Connection Request\").first().json.connectionMessage }}"
},
"matchingColumns": [],
"schema": [
{
"id": "timestamp",
"displayName": "timestamp",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "status",
"displayName": "status",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "profileId",
"displayName": "profileId",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "profileUrl",
"displayName": "profileUrl",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "fullName",
"displayName": "fullName",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "title",
"displayName": "title",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "company",
"displayName": "company",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "jobTitle",
"displayName": "jobTitle",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "jobUrl",
"displayName": "jobUrl",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "messageSent",
"displayName": "messageSent",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
}
]
},
"options": {}
},
"id": "ca9a5bbc-d8fc-451c-a3ea-c89ade611b53",
"name": "\ud83d\udcca Log to Google Sheet",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.5,
"position": [
400,
-208
],
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"notes": "Logs successful connection requests to tracking sheet. Set your Google Sheet Document ID here."
},
{
"parameters": {
"content": "# \ud83c\udfaf LinkedIn Job Search \u2192 Hiring Manager Outreach\n\n## Who is this for?\nJob seekers who want to proactively connect with hiring managers at companies posting relevant jobs.\n\n## What does it do?\n1. **Searches LinkedIn** for job postings matching your criteria\n2. **Finds hiring managers** at each company (Engineering Managers, Directors, VPs, etc.)\n3. **Generates personalized messages** using AI (no generic templates!)\n4. **Sends connection requests** with your custom message\n5. **Tracks everything** in Google Sheets to avoid duplicates\n\n## \u2699\ufe0f Setup Required\n1. **ConnectSafely API Key** - Get yours at [connectsafely.ai](https://connectsafely.ai)\n2. **Google Gemini API Key** - For AI message generation\n3. **Google Sheets** - Create a sheet with columns: `timestamp`, `status`, `profileId`, `profileUrl`, `fullName`, `title`, `company`, `jobTitle`, `jobUrl`, `messageSent`\n\n## \ud83d\udd27 Configuration\nEdit the **\"Set Search Parameters\"** node to customize:\n- `keywords`: Job titles to search (e.g., \"Software Engineer\")\n- `location`: Geographic filter (e.g., \"United States\")\n- `datePosted`: \"past24Hours\", \"pastWeek\", or \"pastMonth\"\n- `limit`: Number of jobs to process (max 100)\n\n## \u23f0 Scheduling\nRuns automatically every 6 hours, or trigger manually for testing.",
"height": 828,
"width": 620
},
"id": "298cea51-3835-404d-9aea-9c85b6c1d632",
"name": "\ud83d\udccb Workflow Overview",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-5184,
-272
]
},
{
"parameters": {
"content": "## \ud83d\ude80 1. Trigger & Configuration\nStart the workflow manually or on schedule.\nConfigure your job search parameters here.",
"height": 468,
"width": 392,
"color": 4
},
"id": "f6c9b3e5-e588-484b-8209-d22ab50636dd",
"name": "Group: Triggers",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-4480,
64
]
},
{
"parameters": {
"content": "## \ud83d\udd0d 2. Search & Parse Jobs\nSearch LinkedIn for jobs matching criteria.\nExtract job details and loop through results.",
"height": 468,
"width": 464,
"color": 6
},
"id": "413afc7a-fe84-4214-90f7-8982b746e883",
"name": "Group: Job Search",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-4064,
64
]
},
{
"parameters": {
"content": "## \ud83c\udfe2 3. Find Hiring Managers\nGet company details, search for relevant decision makers\n(Engineering Managers, Directors, VPs, Recruiters).",
"height": 340,
"width": 900,
"color": 3
},
"id": "702b88a2-ddc5-48f0-a7dd-5b66dd759b11",
"name": "Group: People Search",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-3360,
-176
]
},
{
"parameters": {
"content": "## \ud83d\udc64 4. Enrich Profile Data\nFetch full profile details for personalization.\nCheck if already contacted via Google Sheets.",
"height": 400,
"width": 680,
"color": 2
},
"id": "49e88a4e-3c5e-4f43-af3e-93d5d04ec2ac",
"name": "Group: Profile Enrichment",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-2240,
-320
]
},
{
"parameters": {
"content": "## \ud83e\udd16 5. AI Message Generation\nGoogle Gemini generates a personalized,\nhuman-sounding connection message\n(under 280 characters, no corporate jargon).",
"height": 448,
"width": 400,
"color": 5
},
"id": "f10d3969-7fc4-4f06-861e-6ad304090cc2",
"name": "Group: AI Generation",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-1152,
-560
]
},
{
"parameters": {
"content": "## \u2709\ufe0f 6. Send Connection & Track\nCheck LinkedIn connection status, add random delay\n(mimics human behavior), send request, and log to Google Sheets.",
"height": 512,
"width": 1340,
"color": 7
},
"id": "7847661e-1de6-404d-ba41-1748b6290689",
"name": "Group: Send & Track",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-736,
-528
]
},
{
"parameters": {
"content": "\u26a0\ufe0f **Setup Required**\nConnect your Google Sheet with these columns:\n`timestamp` | `status` | `profileId` | `profileUrl` | `fullName` | `title` | `company` | `jobTitle` | `jobUrl` | `messageSent`",
"height": 140,
"width": 420,
"color": 2
},
"id": "e8c0f0cb-777b-41c3-9cbc-f7b0b693589e",
"name": "Setup: Google Sheet",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-2864,
-432
]
},
{
"parameters": {
"content": "\ud83d\udca1 **Random Delay**\nAdds 1-4 min delay between requests to mimic human behavior and avoid LinkedIn rate limits.",
"height": 100,
"width": 280,
"color": 4
},
"id": "47cc1c43-adcc-47c1-a616-6a0350200b5e",
"name": "Note: Rate Limiting",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-160,
-656
]
}
],
"connections": {
"\u25b6\ufe0f Manual Trigger": {
"main": [
[
{
"node": "\u2699\ufe0f Set Search Parameters",
"type": "main",
"index": 0
}
]
]
},
"\u2699\ufe0f Set Search Parameters": {
"main": [
[
{
"node": "\ud83d\udd0d Search LinkedIn Jobs",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udd0d Search LinkedIn Jobs": {
"main": [
[
{
"node": "\ud83d\udcc4 Parse Job Results",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udcc4 Parse Job Results": {
"main": [
[
{
"node": "\ud83d\udd04 Loop Over Jobs",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udd04 Loop Over Jobs": {
"main": [
[],
[
{
"node": "\ud83c\udfe2 Get Company Details",
"type": "main",
"index": 0
}
]
]
},
"\ud83c\udfe2 Get Company Details": {
"main": [
[
{
"node": "\ud83d\udd17 Merge Company Data",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udd17 Merge Company Data": {
"main": [
[
{
"node": "\ud83d\udc65 Search Hiring Managers",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udc65 Search Hiring Managers": {
"main": [
[
{
"node": "\ud83d\udccb Parse Hiring Managers",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udccb Parse Hiring Managers": {
"main": [
[
{
"node": "\u2753 Has Valid Profile?",
"type": "main",
"index": 0
}
]
]
},
"\u2753 Has Valid Profile?": {
"main": [
[
{
"node": "\ud83d\udc64 Fetch Profile Details",
"type": "main",
"index": 0
}
],
[
{
"node": "\ud83d\udcdd Log: No Manager Found",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udc64 Fetch Profile Details": {
"main": [
[
{
"node": "\u2728 Enrich Profile Data",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udcdd Prepare Connection Request": {
"main": [
[
{
"node": "\ud83d\udd17 Check Connection Status",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udd17 Check Connection Status": {
"main": [
[
{
"node": "\u2753 Not Already Connected?",
"type": "main",
"index": 0
}
]
]
},
"\u2753 Not Already Connected?": {
"main": [
[
{
"node": "\u23f3 Random Delay (1-4 min)",
"type": "main",
"index": 0
}
],
[
{
"node": "\u23ed\ufe0f Log: Skipped (Already Connected)",
"type": "main",
"index": 0
}
]
]
},
"\u23f3 Random Delay (1-4 min)": {
"main": [
[
{
"node": "\u2709\ufe0f Send Connection Request",
"type": "main",
"index": 0
}
]
]
},
"\u2709\ufe0f Send Connection Request": {
"main": [
[
{
"node": "\u2705 Log: Success",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udcdd Log: No Manager Found": {
"main": [
[
{
"node": "\ud83d\udd04 Loop Over Jobs",
"type": "main",
"index": 0
}
]
]
},
"\u2728 Enrich Profile Data": {
"main": [
[
{
"node": "\ud83d\udcca Get Contacted Profiles",
"type": "main",
"index": 0
}
]
]
},
"\ud83e\udd16 AI Generate Message": {
"main": [
[
{
"node": "\ud83d\udcdd Prepare Connection Request",
"type": "main",
"index": 0
}
]
]
},
"\ud83e\udde0 Google Gemini": {
"ai_languageModel": [
[
{
"node": "\ud83e\udd16 AI Generate Message",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"\ud83d\udce4 Structured Output": {
"ai_outputParser": [
[
{
"node": "\ud83e\udd16 AI Generate Message",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"\ud83d\udcca Get Contacted Profiles": {
"main": [
[
{
"node": "\ud83d\udd0d Check If Already Contacted",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udd0d Check If Already Contacted": {
"main": [
[
{
"node": "\u23ed\ufe0f Skip If Already Contacted",
"type": "main",
"index": 0
}
]
]
},
"\u23ed\ufe0f Skip If Already Contacted": {
"main": [
[
{
"node": "\ud83e\udd16 AI Generate Message",
"type": "main",
"index": 0
}
],
[
{
"node": "\ud83d\udcdd Log: Duplicate Skipped",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udcdd Log: Duplicate Skipped": {
"main": [
[
{
"node": "\ud83d\udd04 Loop Over Jobs",
"type": "main",
"index": 0
}
]
]
},
"\u2705 Log: Success": {
"main": [
[
{
"node": "\ud83d\udcca Log to Google Sheet",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udcca Log to Google Sheet": {
"main": [
[
{
"node": "\ud83d\udd04 Loop Over Jobs",
"type": "main",
"index": 0
}
]
]
},
"\u23f0 Schedule (Every 6 Hours)": {
"main": [
[
{
"node": "\u2699\ufe0f Set Search Parameters",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
googlePalmApigoogleSheetsOAuth2ApihttpBearerAuth
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
LinkedIn Job Search → Hiring Manager Outreach. Uses httpRequest, agent, lmChatGoogleGemini, outputParserStructured. Scheduled trigger; 38 nodes.
Source: https://gist.github.com/connectsafely/50354c0b98c6adf4732395cdb77e3213 — 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.
This workflow automates the complete blog publishing process. It removes manual work from content creation, image generation, category management, and WordPress publishing by using AI and n8n. It help
Turn a single topic into a published Instagram Carousel in minutes.
This workflow automatically creates short-form AI videos using Sora 2 Cameos, powered by n8n and AI agents.
Transform your festival marketing with this comprehensive automation workflow that creates and posts culturally authentic social media content across multiple platforms daily.
Automatically generate viral short-form health videos using AI and publish them to social platforms with n8n and Veo 3. This workflow collects viral ideas, analyzes engagement patterns, generates AI v