Hi everyone,
I am building a lead generation and outreach workflow in n8n with the following setup:
-
Data Source / Scraper: Triggering an Apify actor to gather prospect URLs/profiles.
-
AI Node (Google Gemini): Using Google Gemini (instead of OpenAI) to generate custom icebreakers/personalized intro messages based on prospect details.
-
Enrichment (Lusha): Using Lusha (instead of relying solely on LinkedIn scraper data) to pull verified direct business emails and contact details.
-
Destination: Appending the enriched records and Gemini icebreakers directly into Google Sheets.
Where I need help:
-
Ensuring n8n properly pauses/waits for the Apify run to finish before passing data downstream.
-
Correctly mapping the output fields from Apify $\rightarrow$ Google Gemini $\rightarrow$ Lusha $\rightarrow$ Google Sheets so each step receives the right parameters.
Here is my workflow blueprint:
{
“nodes”: [
{
“parameters”: {
“aggregate”: “aggregateAllItemData”,
“options”: {}
},
“id”: “161feae7-64f3-470a-8747-880024aed008”,
“name”: “Aggregate”,
“type”: “n8n-nodes-base.aggregate”,
“position”: [
1824,
240
],
“typeVersion”: 1
},
{
“parameters”: {
“formTitle”: "Audience ",
“formDescription”: “describe your audinece in plain text”,
“formFields”: {
“values”: [
{
“fieldLabel”: "descritpion of the company ",
“placeholder”: "company location,size,industry etc ",
“requiredField”: true
}
]
},
“options”: {}
},
“id”: “f72f38e9-b5b1-4481-8248-f568c8b20123”,
“name”: “Description of the audience you want to scrap”,
“type”: “n8n-nodes-base.formTrigger”,
“position”: [
368,
240
],
“webhookId”: “6aac0ee2-c7d7-427a-ad13-ff570c7f7209”,
“typeVersion”: 2.2
},
{
“parameters”: {
“modelId”: {
“__rl”: true,
“value”: “gpt-4.1-mini”,
“mode”: “list”,
“cachedResultName”: “GPT-4.1-MINI”
},
“messages”: {
“values”: [
{
“content”: “You are an expert system that translates marketing target descriptions into clean LinkedIn Sales Navigator search URLs.\n\nLook at the user’s description of who they want to scrape.\nReturn ONLY a raw JSON object containing the Sales Navigator search URL. Do not write any conversational text or explanation.\n\nUse this format:\n{\n “SearchUrl”: “Sales Navigator}”\n}\n\nRules:\n1. Replace “YOUR_KEYWORDS” with the search keywords based on the user’s input.\n2. URL-encode any spaces (use %20 instead of spaces).\n3. Do not include cacheSig, sessionId, or recentSearchId in the URL. Keep it simple and clean.”
},
{
“content”: “your task is to take as input a prospect audience and turn that into a clean LinkedIn Sales Navigator search URL. Here is an example of the URL:\nhttps://www.linkedin.com/sales/search/people?keywords=marketing%20wedding\n\nThis URL describes a search for people on Sales Navigator matching the keywords “marketing wedding”.\nYou can only make changes to the keywords field.\nReturn your response using this format: \n{“SearchUrl”:“SearchUrlgoeshere”}”
},
{
“content”: “={{ $json['descritpion of the company '] }}”
}
]
},
“jsonOutput”: true,
“options”: {}
},
“id”: “bddc1d94-d65d-4d8f-811b-81a6c1d5f88f”,
“name”: “Genrating appolo Url for apify to scrap”,
“type”: “@n8n/n8n-nodes-langchain.openAi”,
“position”: [
608,
240
],
“typeVersion”: 1.8,
“credentials”: {
“openAiApi”: {
“id”: “X9bwAhk2O6t3iMa8”,
“name”: “OpenAI account”
}
}
},
{
“parameters”: {
“method”: “POST”,
“url”: “https://api.apify.com/v2/acts/harvestapi~linkedin-profile-search/runs”,
“authentication”: “genericCredentialType”,
“genericAuthType”: “httpHeaderAuth”,
“sendHeaders”: true,
“headerParameters”: {
“parameters”: [
{
“name”: “Accept”,
“value”: “application/json”
}
]
},
“sendBody”: true,
“specifyBody”: “json”,
“jsonBody”: “={\n “searchUrl”: “{{ $json.message.content.SearchUrl }}”\n}”,
“options”: {}
},
“id”: “f85a5a7f-97b8-445d-bb39-40671595b72e”,
“name”: “Run apify actor to scrap the proscpect”,
“type”: “n8n-nodes-base.httpRequest”,
“position”: [
1008,
240
],
“typeVersion”: 4.2,
“credentials”: {
“httpHeaderAuth”: {
“id”: “EvHeJjRyMpemPQB9”,
“name”: “Header Auth account”
}
}
},
{
“parameters”: {
“modelId”: {
“__rl”: true,
“value”: “gpt-4.1-mini”,
“mode”: “list”,
“cachedResultName”: “GPT-4.1-MINI”
},
“messages”: {
“values”: [
{
“content”: "you are a helpful ",
“role”: “system”
},
{
“content”: "your task is to input a bunch of linkedin profile information of a user, and then geenrate a very short,very punchy incebreaker that i can use as a variable in my connection request \nreturn your output in this format \n{“Icebreaker”:“Icebreakergoeshere”}\nin order to ensure icebreakers are punchy and high quality, make them follow this template \nHey {{name}}, loved seeing {{thingaboutThem}}, Im also into {{plausibleTieIN}}, thought Id connect.\nFor {{thingaboutThem}}and {{plausibleTiein}}, never use the exact information provided in a linked in field. instead,always parapharse. this makes it seems human written instead of just an automated message, Also, make it super short. Dont say stuff like "“passionate about truning vision into reality " or anything like that be extremely Laconic and spartan.”
},
{
“content”: “{“LinkedInFields”:“Danielle Morgan, Fort Lauderdale, Founder & CEO at DM Creative Agency, previous experience: Regional Sales Director at Out East Eyewear, Account Director at Radancy”}\n”
},
{
“content”: "map the prospect’s LinkedIn or details from the previous node. "
}
]
},
“options”: {}
},
“id”: “6cb4458f-c921-4e9b-ad10-581b47e42c2e”,
“name”: “Genrate ice breaker by scraping linkedin data”,
“type”: “@n8n/n8n-nodes-langchain.openAi”,
“position”: [
1216,
240
],
“typeVersion”: 1.8,
“credentials”: {
“openAiApi”: {
“id”: “X9bwAhk2O6t3iMa8”,
“name”: “OpenAI account”
}
}
},
{
“parameters”: {
“operation”: “append”,
“documentId”: {
“__rl”: true,
“value”: “1Dy7-fQHGk7dASHu6VfusQCrIMqH2KJWfmSvD-4DjR4c”,
“mode”: “list”,
“cachedResultName”: “Marketinf data1”,
“cachedResultUrl”: “https://docs.google.com/spreadsheets/d/1Dy7-fQHGk7dASHu6VfusQCrIMqH2KJWfmSvD-4DjR4c/edit?usp=drivesdk”
},
“sheetName”: {
“__rl”: true,
“value”: “gid=0”,
“mode”: “list”,
“cachedResultName”: “Sheet1”,
“cachedResultUrl”: “https://docs.google.com/spreadsheets/d/1Dy7-fQHGk7dASHu6VfusQCrIMqH2KJWfmSvD-4DjR4c/edit#gid=0”
},
“columns”: {
“mappingMode”: “autoMapInputData”,
“value”: {},
“matchingColumns”: ,
“schema”: [
{
“id”: “index”,
“displayName”: “index”,
“required”: false,
“defaultMatch”: false,
“display”: true,
“type”: “string”,
“canBeUsedToMatch”: true,
“removed”: false
},
{
“id”: “message”,
“displayName”: “message”,
“required”: false,
“defaultMatch”: false,
“display”: true,
“type”: “string”,
“canBeUsedToMatch”: true,
“removed”: false
},
{
“id”: “logprobs”,
“displayName”: “logprobs”,
“required”: false,
“defaultMatch”: false,
“display”: true,
“type”: “string”,
“canBeUsedToMatch”: true,
“removed”: false
},
{
“id”: “finish_reason”,
“displayName”: “finish_reason”,
“required”: false,
“defaultMatch”: false,
“display”: true,
“type”: “string”,
“canBeUsedToMatch”: true,
“removed”: false
}
],
“attemptToConvertTypes”: false,
“convertFieldsToString”: false
},
“options”: {}
},
“id”: “423b22af-c02f-4abc-a77e-8050dcca9c90”,
“name”: “Adding ice breaker to google sheets”,
“type”: “n8n-nodes-base.googleSheets”,
“position”: [
1600,
240
],
“typeVersion”: 4.6,
“credentials”: {
“googleSheetsOAuth2Api”: {
“id”: “e4oPTe5I3052Fn7l”,
“name”: “Google Sheets account”
}
}
},
{
“parameters”: {
“method”: “POST”,
“url”: “https://api.phantombuster.com/api/v2/(your agent id goes here)/launch”,
“sendHeaders”: true,
“headerParameters”: {
“parameters”: [
{
“name”: “X-Phantombuster-Key”,
“value”: “api key goes here”
}
]
},
“options”: {}
},
“id”: “642dd7d3-a634-4cb5-b930-1021a594413d”,
“name”: “trigger phantom buster to send personlized connection request”,
“type”: “n8n-nodes-base.httpRequest”,
“position”: [
2048,
240
],
“typeVersion”: 4.2
},
{
“parameters”: {
“content”: "Create a google sheet add relevant colums name,linkedin url,company website,description and oter coloums that are genrated by the apify node and map them accordingly ",
“height”: 336
},
“id”: “9f714cd6-82dd-4e85-b076-cf01542d8395”,
“name”: “Sticky Note”,
“type”: “n8n-nodes-base.stickyNote”,
“position”: [
1536,
96
],
“typeVersion”: 1
},
{
“parameters”: {
“content”: “Triggers the phantom buster a platform that sends linkedin connection requests. to your leads, you have to go to the api credentials to get the relevant url but i hvae harcoded one for the connection request and add your api key to the header, and the agent ID of the agent you are running will be available in the url of the agent in phantombuster thats it no body is needed.”,
“height”: 384
},
“id”: “bbd5ef47-3815-4157-9269-c558e0fe7d26”,
“name”: “Sticky Note1”,
“type”: “n8n-nodes-base.stickyNote”,
“position”: [
1968,
-16
],
“typeVersion”: 1
},
{
“parameters”: {
“content”: “Hard-coded the prompt for GPT4o to generate the icebreaker; alter the prompt according to your desired need”,
“height”: 304
},
“id”: “792ddc3b-1c38-4e7b-b84e-36c7009b6689”,
“name”: “Sticky Note2”,
“type”: “n8n-nodes-base.stickyNote”,
“position”: [
1232,
96
],
“typeVersion”: 1
},
{
“parameters”: {
“content”: "Send a request to run the apify actor that scrapes the leads; everything is configuered add your API key ",
“height”: 224
},
“id”: “7b0d00de-8404-4bb5-bd9b-9201bab7ef62”,
“name”: “Sticky Note3”,
“type”: “n8n-nodes-base.stickyNote”,
“position”: [
944,
144
],
“typeVersion”: 1
},
{
“parameters”: {
“content”: "Generates an Apollo URL,using GPT-4o, which will be scraped by apify actor to get the leads data. the url is generated by the information you input in the form. ",
“height”: 352
},
“id”: “de1af1ca-e857-4e37-8786-104300f73cad”,
“name”: “Sticky Note4”,
“type”: “n8n-nodes-base.stickyNote”,
“position”: [
624,
80
],
“typeVersion”: 1
}
],
“connections”: {
“Aggregate”: {
“main”: [
[
{
“node”: “trigger phantom buster to send personlized connection request”,
“type”: “main”,
“index”: 0
}
]
]
},
“Description of the audience you want to scrap”: {
“main”: [
[
{
“node”: “Genrating appolo Url for apify to scrap”,
“type”: “main”,
“index”: 0
}
]
]
},
“Genrating appolo Url for apify to scrap”: {
“main”: [
[
{
“node”: “Run apify actor to scrap the proscpect”,
“type”: “main”,
“index”: 0
},
{
“node”: “Genrate ice breaker by scraping linkedin data”,
“type”: “main”,
“index”: 0
}
]
]
},
“Run apify actor to scrap the proscpect”: {
“main”: [
[
{
“node”: “Genrate ice breaker by scraping linkedin data”,
“type”: “main”,
“index”: 0
}
]
]
},
“Genrate ice breaker by scraping linkedin data”: {
“main”: [
[
{
“node”: “Adding ice breaker to google sheets”,
“type”: “main”,
“index”: 0
}
]
]
},
“Adding ice breaker to google sheets”: {
“main”: [
[
{
“node”: “Aggregate”,
“type”: “main”,
“index”: 0
}
]
]
}
},
“pinData”: {},
“meta”: {
“templateId”: “7182”,
“templateCredsSetupCompleted”: true,
“instanceId”: “1e78b1373da4a45ed93f6951e503328c634cef8780946387f0609e0ba62d734e”
}
}