Google Sheets + HubSpot: LinkedIn outreach stays tidy
You send the LinkedIn invites, then the real mess starts. Who accepted, who ignored you, who needs a follow-up, and which “hot lead” never made it into your CRM because you got busy.
This hits marketers running outbound campaigns first. A recruiter juggling pipelines feels it too. Same for a founder doing their own prospecting. With this LinkedIn outreach automation, your invites get tracked in Google Sheets and accepted connections get synced to HubSpot so you stop losing people in the cracks.
Below you’ll see how the workflow moves from “send invite” to “logged lead + icebreaker sent”, what you need to run it, and the easy places to customize without breaking anything.
How This Automation Works
The full n8n workflow, from trigger to final output:
n8n Workflow Template: Google Sheets + HubSpot: LinkedIn outreach stays tidy
flowchart LR
subgraph sg0["Manual Execution Start Flow"]
direction LR
n4["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>Dispatch Contact Request"]
n5@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Check Relationship Status", pos: "b", h: 48 }
n6@{ icon: "mdi:database", form: "rounded", label: "Retrieve LinkedIn Profiles", pos: "b", h: 48 }
n7@{ icon: "mdi:database", form: "rounded", label: "Populate Profile Records", pos: "b", h: 48 }
n11@{ icon: "mdi:cog", form: "rounded", label: "Extract Data from File", pos: "b", h: 48 }
n13@{ icon: "mdi:cog", form: "rounded", label: "Read Source File", pos: "b", h: 48 }
n21@{ icon: "mdi:cog", form: "rounded", label: "Run Sub-Workflow (Configure ..", pos: "b", h: 48 }
n23@{ icon: "mdi:database", form: "rounded", label: "Update Status Record", pos: "b", h: 48 }
n24["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>Fetch User LinkedIn Data"]
n25@{ icon: "mdi:play-circle", form: "rounded", label: "Manual Execution Start", pos: "b", h: 48 }
n26["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/code.svg' width='40' height='40' /></div><br/>Derive LinkedIn Username"]
n30@{ icon: "mdi:database", form: "rounded", label: "Update Status Record B", pos: "b", h: 48 }
n13 --> n11
n21 --> n30
n11 --> n6
n7 --> n5
n4 --> n23
n6 --> n26
n24 --> n7
n5 --> n4
n5 --> n21
n26 --> n24
n25 --> n13
end
subgraph sg1["Flow 2"]
direction LR
n8["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/webhook.dark.svg' width='40' height='40' /></div><br/>Incoming Webhook Listener"]
n12@{ icon: "mdi:cog", form: "rounded", label: "Extract Data from File A", pos: "b", h: 48 }
n15@{ icon: "mdi:cog", form: "rounded", label: "Read Source File A", pos: "b", h: 48 }
n18@{ icon: "mdi:database", form: "rounded", label: "Retrieve LinkedIn Profiles A", pos: "b", h: 48 }
n19@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Validate Invite Status", pos: "b", h: 48 }
n20@{ icon: "mdi:cog", form: "rounded", label: "Run Sub-Workflow (Configure ..", pos: "b", h: 48 }
n27@{ icon: "mdi:database", form: "rounded", label: "Update Status Record A", pos: "b", h: 48 }
n15 --> n12
n20 --> n27
n8 --> n15
n12 --> n18
n19 --> n20
n18 --> n19
end
subgraph sg2["Form Submission Start Flow"]
direction LR
n1["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>Generate Webhook Endpoint"]
n2["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/form.svg' width='40' height='40' /></div><br/>Form Submission Start"]
n3["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/form.svg' width='40' height='40' /></div><br/>Webhook Status Reply"]
n10@{ icon: "mdi:cog", form: "rounded", label: "Convert Data to File", pos: "b", h: 48 }
n14@{ icon: "mdi:cog", form: "rounded", label: "Write Output File", pos: "b", h: 48 }
n22@{ icon: "mdi:swap-vertical", form: "rounded", label: "Split Connection String", pos: "b", h: 48 }
n2 --> n22
n14 --> n1
n22 --> n10
n1 --> n3
n10 --> n14
end
subgraph sg3["Triggered by Another Flow Flow"]
direction LR
n0@{ icon: "mdi:play-circle", form: "rounded", label: "Triggered by Another Flow", pos: "b", h: 48 }
n9["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>Fetch Chat Participants"]
n16@{ icon: "mdi:cog", form: "rounded", label: "Extract Data from File B", pos: "b", h: 48 }
n17@{ icon: "mdi:cog", form: "rounded", label: "Read Source File B", pos: "b", h: 48 }
n28["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>Initiate Chat Session"]
n29@{ icon: "mdi:swap-vertical", form: "rounded", label: "Assign Attendee & Chat IDs", pos: "b", h: 48 }
n17 --> n16
n28 --> n9
n16 --> n28
n9 --> n29
n0 --> n17
end
%% Styling
classDef trigger fill:#e8f5e9,stroke:#388e3c,stroke-width:2px
classDef ai fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
classDef aiModel fill:#e8eaf6,stroke:#3f51b5,stroke-width:2px
classDef decision fill:#fff8e1,stroke:#f9a825,stroke-width:2px
classDef database fill:#fce4ec,stroke:#c2185b,stroke-width:2px
classDef api fill:#fff3e0,stroke:#e65100,stroke-width:2px
classDef code fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
classDef disabled stroke-dasharray: 5 5,opacity: 0.5
class n25,n2,n0 trigger
class n5,n19 decision
class n6,n7,n23,n30,n18,n27 database
class n4,n24,n8,n1,n9,n28 api
class n26 code
classDef customIcon fill:none,stroke:none
class n4,n24,n26,n8,n1,n2,n3,n9,n28 customIcon
The Problem: LinkedIn outreach gets messy fast
LinkedIn outreach looks simple until you do it at any real volume. You send a batch of invites, a few people accept, and suddenly you’re bouncing between LinkedIn, a spreadsheet, maybe a notes app, and your CRM trying to remember what happened. The worst part is the lag: someone accepts on Tuesday, you notice on Friday, and your follow-up feels cold. Manual tracking also breaks quietly. One missed copy-paste and the lead never reaches HubSpot, so your pipeline reports lie to you.
It adds up fast. Here’s where it breaks down.
- You end up checking LinkedIn acceptance status by hand, which is tedious and easy to skip on busy days.
- Contact data gets copied into Google Sheets inconsistently, so you can’t trust the sheet when you’re planning follow-ups.
- Accepted connections don’t reliably make it into HubSpot, which means sales or recruiting sequences never start.
- Icebreakers get sent late (or not at all), and that delay quietly kills reply rates.
The Solution: Track invites in Sheets, sync acceptances to HubSpot
This workflow turns LinkedIn outreach into a simple, trackable system. It starts from a form submission or another connected workflow, reads your source data, and pulls LinkedIn profile details into Google Sheets so your tracking stays clean. Then it sends the actual contact request through HTTP calls (via an external LinkedIn connector) and updates the row with the current status. When a connection is accepted, it triggers the “next best action”: the workflow prepares the message context and initiates a chat session to send your predefined icebreaker. At the same time, it can push the accepted lead into HubSpot so your CRM stays aligned with what’s happening on LinkedIn.
The workflow begins when you submit a lead (or run it manually for a batch). From there it enriches and normalizes profile data, dispatches invites, and keeps checking relationship or invite status. Once accepted, it kicks off the follow-up message and updates your records so you always know where things stand.
What You Get: Automation vs. Results
| What This Workflow Automates | Results You’ll Get |
|---|---|
|
|
Example: What This Looks Like
Say you send 30 LinkedIn invites a day. Manually, you might spend about 2 minutes per invite to copy profile info into Google Sheets, then another 10 minutes later checking who accepted and drafting quick icebreakers, which puts you at roughly an hour a day. With this workflow, adding the list can take about 10 minutes, then n8n handles dispatch, logging, and status updates in the background. When acceptances come in, the icebreaker and HubSpot sync happen automatically, so you’re mostly just handling replies.
What You’ll Need
- n8n instance (try n8n Cloud free)
- Self-hosting option if you prefer (Hostinger works well)
- Google Sheets to store invite status and profile data
- HubSpot to store accepted connections as leads/contacts
- Unipile account + API key (get it from your Unipile dashboard)
Skill level: Intermediate. You’ll connect accounts, paste an API key, and adjust a few fields to match your sheet and HubSpot properties.
Don’t want to set this up yourself? Talk to an automation expert (free 15-minute consultation).
How It Works
A lead gets submitted. The workflow can start from a form trigger, a webhook, or a “run now” manual execution when you want to process a batch.
Profiles are pulled into your tracking sheet. It reads a source file, extracts the data, then looks up LinkedIn profiles and populates Google Sheets with consistent fields (so you aren’t cleaning columns later).
Invites are sent and statuses are monitored. Using HTTP requests, the workflow dispatches contact requests, checks relationship status, and updates the Google Sheet rows as things change. The If and Switch logic keeps accepted, pending, and failed cases separated so you can trust what you’re seeing.
Accepted connections get a follow-up and can be synced to HubSpot. Once an invite is accepted, the workflow initiates a chat session, fetches participant details, and sends your predefined icebreaker. From there, HubSpot nodes can push the contact into your CRM so sales or recruiting follow-ups start immediately.
You can easily modify the message copy and the sheet columns to match your needs. See the full implementation guide below for customization options.
Step-by-Step Implementation Guide
Step 1: Configure the Webhook Trigger
This workflow can start from multiple entry points. Set up each trigger to match how you want to initiate outreach.
- Open Form Submission Start and publish the form to obtain its public URL.
- Open Incoming Webhook Listener and copy the production webhook URL for external callbacks.
- Keep Triggered by Another Flow enabled if this workflow is called via Execute Workflow from another automation.
- Use Manual Execution Start for testing-only runs that read from local source files.
Step 2: Connect Google Sheets
Several nodes read and update LinkedIn profile data and status records. Configure Google Sheets once and reuse it across all sheets nodes.
- Open Retrieve LinkedIn Profiles and select your target spreadsheet and worksheet.
- Repeat the spreadsheet mapping for Retrieve LinkedIn Profiles A, Populate Profile Records, Update Status Record, Update Status Record A, and Update Status Record B.
- Credential Required: Connect your Google Sheets credentials to all Google Sheets nodes (6 total).
Step 3: Set Up File Ingestion and Parsing
Local file reads and extraction steps load LinkedIn data before it is pushed to Sheets and APIs.
- Configure Read Source File, Read Source File A, and Read Source File B with the correct file paths or binary inputs.
- Ensure the extraction nodes Extract Data from File, Extract Data from File A, and Extract Data from File B are set to parse the correct file format.
- Verify the output mapping into Retrieve LinkedIn Profiles and Retrieve LinkedIn Profiles A aligns with your spreadsheet column names.
Step 4: Configure LinkedIn Data Enrichment
This stage derives usernames and fetches LinkedIn details for outreach decisions.
- Review Derive LinkedIn Username and confirm the code logic correctly parses usernames from your input.
- Configure Fetch User LinkedIn Data to call your LinkedIn API provider endpoint.
- Credential Required: Connect the appropriate API credentials in Fetch User LinkedIn Data (HTTP authentication depends on your LinkedIn data provider).
- Confirm Populate Profile Records writes enriched profile fields back to your spreadsheet.
Step 5: Set Up Outreach Logic and Branching
The outreach path is determined by relationship and invite status conditions.
- Configure Check Relationship Status with the exact field and condition to decide whether a contact request should be sent.
- Note that Check Relationship Status outputs to both Dispatch Contact Request and Run Sub-Workflow (Configure Required) 2 depending on condition outcomes.
- Configure Validate Invite Status to determine when to hand off to Run Sub-Workflow (Configure Required).
- Open Run Sub-Workflow (Configure Required) and Run Sub-Workflow (Configure Required) 2 and select the target sub-workflow to execute.
Step 6: Configure Outreach Actions and Status Updates
These nodes send requests, update statuses, and keep records in sync.
- Configure Dispatch Contact Request to send the LinkedIn connection or outreach request via your API endpoint.
- Credential Required: Connect the appropriate API credentials in Dispatch Contact Request (HTTP authentication depends on your outreach provider).
- Ensure Update Status Record, Update Status Record A, and Update Status Record B update the correct row IDs and fields.
Outreach Status) so all three status update nodes write consistently.Step 7: Configure Chat Session and Webhook Output Flow
This section prepares chat sessions and provides webhook feedback to external systems.
- Configure Initiate Chat Session to create a chat for outreach follow-up.
- Configure Fetch Chat Participants to retrieve participant IDs and pass them to Assign Attendee & Chat IDs.
- Set up Split Connection String and Convert Data to File to format webhook payloads as files for response handling.
- Confirm Write Output File stores the generated payload and triggers Generate Webhook Endpoint → Webhook Status Reply.
- Credential Required: If your chat or webhook APIs require authentication, add credentials directly inside Initiate Chat Session, Fetch Chat Participants, and Generate Webhook Endpoint.
Step 8: Test and Activate Your Workflow
Run end-to-end tests for each entry path before activating the workflow in production.
- Click Execute Workflow starting from Manual Execution Start to validate file parsing and Sheets updates.
- Submit a test entry to Form Submission Start and verify Webhook Status Reply returns a valid response.
- Send a test payload to Incoming Webhook Listener and confirm the chat session flow completes through Assign Attendee & Chat IDs.
- Verify rows are updated in Update Status Record, Update Status Record A, and Update Status Record B to confirm success.
- Once verified, toggle the workflow to Active to enable production use.
Common Gotchas
- Unipile credentials can expire or need specific permissions. If things break, check your Unipile dashboard tokens and the API access settings first.
- If you’re using Wait nodes or external processing, timings vary. Bump up the wait duration if downstream HTTP steps fail because a status update hasn’t arrived yet.
- Default icebreaker messages are usually bland. Edit the message text early and match your tone, otherwise you’ll be rewriting “automated” outreach one message at a time.
Frequently Asked Questions
About an hour if your Google Sheet, HubSpot, and Unipile account are ready.
No. You’ll mostly connect accounts and map fields between Google Sheets and HubSpot.
Yes. n8n has a free self-hosted option and a free trial on n8n Cloud. Cloud plans start at $20/month for higher volume. You’ll also need to factor in Unipile costs (their plan + API usage), and of course any HubSpot tier you’re on.
Two options: n8n Cloud (managed, easiest setup) or self-hosting on a VPS. For self-hosting, Hostinger VPS is affordable and handles n8n well. Self-hosting gives you unlimited executions but requires basic server management.
Yes, but plan it. The workflow can be duplicated per account, or you can add an “account” column in Google Sheets and route requests through a Switch node to the correct Unipile credentials. Common tweaks include per-account daily limits, different icebreakers by campaign, and separate HubSpot pipelines per account. Also note LinkedIn limits: a free account is typically capped around 150 invitations per week.
Usually it’s an expired or replaced API token in Unipile, so regenerate the key and update it in n8n. Permissions can also be the culprit if the Unipile workspace doesn’t allow the endpoints you’re calling. And if you’re pushing higher volume, you might be hitting rate limits, which shows up as intermittent HTTP errors rather than a clean “login failed” message.
It can handle LinkedIn’s limits, not magic. A free LinkedIn account is typically limited to about 150 invitations per week, and you can spread volume across multiple accounts if you operate that way. On n8n Cloud, your cap is executions per month (Starter is fine for modest daily outreach), while self-hosting removes the execution ceiling and shifts the constraint to your server resources.
Often, yes, because this workflow leans on webhooks, conditional logic, and file handling, and that gets awkward (and expensive) in many no-code tools. n8n also gives you more control over branching, retries, and “what happens if status is still pending.” The catch is hosting: this specific template uses file system nodes, which frankly pushes you toward self-hosting. If you only need a simple “accepted connection → create HubSpot contact” flow, Zapier or Make can be quicker to start. Talk to an automation expert if you want help choosing the cleanest setup.
Once this is running, your sheet stays truthful and HubSpot stays current. That’s real consistency, without you babysitting LinkedIn all week.
Need Help Setting This Up?
Our automation experts can build and customize this workflow for your specific needs. Free 15-minute consultation—no commitment required.