Apollo to HubSpot, enriched leads sent via Gmail
Your cold outreach stack breaks in the boring places. A great lead slips through because the email wasn’t verified, the CRM wasn’t updated, or the “personalized” message was copied from the last campaign and tweaked in a rush.
This Apollo HubSpot automation hits Sales Ops first (they own the data). But founders doing their own outbound and agency teams running campaigns for clients feel it just as much. You get enriched leads in HubSpot and verified Gmail outreach sent automatically, without babysitting spreadsheets.
Below, you’ll see exactly what the workflow does, what you need to run it, and what results to expect when you press run and let n8n handle the repetitive parts.
How This Automation Works
Here’s the complete workflow you’ll be setting up:
n8n Workflow Template: Apollo to HubSpot, enriched leads sent via Gmail
flowchart LR
subgraph sg0["Form Intake Flow"]
direction LR
n0["<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 Intake Trigger"]
n1@{ icon: "mdi:brain", form: "rounded", label: "Primary AI Chat Model", pos: "b", h: 48 }
n2@{ icon: "mdi:robot", form: "rounded", label: "Apollo Search URL Builder", pos: "b", h: 48 }
n3@{ icon: "mdi:cog", form: "rounded", label: "Cap Records", pos: "b", h: 48 }
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/>Fetch Profile Details"]
n5@{ icon: "mdi:brain", form: "rounded", label: "Secondary AI Chat Model", pos: "b", h: 48 }
n6@{ icon: "mdi:robot", form: "rounded", label: "Structured Response Parser", pos: "b", h: 48 }
n7["<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/>Send LinkedIn Invite"]
n8@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Connection Status Check", pos: "b", h: 48 }
n9@{ icon: "mdi:message-outline", form: "rounded", label: "Dispatch Email Message", pos: "b", h: 48 }
n10["<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/>Validate Email Address"]
n11@{ icon: "mdi:robot", form: "rounded", label: "Compose Outreach Content", pos: "b", h: 48 }
n12@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Email Validity Branch", pos: "b", h: 48 }
n13@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Email Present Check", pos: "b", h: 48 }
n14["<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/hubspot.svg' width='40' height='40' /></div><br/>Upsert CRM Contact"]
n15["<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/>Retrieve Company Profile"]
n16["<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/>Execute Apify Scrape"]
n17@{ icon: "mdi:swap-vertical", form: "rounded", label: "Derive Public IDs", pos: "b", h: 48 }
n12 --> n9
n3 --> n17
n16 --> n3
n13 --> n10
n10 --> n12
n8 --> n7
n4 --> n15
n15 --> n14
n11 --> n8
n11 --> n13
n1 -.-> n2
n0 --> n2
n5 -.-> n11
n17 --> n4
n2 --> n16
n6 -.-> n11
n14 --> n11
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 n0 trigger
class n2,n6,n11 ai
class n1,n5 aiModel
class n8,n12,n13 decision
class n4,n7,n10,n15,n16 api
classDef customIcon fill:none,stroke:none
class n0,n4,n7,n10,n14,n15,n16 customIcon
Why This Matters: cold outreach falls apart between tools
Cold outreach is rarely “hard” because you can’t find leads. It’s hard because the work is scattered. You define an ICP in one place, scrape leads somewhere else, enrich LinkedIn manually, paste details into HubSpot, then write emails one-by-one and hope the addresses don’t bounce. And when you’re moving fast, you skip steps. That’s when deliverability tanks, CRM data gets messy, and follow-ups become guesswork because you can’t trust what’s logged.
It adds up fast. Here’s where the process usually breaks down.
- You end up rewriting the same “personalized” email blocks, and it still reads generic.
- HubSpot doesn’t match reality because updates happen after the fact (or not at all).
- Unverified emails lead to bounces and warnings, which means your next campaigns struggle too.
- LinkedIn outreach becomes inconsistent because connection requests are done only when someone remembers.
What You’ll Build: Apollo leads enriched, synced, and contacted
This workflow turns a simple prospect definition into a complete outreach cycle. You start by entering the basics (job titles, company size, keywords, locations). OpenAI converts that into a clean Apollo search URL, then Apify runs an Apollo scraper to pull lead details (capped to 10 per run so you can test safely). Next, the workflow enriches each prospect using LinkedIn and company profile lookups via Unipile and supporting HTTP requests. Then it upserts the contact into HubSpot, so your CRM stays current before outreach happens. Finally, OpenAI generates a short LinkedIn connection request plus a personalized email subject and body, the email is validated in ZeroBounce, and verified addresses get an outreach email sent via Gmail.
It starts with a form submission. Then enrichment and CRM sync happen automatically, so the messaging step has real context. Last, the workflow branches: it sends LinkedIn invites if you’re not connected, and it only emails when ZeroBounce says the address is safe enough to use.
What You’re Building
| What Gets Automated | What You’ll Achieve |
|---|---|
|
|
Expected Results
Say you run two batches per day, 10 leads each. Manually, you might spend about 5 minutes per lead exporting, enriching, updating HubSpot, writing an email, and double-checking deliverability, which is roughly 100 minutes daily. With this workflow, you submit the form in about 5 minutes total, let the scrape/enrichment run in the background, and only review exceptions. For most teams, that’s about an hour back per day, plus fewer “oops” moments from emailing risky addresses.
Before You Start
- n8n instance (try n8n Cloud free)
- Self-hosting option if you prefer (Hostinger works well)
- Apollo.io to source prospects via search.
- Apify to run the Apollo scraper task.
- Unipile for LinkedIn enrichment and invites.
- HubSpot to create/update CRM contacts.
- ZeroBounce API key (get it from your ZeroBounce dashboard)
- OpenAI API key (get it from the OpenAI API settings page)
- Gmail account to send outreach from your inbox.
- Google Sheets for optional logging and review.
Skill level: Intermediate. You’ll connect several accounts, add API keys, and do a quick test run to confirm each branch behaves correctly.
Want someone to build this for you? Talk to an automation expert (free 15-minute consultation).
Step by Step
Prospect inputs trigger the run. You submit a form with your target roles, company filters, keywords, and locations. That becomes the single “source of truth” for the batch.
AI turns that into an Apollo search. OpenAI generates a precise Apollo search URL based on what you entered, then the workflow hands it to Apify to scrape lead data. A limit node caps the batch (10 leads) so you can iterate without burning credits.
Enrichment and CRM sync happen before messaging. The workflow derives public IDs, fetches profile details, retrieves company profile context, and upserts each contact in HubSpot. When you later look at a contact, you’re not staring at blanks.
Outreach is generated, then gated by validation. OpenAI writes a short LinkedIn connection request (under 300 characters) plus an email subject and body with a soft CTA. If there’s an email address, ZeroBounce validates it. Verified or catch-all can proceed depending on how you configure the “Email Validity” branch.
LinkedIn and Gmail actions fire only when appropriate. If you’re not already connected, the Unipile branch sends the LinkedIn invite. And when the email is verified, Gmail sends the outreach message automatically.
You can easily modify the lead cap (10 per run) to match your daily volume, and adjust the validation rules to be stricter or looser based on your domain health. See the full implementation guide below for customization options.
Step-by-Step Implementation Guide
Step 1: Configure the Form Trigger
Set up the intake form that initiates the workflow and captures the lead search criteria.
- Add and open Form Intake Trigger.
- Set Form Title to
Apollo + Apify Lead Generation. - Set Form Description to
Describe the types of leads you are trying to find. - Configure fields for Job Title, Company Size, Keywords, and Location as shown in the node.
Step 2: Connect the AI URL Builder
Use AI to convert form inputs into an Apollo search URL.
- Open Primary AI Chat Model and select Credential Required: Connect your
openAiApicredentials. - Ensure the model is set to
gpt-4.1. - Open Apollo Search URL Builder and set Text to
=Job Title: {{ $json['Job Title'] }} Company Size: {{ $json['Company Size'] }} Keywords: {{ $json.Keywords }} Location: {{ $json.Location }}. - Confirm the detailed instruction prompt remains in the Messages field to generate proper Apollo URL parameters.
Tip: The URL builder relies on the exact form field labels, so keep labels like Job Title and Company Size unchanged.
Step 3: Configure Data Collection and Enrichment
Scrape Apollo results, cap record volume, derive LinkedIn IDs, and fetch profile and company details.
- Open Execute Apify Scrape and set URL to
https://api.apify.com/v2/acts/code_crafter~apollo-io-scraper/run-sync-get-dataset-items. - Set JSON Body to
={ "cleanOutput": false, "url": "{{ $json.text }}" }and set header Authorization toBearer [CONFIGURE_YOUR_TOKEN]. - Open Cap Records and set Max Items to
10. - Open Derive Public IDs and set user to
={{ $json.linkedin_url.split("/").filter(Boolean).pop() }}and company_url to={{ $json.organization.linkedin_url.split("/").filter(Boolean).pop() }}. - In Fetch Profile Details, set URL to
=https://<subdomain>.unipile.com:<port>/api/v1/users/{{ $json.user }}and add headerX-API-KEYwith[CONFIGURE_YOUR_API_KEY]plus queryaccount_idwith[YOUR_ID]. - In Retrieve Company Profile, set URL to
=https://<subdomain>.unipile.com:<port>/api/v1/linkedin/company/{{ $('Derive Public IDs').item.json.company_url }}and add headerX-API-KEYwith[CONFIGURE_YOUR_API_KEY]plus queryaccount_idwith[YOUR_ID].
⚠️ Common Pitfall: The HTTP nodes (Execute Apify Scrape, Fetch Profile Details, Retrieve Company Profile, Validate Email Address, Send LinkedIn Invite) require valid API keys/tokens in headers or query parameters even though no credentials are configured.
Step 4: Set Up CRM Upsert and AI Outreach Generation
Create or update the HubSpot contact and generate structured outreach content via AI.
- Open Upsert CRM Contact and select Credential Required: Connect your
hubspotAppTokencredentials. - Set Email to
={{ $('Execute Apify Scrape').item.json.email }}and confirm Authentication isappToken. - Review additional fields like companyName, websiteUrl, and industry to ensure expressions pull from Execute Apify Scrape.
- Open Secondary AI Chat Model and select Credential Required: Connect your
openAiApicredentials, modelgpt-4.1-mini. - Open Compose Outreach Content and set Text to
=first_name : {{ $('Fetch Profile Details').item.json.first_name }} company_name : {{ $('Execute Apify Scrape').item.json.organization_name }} company_website: {{ $('Execute Apify Scrape').item.json.organization_website_url }} Industry: {{ $('Execute Apify Scrape').item.json.organization.industry }} company_description: {{ $('Retrieve Company Profile').item.json.description }}. - Ensure Structured Response Parser is connected as the output parser for Compose Outreach Content. Credentials, if needed, must be added to Compose Outreach Content, not the parser.
Step 5: Configure Conditional Routing and Outreach Actions
Route leads through LinkedIn invite or email flows based on relationship and email validity.
- In Connection Status Check, set the condition to
={{ $('Fetch Profile Details').item.json.is_relationship }}with booleanfalseto determine invite eligibility. - In Send LinkedIn Invite, set URL to
https://<subdomain>.unipile.com:<port>/api/v1/users/inviteand includeX-API-KEYin headers andaccount_idin the body. - In Email Present Check, verify it checks
={{ $('Append or update row in sheet').item.json.email }}(update this expression if your email source node differs). - In Validate Email Address, set query parameters
api_keyto[CONFIGURE_YOUR_API_KEY]andemailto={{ $('Execute Apify Scrape').item.json.email }}. - In Email Validity Branch, keep the conditions for
validandcatch-allstatuses. - Open Dispatch Email Message and select Credential Required: Connect your
gmailOAuth2credentials. - Set Send To to
={{ $('Execute Apify Scrape').item.json.email }}, Subject to={{ $('Compose Outreach Content').item.json.output.email.subject }}, and Message to={{ $('Compose Outreach Content').item.json.output.email.body }}.
Tip: Compose Outreach Content outputs to both Connection Status Check and Email Present Check in parallel, so both LinkedIn and email paths can evaluate simultaneously.
Step 6: Test and Activate Your Workflow
Verify the flow end-to-end and then enable it for production use.
- Click Execute Workflow and submit a test entry through Form Intake Trigger.
- Confirm Apollo Search URL Builder generates a valid Apollo URL and Execute Apify Scrape returns lead data.
- Verify Upsert CRM Contact creates/updates a HubSpot contact, and Compose Outreach Content returns structured JSON.
- Check that either Send LinkedIn Invite or Dispatch Email Message fires based on the IF node logic.
- Once successful, toggle the workflow to Active for production use.
Troubleshooting Tips
- HubSpot credentials can expire or lack scopes. If syncing fails, check your HubSpot private app permissions and then reconnect the account in n8n.
- If you’re using Wait-like timing (or Apify scrape runtime varies), processing times will fluctuate. Increase the delay or add a re-check if downstream nodes sometimes receive empty lead data.
- OpenAI prompts ship generic by default. Add your positioning, taboo phrases, and CTA style early, or you’ll be stuck rewriting “friendly check-ins” forever.
Quick Answers
Plan for about an hour if you already have the accounts and API keys.
No. You’ll mostly connect services and paste API keys. The logic is already built into the workflow branches.
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 OpenAI usage plus ZeroBounce verification credits and any Apify runs.
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, and you probably should. You can tighten or loosen the Email Validity Branch rules (for example, only “valid” emails, or allow “catch-all”), increase the Cap Records limit, and swap the messaging style inside Compose Outreach Content to match your brand voice. If you don’t want LinkedIn invites, disable the Send LinkedIn Invite path and keep email only. If HubSpot isn’t your CRM, the Upsert CRM Contact step is the one you’d replace.
Most of the time it’s permissions. Recheck the scopes on your HubSpot private app, then reconnect the HubSpot credential inside n8n so the token updates. Also confirm the workflow is sending required fields (like email or name) into the upsert step, because empty fields can cause confusing errors. If you’re running big batches later, rate limits can show up too, so spacing requests out can help.
As shipped, it’s capped at 10 leads per run, but you can raise that. On self-hosted n8n there’s no execution cap (your server is the limit), while n8n Cloud limits executions by plan. Practically, your bottlenecks are Apify runtime, Unipile/LinkedIn constraints, and how many emails you should safely send per day from Gmail without hurting your domain.
Usually, yes, if you care about branching logic and controlling the “only send when verified” rules. n8n is also more comfortable when you need multiple HTTP steps, enrichment calls, and conditional paths without paying extra for every filter. Zapier or Make can still be fine for a basic “new lead → create contact → send email” chain. The moment you add LinkedIn enrichment, AI copy, and email validation gates, things get messy. If you want help choosing, Talk to an automation expert and sanity-check your exact use case.
Once this is running, your outreach pipeline stops depending on reminders and copy-paste. You get cleaner HubSpot data, safer sending, and a process you can repeat next week without dreading it.
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.