This workflow corresponds to n8n.io template #16004 — we link there as the canonical source.
This workflow follows the Gmail → 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 →
{
"id": "VZPqtqdqOEj9Nv1v",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "AI-Powered HubSpot Deal Follow-Up Automation",
"tags": [
{
"id": "4Of2LE0HSt4qmmUG",
"name": "Ansh",
"createdAt": "2026-04-20T10:32:01.726Z",
"updatedAt": "2026-04-20T10:32:01.726Z"
}
],
"nodes": [
{
"id": "a7983f9e-fdc7-43ac-80f0-c5ce6d3dd983",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
16,
656
],
"parameters": {
"width": 512,
"height": 656,
"content": "## AI-Powered HubSpot Deal Follow-Up Automation\nThis workflow automatically starts a personalised follow-up email sequence the moment a new deal is created in HubSpot. It validates the deal, enriches the CRM, sends a timed 3-email sequence, detects replies, updates the deal stage, notifies your sales rep on Slack, and logs everything to Google Sheets. It keeps every new deal warm and fully tracked without any manual effort.\n\n### How it works\n\n\t\u2022\tIt watches HubSpot for any newly created deal and validates the contact data.\n\t\u2022\tIt sends a personalised intro email immediately and updates the deal stage to Contacted.\n\t\u2022\tIt waits 2 days and sends a value-focused follow-up email.\n\t\u2022\tIt re-fetches the contact to check for a fresh reply signal after 5 days.\n\t\u2022\tIf replied, it alerts the sales rep on Slack and updates the deal stage to Meeting Booked.\n\t\u2022\tIf no reply, it sends a breakup email and marks the deal as Closed Lost.\n\t\u2022\tAll outcomes are logged to Google Sheets for pipeline reporting.\n\n### Setup Steps\n\n\t1.\tConnect your HubSpot account (OAuth2).\n\t2.\tConnect your Gmail account.\n\t3.\tConnect your Slack account and set your sales rep channel.\n\t4.\tConnect Google Sheets and create a log sheet with columns: Date, Deal Name, Contact Name, Contact Email, Sequence Status.\n\t5.\tTurn on the workflow."
},
"typeVersion": 1
},
{
"id": "7b599041-51c0-4060-a054-853f19949a21",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
544,
656
],
"parameters": {
"color": 7,
"width": 640,
"height": 656,
"content": "## Step 1: Detect New Deal in HubSpot\nThis step watches your HubSpot account and fires the moment a new deal is created. It then pulls the full deal record and its associated contact details so the rest of the workflow knows exactly who to reach out to."
},
"typeVersion": 1
},
{
"id": "589ecba2-6a18-4b91-8c05-bca7c3434e87",
"name": "Sticky Note Validate",
"type": "n8n-nodes-base.stickyNote",
"position": [
1200,
656
],
"parameters": {
"color": 7,
"width": 496,
"height": 656,
"content": "## Step 2: Validate Deal Data\nBefore sending any email, this step checks that the deal has a linked contact and a valid email address. If the data is missing or incomplete, the workflow stops cleanly and logs a warning \u2014 preventing any broken or embarrassing outreach."
},
"typeVersion": 1
},
{
"id": "26d447ad-e136-44cd-bc5c-e7b6f291d0a5",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
1712,
656
],
"parameters": {
"color": 7,
"width": 416,
"height": 656,
"content": "## Step 3: Send Intro Email and Update CRM\nThis step sends the first personalised email to the contact straight away. It then immediately updates the HubSpot deal stage to Contacted so your pipeline reflects the outreach in real time."
},
"typeVersion": 1
},
{
"id": "438828ea-552c-4a89-8a8d-5654dee4a59a",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
2144,
656
],
"parameters": {
"color": 7,
"width": 800,
"height": 656,
"content": "## Step 4: Wait and Send Follow-Up Email\nAfter 2 days the workflow automatically sends a second email packed with value. It waits another 3 days and then re-fetches the contact record fresh from HubSpot to get an up-to-date reply signal before making any decision."
},
"typeVersion": 1
},
{
"id": "cbf14b0b-37d0-4ab0-8e97-7eb53ec9cb61",
"name": "Sticky Note Reply",
"type": "n8n-nodes-base.stickyNote",
"position": [
2960,
656
],
"parameters": {
"color": 7,
"width": 672,
"height": 656,
"content": "## Step 5: Reply Detection and Branching\nThis step checks the freshly fetched contact data to see if they have replied. If they have, it alerts your sales rep on Slack and moves the deal to Meeting Booked. If they have not, it sends a final breakup email and marks the deal as Closed Lost."
},
"typeVersion": 1
},
{
"id": "0e3560ce-47c8-4658-ba67-09c9a5fdac93",
"name": "Sticky Note Log",
"type": "n8n-nodes-base.stickyNote",
"position": [
3648,
656
],
"parameters": {
"color": 7,
"width": 544,
"height": 656,
"content": "## Step 6: Log Outcome\nEvery outcome \u2014 replied, no reply, invalid data \u2014 is written to a Google Sheet for pipeline reporting."
},
"typeVersion": 1
},
{
"id": "28e47d68-4d62-45d9-bc3c-efef08a7f9f2",
"name": "HubSpot Trigger \u2013 New Deal",
"type": "n8n-nodes-base.hubspotTrigger",
"position": [
608,
1024
],
"parameters": {
"additionalFields": {}
},
"typeVersion": 1
},
{
"id": "ea6e75ba-7443-4b6f-9d8c-db9fb5f2d0a1",
"name": "Get Deal Details",
"type": "n8n-nodes-base.hubspot",
"position": [
832,
1024
],
"parameters": {
"dealId": "={{ $json.objectId }}",
"filters": {},
"resource": "deal",
"operation": "get"
},
"typeVersion": 2
},
{
"id": "42fca930-14e2-40e7-a9ef-655e780891ec",
"name": "Get Contact Details",
"type": "n8n-nodes-base.hubspot",
"position": [
1056,
1024
],
"parameters": {
"contactId": "={{ $json.associations.contacts.results[0].id }}",
"operation": "get",
"additionalFields": {}
},
"typeVersion": 2
},
{
"id": "eb70933f-6657-4085-b34f-0cd2d12f1961",
"name": "Valid Contact & Email?",
"type": "n8n-nodes-base.if",
"position": [
1280,
1024
],
"parameters": {
"conditions": {
"string": [
{
"value1": "={{ $json.properties.email }}",
"operation": "isNotEmpty"
}
],
"boolean": [
{
"value1": "={{ $('Get Deal Details').item.json.associations.contacts.results.length > 0 }}",
"value2": true
}
]
}
},
"typeVersion": 1
},
{
"id": "db178eca-a7f8-43ed-af7b-397c8fa04b22",
"name": "Log Invalid \u2013 Skip",
"type": "n8n-nodes-base.googleSheets",
"position": [
1504,
1120
],
"parameters": {
"columns": {
"value": {
"Date": "={{ $now.toISO() }}",
"Deal Name": "={{ $('Get Deal Details').item.json.properties.dealname }}",
"Contact Name": "",
"Contact Email": "",
"Sequence Status": "SKIPPED \u2013 Missing Contact or Email"
},
"mappingMode": "defineBelow"
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "REPLACE_WITH_YOUR_SHEET_ID"
}
},
"typeVersion": 4
},
{
"id": "c84d7f3c-2d01-417e-bde8-024ba46a31dd",
"name": "Send Intro Email",
"type": "n8n-nodes-base.gmail",
"position": [
1760,
928
],
"parameters": {
"sendTo": "={{ $('Get Contact Details').item.json.properties.email }}",
"message": "=<p>Hi {{ $('Get Contact Details').item.json.properties.firstname }},</p><p>Thanks for your interest \u2014 we just created a deal for <strong>{{ $('Get Deal Details').item.json.properties.dealname }}</strong> and I wanted to reach out personally.</p><p>I'd love to understand your goals better and show you exactly how we can help. Are you free for a quick 15-minute call this week?</p><p>Best,<br>Your Sales Team</p>",
"options": {},
"subject": "=Great to connect, {{ $('Get Contact Details').item.json.properties.firstname }}!"
},
"typeVersion": 2
},
{
"id": "3aa648b0-28a3-4894-b0ed-3fcf624c56cf",
"name": "Update Deal Stage \u2013 Contacted",
"type": "n8n-nodes-base.hubspot",
"position": [
1984,
928
],
"parameters": {
"dealId": "={{ $('Get Deal Details').item.json.id }}",
"resource": "deal",
"operation": "update",
"updateFields": {}
},
"typeVersion": 2
},
{
"id": "653101b3-f442-463c-8061-562faaa78ec9",
"name": "Wait 2 Days",
"type": "n8n-nodes-base.wait",
"position": [
2176,
928
],
"parameters": {
"unit": "days",
"amount": 2
},
"typeVersion": 1
},
{
"id": "e8802fcd-ced6-4b16-812f-7236606178be",
"name": "Send Follow-Up Email",
"type": "n8n-nodes-base.gmail",
"position": [
2368,
928
],
"parameters": {
"sendTo": "={{ $('Get Contact Details').item.json.properties.email }}",
"message": "=<p>Hi {{ $('Get Contact Details').item.json.properties.firstname }},</p><p>Just following up on <strong>{{ $('Get Deal Details').item.json.properties.dealname }}</strong>. I wanted to share a quick win our clients typically see in the first 30 days.</p><p>[Insert your value prop / case study here]</p><p>Happy to walk you through it \u2014 just reply and we'll find a time.</p><p>Best,<br>Your Sales Team</p>",
"options": {},
"subject": "=One thing that might help, {{ $('Get Contact Details').item.json.properties.firstname }}"
},
"typeVersion": 2
},
{
"id": "6e72bc12-a886-4872-a621-146b45ebeb97",
"name": "Wait 3 More Days",
"type": "n8n-nodes-base.wait",
"position": [
2576,
928
],
"parameters": {
"unit": "days",
"amount": 3
},
"typeVersion": 1
},
{
"id": "0dc0def0-62e6-42ed-acf5-2ef8c151d7a6",
"name": "Re-fetch Contact (Fresh Data)",
"type": "n8n-nodes-base.hubspot",
"position": [
2784,
928
],
"parameters": {
"contactId": "={{ $('Get Contact Details').item.json.id }}",
"operation": "get",
"additionalFields": {}
},
"typeVersion": 2
},
{
"id": "07078141-4905-4a2b-911d-cb785063dbeb",
"name": "Has Contact Replied?",
"type": "n8n-nodes-base.if",
"position": [
3008,
928
],
"parameters": {
"conditions": {
"string": [
{
"value1": "={{ $json.properties.hs_email_last_reply_date }}",
"operation": "isNotEmpty"
}
]
}
},
"typeVersion": 1
},
{
"id": "87bc7cca-c66b-4619-a56b-15f39fded439",
"name": "Slack Alert \u2013 Contact Replied",
"type": "n8n-nodes-base.slack",
"position": [
3248,
832
],
"parameters": {
"text": "=:tada: *Deal Reply Detected!*\n\n*Deal:* {{ $('Get Deal Details').item.json.properties.dealname }}\n*Contact:* {{ $('Re-fetch Contact (Fresh Data)').item.json.properties.firstname }} {{ $('Re-fetch Contact (Fresh Data)').item.json.properties.lastname }}\n*Email:* {{ $('Re-fetch Contact (Fresh Data)').item.json.properties.email }}\n*Last Reply:* {{ $('Re-fetch Contact (Fresh Data)').item.json.properties.hs_email_last_reply_date }}\n\nTime to follow up and book a meeting!",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "id",
"value": "REPLACE_WITH_YOUR_SLACK_CHANNEL_ID"
},
"otherOptions": {}
},
"typeVersion": 2
},
{
"id": "d5c2d92b-a18a-45eb-9dbf-a1ee762171cd",
"name": "Update Deal Stage \u2013 Meeting Booked",
"type": "n8n-nodes-base.hubspot",
"position": [
3456,
832
],
"parameters": {
"dealId": "={{ $('Get Deal Details').item.json.id }}",
"resource": "deal",
"operation": "update",
"updateFields": {}
},
"typeVersion": 2
},
{
"id": "1567540e-59a6-4e9e-acaa-16882a83f8ab",
"name": "Send Breakup Email",
"type": "n8n-nodes-base.gmail",
"position": [
3248,
1024
],
"parameters": {
"sendTo": "={{ $('Re-fetch Contact (Fresh Data)').item.json.properties.email }}",
"message": "=<p>Hi {{ $('Re-fetch Contact (Fresh Data)').item.json.properties.firstname }},</p><p>I wanted to send one last note regarding <strong>{{ $('Get Deal Details').item.json.properties.dealname }}</strong>. I'll assume the timing isn't right for now \u2014 no worries at all.</p><p>If anything changes or you'd like to revisit, my inbox is always open.</p><p>Wishing you all the best,<br>Your Sales Team</p>",
"options": {},
"subject": "=Closing the loop, {{ $('Re-fetch Contact (Fresh Data)').item.json.properties.firstname }}"
},
"typeVersion": 2
},
{
"id": "80a766fc-2489-4418-a688-6e21c4639f2d",
"name": "Update Deal Stage \u2013 Closed Lost",
"type": "n8n-nodes-base.hubspot",
"position": [
3456,
1024
],
"parameters": {
"dealId": "={{ $('Get Deal Details').item.json.id }}",
"resource": "deal",
"operation": "update",
"updateFields": {}
},
"typeVersion": 2
},
{
"id": "7242194b-f8a3-4207-a126-4ebb8ee9491e",
"name": "Log to Sheet \u2013 Replied",
"type": "n8n-nodes-base.googleSheets",
"position": [
3728,
832
],
"parameters": {
"columns": {
"value": {
"Date": "={{ $now.toISO() }}",
"Deal Name": "={{ $('Get Deal Details').item.json.properties.dealname }}",
"Contact Name": "={{ $('Re-fetch Contact (Fresh Data)').item.json.properties.firstname }} {{ $('Re-fetch Contact (Fresh Data)').item.json.properties.lastname }}",
"Contact Email": "={{ $('Re-fetch Contact (Fresh Data)').item.json.properties.email }}",
"Sequence Status": "Replied \u2013 Meeting Booked"
},
"mappingMode": "defineBelow"
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "REPLACE_WITH_YOUR_SHEET_ID"
}
},
"typeVersion": 4
},
{
"id": "8d63dbc7-ff52-447d-a376-8d7e96535217",
"name": "Log to Sheet \u2013 No Reply",
"type": "n8n-nodes-base.googleSheets",
"position": [
3728,
1024
],
"parameters": {
"columns": {
"value": {
"Date": "={{ $now.toISO() }}",
"Deal Name": "={{ $('Get Deal Details').item.json.properties.dealname }}",
"Contact Name": "={{ $('Re-fetch Contact (Fresh Data)').item.json.properties.firstname }} {{ $('Re-fetch Contact (Fresh Data)').item.json.properties.lastname }}",
"Contact Email": "={{ $('Re-fetch Contact (Fresh Data)').item.json.properties.email }}",
"Sequence Status": "No Reply \u2013 Closed Lost"
},
"mappingMode": "defineBelow"
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "REPLACE_WITH_YOUR_SHEET_ID"
}
},
"typeVersion": 4
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "94628603-fb71-487a-9d30-9b0222b899a8",
"connections": {
"Wait 2 Days": {
"main": [
[
{
"node": "Send Follow-Up Email",
"type": "main",
"index": 0
}
]
]
},
"Get Deal Details": {
"main": [
[
{
"node": "Get Contact Details",
"type": "main",
"index": 0
}
]
]
},
"Send Intro Email": {
"main": [
[
{
"node": "Update Deal Stage \u2013 Contacted",
"type": "main",
"index": 0
}
]
]
},
"Wait 3 More Days": {
"main": [
[
{
"node": "Re-fetch Contact (Fresh Data)",
"type": "main",
"index": 0
}
]
]
},
"Send Breakup Email": {
"main": [
[
{
"node": "Update Deal Stage \u2013 Closed Lost",
"type": "main",
"index": 0
}
]
]
},
"Get Contact Details": {
"main": [
[
{
"node": "Valid Contact & Email?",
"type": "main",
"index": 0
}
]
]
},
"Has Contact Replied?": {
"main": [
[
{
"node": "Slack Alert \u2013 Contact Replied",
"type": "main",
"index": 0
}
],
[
{
"node": "Send Breakup Email",
"type": "main",
"index": 0
}
]
]
},
"Send Follow-Up Email": {
"main": [
[
{
"node": "Wait 3 More Days",
"type": "main",
"index": 0
}
]
]
},
"Valid Contact & Email?": {
"main": [
[
{
"node": "Send Intro Email",
"type": "main",
"index": 0
}
],
[
{
"node": "Log Invalid \u2013 Skip",
"type": "main",
"index": 0
}
]
]
},
"HubSpot Trigger \u2013 New Deal": {
"main": [
[
{
"node": "Get Deal Details",
"type": "main",
"index": 0
}
]
]
},
"Re-fetch Contact (Fresh Data)": {
"main": [
[
{
"node": "Has Contact Replied?",
"type": "main",
"index": 0
}
]
]
},
"Slack Alert \u2013 Contact Replied": {
"main": [
[
{
"node": "Update Deal Stage \u2013 Meeting Booked",
"type": "main",
"index": 0
}
]
]
},
"Update Deal Stage \u2013 Contacted": {
"main": [
[
{
"node": "Wait 2 Days",
"type": "main",
"index": 0
}
]
]
},
"Update Deal Stage \u2013 Closed Lost": {
"main": [
[
{
"node": "Log to Sheet \u2013 No Reply",
"type": "main",
"index": 0
}
]
]
},
"Update Deal Stage \u2013 Meeting Booked": {
"main": [
[
{
"node": "Log to Sheet \u2013 Replied",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow triggers when a new deal is created in HubSpot, sends a two-step follow-up sequence via Gmail, checks HubSpot for a recent email reply, then updates the deal stage, alerts a Slack channel on replies, and logs outcomes to Google Sheets. Triggers when a new deal is…
Source: https://n8n.io/workflows/16004/ — 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 automatically handles every resolved Jira bug by verifying the fix, notifying the customer, updating HubSpot, commenting on the Jira issue, alerting the team on Slack, and logging everyt
This workflow automatically handles every failed Stripe payment by collecting all details, assessing severity, and taking immediate action — alerting your team, creating recovery tasks, escalating rep
This workflow automatically detects bounced or invalid email addresses from your Gmail inbox and updates their status in Google Sheets. It fetches bounce notifications, extracts failed email addresses
This is an elite enterprise-grade solution for Talent Acquisition and HR Ops teams. It automates the high-volume task of resume screening by transforming unstructured PDF applications into structured
📘 Description