This workflow corresponds to n8n.io template #10147 — 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": "zAhUYo1pPedFF2vq",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Automate Client Renewal Alerts from GoHighLevel to Gmail and Slack",
"tags": [],
"nodes": [
{
"id": "e89a6dcb-42d4-4674-bd3d-2f91b86231b2",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3120,
-64
],
"parameters": {
"color": 4,
"width": 420,
"height": 656,
"content": "## \ud83d\udd04 Client Retention Renewal Reminder Workflow\n\nAutomatically identifies clients with contracts expiring within 10 days and sends personalized renewal reminders.\n\n### What This Workflow Does:\n- **Runs daily at 9 AM** to check for expiring contracts\n- **Fetches all contacts** from GoHighLevel CRM\n- **Filters clients** whose contracts expire in 0-10 days\n- **Sends renewal emails** to clients via Gmail\n- **Notifies account managers** via Slack\n- **Logs all activities** to Google Sheets for tracking\n\n### Business Benefits:\n- \u2705 Never miss a renewal opportunity\n- \u2705 Improve client retention rates\n- \u2705 Automate manual follow-up tasks\n- \u2705 Track renewal campaign effectiveness\n\n### Requirements:\n- GoHighLevel account with custom fields:\n - Contract End Date field\n - Account Manager field\n- Gmail account for sending emails\n- Slack workspace for notifications\n- Google Sheets for logging"
},
"typeVersion": 1
},
{
"id": "9ffba3e1-2389-47df-8257-708871c6c5e4",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2656,
-208
],
"parameters": {
"color": 5,
"width": 280,
"height": 240,
"content": "## \u23f0 Schedule Trigger Setup\n\nRuns daily at 9:00 AM server time.\n\n**Configuration:**\n- Cron: `0 9 * * *`\n- Frequency: Every day\n- Time: 9:00 AM\n\n**Why 9 AM?**\nEarly morning execution ensures:\n- Emails arrive during business hours\n- Account managers have full day to respond\n- Prevents weekend/holiday issues"
},
"typeVersion": 1
},
{
"id": "94e0e170-fe29-42a6-9926-d052fbc28fd5",
"name": "Schedule Daily at 9 AM",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-2560,
48
],
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 9 * * *"
}
]
}
},
"typeVersion": 1.1
},
{
"id": "05b63bb3-1942-4f24-9d53-3b3f2a25a7f5",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2432,
240
],
"parameters": {
"color": 5,
"width": 280,
"height": 396,
"content": "## \ud83d\udce5 Fetch Contacts from CRM\n\n**Setup Instructions:**\n1. Connect your GoHighLevel account\n2. Set limit to 50 (or adjust based on needs)\n3. Ensure API permissions include contact read access\n\n**Required Custom Fields:**\n- Contract End Date (Date field)\n- Account Manager (Text field)\n\n**Note:** Increase limit if you have more than 50 active clients"
},
"typeVersion": 1
},
{
"id": "5e3e0e62-267e-4728-a4ae-f92581607f06",
"name": "Fetch Contacts from GoHighLevel",
"type": "n8n-nodes-base.highLevel",
"position": [
-2336,
48
],
"parameters": {
"filters": {},
"options": {},
"operation": "getAll",
"requestOptions": {}
},
"credentials": {
"highLevelOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "9e665b65-003d-430d-a7f7-48228b7dcad8",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2208,
-272
],
"parameters": {
"color": 5,
"width": 260,
"height": 292,
"content": "## \ud83d\udd0d Validate Data Quality\n\nEnsures contacts have custom fields before processing.\n\n**Why This Matters:**\n- Prevents errors in downstream nodes\n- Filters out incomplete contact records\n- Improves workflow reliability"
},
"typeVersion": 1
},
{
"id": "eb37756d-3de4-4d63-8215-b4e8520bcb5e",
"name": "Check Has Custom Fields",
"type": "n8n-nodes-base.if",
"position": [
-2112,
48
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "58341a8d-2417-47e1-b29b-be575b270c35",
"operator": {
"type": "array",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $json.customFields }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "f102174e-cba7-4d13-a322-f2327d9f1449",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1936,
208
],
"parameters": {
"color": 5,
"width": 280,
"height": 404,
"content": "## \ud83c\udfaf Filter Expiring Contracts\n\n**Logic:**\n- Finds Contract End Date field\n- Calculates days until expiry\n- Filters: 0-10 days remaining\n\n**Customization:**\nChange `daysUntilExpiry <= 10` to adjust reminder window:\n- 7 days = 1 week notice\n- 14 days = 2 weeks notice\n- 30 days = 1 month notice\n\n**Important:** Update field IDs to match YOUR GoHighLevel custom fields"
},
"typeVersion": 1
},
{
"id": "6f9a5a69-8ce9-46c1-88f2-d12a44a3f319",
"name": "Filter Renewals Code",
"type": "n8n-nodes-base.code",
"position": [
-1888,
48
],
"parameters": {
"jsCode": "// Get current date at midnight for accurate day comparison\nconst today = new Date();\ntoday.setHours(0, 0, 0, 0);\n\n// Array to store filtered contacts\nconst filteredContacts = [];\n\n// Process each contact from input\nfor (const contact of $input.all()) {\n const item = contact.json;\n \n // Check if contact has customFields\n if (!item.customFields || !Array.isArray(item.customFields)) {\n continue;\n }\n \n // TODO: Replace with YOUR custom field ID for Contract End Date\n const contractEndField = item.customFields.find(\n field => field.id === 'YOUR_CONTRACT_END_DATE_FIELD_ID'\n );\n \n // Skip if no contract end date exists\n if (!contractEndField || !contractEndField.value) {\n continue;\n }\n \n // Parse contract end date (timestamp in milliseconds)\n const contractEndDate = new Date(contractEndField.value);\n contractEndDate.setHours(0, 0, 0, 0);\n \n // Calculate days until expiry\n const daysUntilExpiry = Math.ceil(\n (contractEndDate - today) / (1000 * 60 * 60 * 24)\n );\n \n // Filter: expires within next 10 days (0-10 days)\n if (daysUntilExpiry >= 0 && daysUntilExpiry <= 10) {\n \n // TODO: Replace with YOUR custom field ID for Account Manager\n const accountManagerField = item.customFields.find(\n field => field.id === 'YOUR_ACCOUNT_MANAGER_FIELD_ID'\n );\n \n // Format the contract end date for display\n const formattedDate = contractEndDate.toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric'\n });\n \n // Create enriched contact object\n filteredContacts.push({\n json: {\n // Original contact data\n id: item.id,\n contactName: item.contactName,\n firstName: item.firstName || 'N/A',\n lastName: item.lastName || 'N/A',\n email: item.email || 'No email on file',\n companyName: item.companyName || 'N/A',\n phone: item.phone,\n \n // Contract information\n contractEndDate: formattedDate,\n contractEndDateRaw: contractEndDate.toISOString(),\n contractEndTimestamp: contractEndField.value,\n daysUntilExpiry: daysUntilExpiry,\n \n // Account manager info\n accountManager: accountManagerField?.value || 'Unassigned',\n \n // Additional fields for workflow\n allCustomFields: item.customFields,\n tags: item.tags || [],\n source: item.source\n }\n });\n }\n}\n\n// Return filtered contacts\nreturn filteredContacts;"
},
"typeVersion": 2
},
{
"id": "6f7a46e5-115c-4bef-9cc7-e30b96b1b94d",
"name": "Verify Expiring Within 10 Days",
"type": "n8n-nodes-base.filter",
"position": [
-1664,
48
],
"parameters": {
"conditions": {
"number": [
{
"value1": "={{ $json.daysUntilExpiry }}",
"value2": 10,
"operation": "smallerEqual"
}
],
"string": [
{
"value1": "={{ $json.contractEndDate }}",
"operation": "isNotEmpty"
}
]
}
},
"typeVersion": 1
},
{
"id": "3b9392da-852b-4692-bfac-5c177bbda695",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1536,
-400
],
"parameters": {
"color": 5,
"width": 280,
"height": 340,
"content": "## \ud83d\udce7 Send Client Email\n\n**Setup:**\n1. Connect Gmail account\n2. Replace recipient with: `={{ $json.email }}`\n3. Customize email template\n\n**Email Variables:**\n- `firstName` - Client first name\n- `lastName` - Client last name\n- `contractEndDate` - Formatted expiry date\n- `companyName` - Company name\n\n**Best Practice:** Test email with your own address first"
},
"typeVersion": 1
},
{
"id": "17f722d7-781c-41d4-bd15-b84b187df947",
"name": "Send Renewal Email via Gmail",
"type": "n8n-nodes-base.gmail",
"position": [
-1440,
-48
],
"parameters": {
"sendTo": "={{ $json.email }}",
"message": "=Hi {{ $json.firstName }} {{ $json.lastName }},\n\nThis is a friendly reminder that your contract with us is set to expire on {{ $json.contractEndDate }}.\n\nWe'd love to continue working with you! Please let us know if you'd like to discuss renewal options.\n\nYour account manager will be reaching out shortly.\n\nBest regards,\nYour Team",
"options": {},
"subject": "Important: Your Contract Renewal is Coming Up"
},
"typeVersion": 2.1
},
{
"id": "3e716197-e31d-49d9-8c3e-b1ba215bed06",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1488,
304
],
"parameters": {
"color": 5,
"width": 280,
"height": 384,
"content": "## \ud83d\udce2 Slack Team Notification\n\n**Setup:**\n1. Connect Slack workspace\n2. Select channel for renewal alerts\n3. Customize message format\n\n**Recommended Channels:**\n- #renewals\n- #account-management\n- #sales-alerts\n\n**Note:** Ensure all account managers have access to the selected channel"
},
"typeVersion": 1
},
{
"id": "79b9f0dd-1fd1-4562-b93e-99e3bc111aeb",
"name": "Send Slack Alert to Team",
"type": "n8n-nodes-base.slack",
"position": [
-1440,
144
],
"parameters": {
"text": "=\ud83d\udd14 *Contract Renewal Alert*\n\n*Client:* {{ $json.firstName }} {{ $json.lastName }}\n*Email:* {{ $json.email }}\n*Company:* {{ $json.companyName || \"N/A\" }}\n*Contract Expires:* {{ $json.contractEndDate }}\n*Days Remaining:* {{ $json.daysUntilExpiry }}\n*Account Manager:* {{ $json.accountManager }}\n\n\u2705 Renewal email has been sent to client.\n\ud83d\udccb Please follow up within 3 business days.",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "YOUR_SLACK_CHANNEL_ID",
"cachedResultName": "Select your renewals channel"
},
"otherOptions": {}
},
"typeVersion": 2.1
},
{
"id": "f3448850-b573-416a-b34c-8e1e7d985151",
"name": "Merge Email and Slack Results",
"type": "n8n-nodes-base.merge",
"position": [
-1216,
48
],
"parameters": {
"mode": "combine",
"options": {},
"combinationMode": "mergeByPosition"
},
"typeVersion": 2.1
},
{
"id": "96ae9e61-3ffc-4f04-b6ac-2a58495dc455",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1072,
-272
],
"parameters": {
"color": 5,
"width": 260,
"height": 296,
"content": "## \ud83d\udcca Generate Summary Report\n\nCreates execution summary with:\n- Total reminders sent\n- Timestamp of execution\n- Success status\n\n**Used for:**\n- Tracking workflow performance\n- Logging to Google Sheets\n- Debugging issues"
},
"typeVersion": 1
},
{
"id": "c61d0e08-0b24-4be6-bbcd-b58de927212a",
"name": "Generate Summary Report",
"type": "n8n-nodes-base.code",
"position": [
-992,
48
],
"parameters": {
"jsCode": "const totalReminders = $input.all().length;\nconst timestamp = new Date().toLocaleString();\n\nreturn {\n json: {\n summary: `Renewal Reminder Workflow Completed`,\n totalRemindersSent: totalReminders,\n timestamp: timestamp,\n message: `${totalReminders} renewal reminder(s) sent successfully on ${timestamp}`\n }\n};"
},
"typeVersion": 2
},
{
"id": "9e39598a-d4f0-43e9-9885-a306371772e1",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
-752,
256
],
"parameters": {
"color": 5,
"width": 280,
"height": 268,
"content": "## \ud83d\udcdd Log to Google Sheets\n\n**Setup:**\n1. Create a Google Sheet for tracking\n2. Add columns: summary, totalRemindersSent, timestamp, message\n3. Connect your Google account\n4. Select your spreadsheet and sheet\n\n"
},
"typeVersion": 1
},
{
"id": "ace3170b-c470-4458-b601-db08918f1572",
"name": "Log Results to Google Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
-768,
48
],
"parameters": {
"columns": {
"value": {},
"schema": [
{
"id": "summary",
"type": "string",
"display": true,
"required": false,
"displayName": "summary",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "totalRemindersSent",
"type": "string",
"display": true,
"required": false,
"displayName": "totalRemindersSent",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "timestamp",
"type": "string",
"display": true,
"required": false,
"displayName": "timestamp",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "message",
"type": "string",
"display": true,
"required": false,
"displayName": "message",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "autoMapInputData",
"matchingColumns": []
},
"options": {},
"operation": "appendOrUpdate",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "Sheet1",
"cachedResultName": "Select sheet name"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "YOUR_GOOGLE_SHEET_ID",
"cachedResultName": "Select your tracking sheet"
}
},
"typeVersion": 4.7
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "180a6f20-55ff-43e1-bbd0-85e6eebaf0bb",
"connections": {
"Filter Renewals Code": {
"main": [
[
{
"node": "Verify Expiring Within 10 Days",
"type": "main",
"index": 0
}
]
]
},
"Schedule Daily at 9 AM": {
"main": [
[
{
"node": "Fetch Contacts from GoHighLevel",
"type": "main",
"index": 0
}
]
]
},
"Check Has Custom Fields": {
"main": [
[
{
"node": "Filter Renewals Code",
"type": "main",
"index": 0
}
]
]
},
"Generate Summary Report": {
"main": [
[
{
"node": "Log Results to Google Sheets",
"type": "main",
"index": 0
}
]
]
},
"Send Slack Alert to Team": {
"main": [
[
{
"node": "Merge Email and Slack Results",
"type": "main",
"index": 1
}
]
]
},
"Send Renewal Email via Gmail": {
"main": [
[
{
"node": "Merge Email and Slack Results",
"type": "main",
"index": 0
}
]
]
},
"Merge Email and Slack Results": {
"main": [
[
{
"node": "Generate Summary Report",
"type": "main",
"index": 0
}
]
]
},
"Verify Expiring Within 10 Days": {
"main": [
[
{
"node": "Send Renewal Email via Gmail",
"type": "main",
"index": 0
},
{
"node": "Send Slack Alert to Team",
"type": "main",
"index": 0
}
]
]
},
"Fetch Contacts from GoHighLevel": {
"main": [
[
{
"node": "Check Has Custom Fields",
"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.
highLevelOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Streamline client retention and contract renewals by automatically identifying expiring accounts, sending personalized reminder emails, and notifying account managers through Slack. This workflow ensures timely outreach, improved renewal rates, and centralized tracking — without…
Source: https://n8n.io/workflows/10147/ — 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.
Automatically identify clients who haven’t been contacted in 14+ days and re-engage them with personalized Gmail follow-up emails, Google Sheets tracking, and Slack notifications for account managers.
This powerful n8n workflow automatically processes, categorizes, and organizes your Gmail inbox using customizable rules stored in Google Sheets. Say goodbye to manual email sorting and hello to a per
This workflow automatically monitors solar energy production every 2 hours by fetching data from the Energidataservice API. If the energy output falls below a predefined threshold, it instantly notifi
This workflow is an automated invoice payment tracking and reminder system for the Polish accounting service iFirma.pl. It monitors unpaid and overdue invoices, then automatically sends escalating rem
Automatically extract structured information from emails using AI-powered document analysis. This workflow processes emails from specified domains, classifies them by type, and extracts structured dat