This workflow corresponds to n8n.io template #18109 — we link there as the canonical source.
This workflow follows the Chainllm → Gmail 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": "8Vz99xDWy8LcIYxe",
"name": "AI-Based Deal Momentum Score Engine",
"tags": [],
"nodes": [
{
"id": "787d70ca-3d40-43da-8370-39df170f81cb",
"name": "Fetch Opportunities",
"type": "n8n-nodes-base.salesforce",
"position": [
352,
1024
],
"parameters": {
"options": {},
"resource": "opportunity",
"operation": "getAll"
},
"typeVersion": 1
},
{
"id": "ca67f00a-cee8-444c-b77f-c8217b5a3066",
"name": "Fetch Opportunity Details",
"type": "n8n-nodes-base.salesforce",
"position": [
912,
1040
],
"parameters": {
"resource": "opportunity",
"operation": "get",
"opportunityId": "={{ $json.Id }}"
},
"typeVersion": 1
},
{
"id": "839b1f4e-104c-4508-8ec7-493e28661306",
"name": "Fetch Contact Details",
"type": "n8n-nodes-base.salesforce",
"position": [
1312,
816
],
"parameters": {
"resource": "contact",
"contactId": "={{ $json.ContactId }}",
"operation": "get"
},
"typeVersion": 1
},
{
"id": "c2b7b9df-8031-44c5-a736-0b498bf14a31",
"name": "Fetch Deal Emails",
"type": "n8n-nodes-base.gmail",
"position": [
1888,
448
],
"parameters": {
"filters": {
"q": "=(to:{{$node[\"Fetch Contact Details\"].json[\"Email\"]}} OR from:{{$node[\"Fetch Contact Details\"].json[\"Email\"]}}) newer_than:14d -from:mailer-daemon"
},
"operation": "getAll"
},
"typeVersion": 2.2,
"alwaysOutputData": true
},
{
"id": "7796c8d1-9866-4e9f-bd3a-227674f16493",
"name": "Fetch Deal Meetings",
"type": "n8n-nodes-base.googleCalendar",
"position": [
1888,
688
],
"parameters": {
"options": {
"query": "={{$node[\"Fetch Contact Details\"].json[\"Email\"]}}"
},
"timeMax": "={{ $now.toISO() }}",
"timeMin": "={{ $now.minus({ days: 14 }).toISO() }}",
"calendar": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultName": ""
},
"operation": "getAll"
},
"typeVersion": 1.3,
"alwaysOutputData": true
},
{
"id": "97a6bddb-e4fd-49f2-baaa-6c93be6d02c5",
"name": "Daily Momentum Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
128,
1024
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 7
}
]
}
},
"typeVersion": 1.3
},
{
"id": "f9867343-d4ad-48f9-a4bb-a7a2df92c910",
"name": "Process Deals One by One",
"type": "n8n-nodes-base.splitInBatches",
"position": [
560,
1024
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "27a33eb9-8fc8-4dab-bdc7-44abb3faf03c",
"name": "Merge Engagement Metrics",
"type": "n8n-nodes-base.merge",
"position": [
2512,
592
],
"parameters": {
"mode": "combine",
"options": {},
"fieldsToMatchString": "contact_email"
},
"typeVersion": 3.2
},
{
"id": "c0721c91-a859-4a2f-aaa9-f22bdb5d601a",
"name": "Merge Deal Data",
"type": "n8n-nodes-base.merge",
"position": [
2736,
736
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineByPosition"
},
"typeVersion": 3.2
},
{
"id": "cb09194c-0070-4e0c-aafe-69049ae0a56d",
"name": "Calculate Momentum Score",
"type": "n8n-nodes-base.code",
"position": [
2960,
736
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const deal = $json;\n\nconst stage = deal.StageName || \"\";\n\nconst emailCount = Number(deal.email_count_14d || 0);\nconst meetingCount = Number(deal.meeting_count_14d || 0);\nconst daysSinceLastEmail = Number(deal.days_since_last_email ?? 999);\nconst daysSinceLastMeeting = Number(deal.days_since_last_meeting ?? 999);\n\n// Email engagement score\nlet emailScore = 0;\nif (emailCount >= 6) emailScore = 25;\nelse if (emailCount >= 4) emailScore = 18;\nelse if (emailCount >= 2) emailScore = 10;\nelse if (emailCount >= 1) emailScore = 5;\n\n// Meeting engagement score\nlet meetingScore = 0;\nif (meetingCount >= 3) meetingScore = 25;\nelse if (meetingCount >= 2) meetingScore = 18;\nelse if (meetingCount >= 1) meetingScore = 10;\n\n// Email recency score\nlet emailRecencyScore = 0;\nif (daysSinceLastEmail <= 1) emailRecencyScore = 15;\nelse if (daysSinceLastEmail <= 3) emailRecencyScore = 10;\nelse if (daysSinceLastEmail <= 7) emailRecencyScore = 5;\n\n// Meeting recency score\nlet meetingRecencyScore = 0;\nif (daysSinceLastMeeting <= 2) meetingRecencyScore = 15;\nelse if (daysSinceLastMeeting <= 5) meetingRecencyScore = 10;\nelse if (daysSinceLastMeeting <= 10) meetingRecencyScore = 5;\n\n// Stage score\nlet stageScore = 0;\nconst advancedStages = [\"Proposal/Price Quote\", \"Negotiation/Review\"];\nconst midStages = [\"Qualification\", \"Needs Analysis\", \"Value Proposition\"];\n\nif (advancedStages.includes(stage)) stageScore = 20;\nelse if (midStages.includes(stage)) stageScore = 12;\nelse if (stage) stageScore = 6;\n\n// Inactivity penalty\nlet inactivityPenalty = 0;\nconst minDays = Math.min(daysSinceLastEmail, daysSinceLastMeeting);\n\nif (minDays >= 14) inactivityPenalty = 15;\nelse if (minDays >= 10) inactivityPenalty = 10;\nelse if (minDays >= 7) inactivityPenalty = 5;\n\n// Final score\nlet momentumScore =\n emailScore +\n meetingScore +\n emailRecencyScore +\n meetingRecencyScore +\n stageScore -\n inactivityPenalty;\n\nmomentumScore = Math.max(0, Math.min(100, momentumScore));\n\nlet momentumBand = \"Low\";\nif (momentumScore >= 75) momentumBand = \"High\";\nelse if (momentumScore >= 45) momentumBand = \"Medium\";\n\n// Optional trend label\nlet momentumStatus = \"Cold\";\nif (momentumScore >= 75) momentumStatus = \"Hot\";\nelse if (momentumScore >= 45) momentumStatus = \"Active\";\n\n// Reason drivers\nconst positives = [];\nconst negatives = [];\n\nif (emailCount >= 4) positives.push(`strong email activity (${emailCount} emails in 14d)`);\nelse if (emailCount === 0) negatives.push(\"no recent email activity\");\n\nif (meetingCount >= 2) positives.push(`strong meeting activity (${meetingCount} meetings in 14d)`);\nelse if (meetingCount === 0) negatives.push(\"no recent meetings\");\n\nif (daysSinceLastEmail <= 3) positives.push(`very recent email activity (${daysSinceLastEmail} day(s) ago)`);\nelse if (daysSinceLastEmail >= 10) negatives.push(`email activity is stale (${daysSinceLastEmail} days ago)`);\n\nif (daysSinceLastMeeting <= 5) positives.push(`recent meeting activity (${daysSinceLastMeeting} day(s) ago)`);\nelse if (daysSinceLastMeeting >= 10) negatives.push(`meeting activity is stale (${daysSinceLastMeeting} days ago)`);\n\nif (advancedStages.includes(stage)) positives.push(`deal is in an advanced stage (${stage})`);\nelse if (!stage) negatives.push(\"deal stage is missing\");\n\nreturn {\n json: {\n ...deal,\n momentum_score: momentumScore,\n momentum_band: momentumBand,\n momentum_status: momentumStatus,\n score_breakdown: {\n email_score: emailScore,\n meeting_score: meetingScore,\n email_recency_score: emailRecencyScore,\n meeting_recency_score: meetingRecencyScore,\n stage_score: stageScore,\n inactivity_penalty: inactivityPenalty\n },\n positive_drivers: positives,\n negative_drivers: negatives\n }\n};"
},
"typeVersion": 2
},
{
"id": "f00cf5b0-39b4-4d58-9b04-3de9dd4dc241",
"name": "Google Gemini Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
3424,
1184
],
"parameters": {
"options": {},
"modelName": "models/gemini-flash-lite-latest"
},
"typeVersion": 1
},
{
"id": "166bb3fe-1c7f-45d5-897b-9466d4ce41d3",
"name": "Generate Momentum Reasoning",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
3424,
992
],
"parameters": {
"text": "=You are a sales operations analyst.\n\nYour task is to explain the current momentum of a sales deal using the provided structured data.\n\nRules:\n- Write a concise explanation in 2 to 4 sentences.\n- Be specific and grounded in the given data.\n- Mention the strongest positive signals and any missing or stale activity.\n- Keep the tone professional, crisp, and business-friendly.\n- Do not invent facts.\n- If the score is high, explain why the deal appears active and progressing.\n- If the score is medium, explain what is working and what is still limited.\n- If the score is low, explain what is weak, missing, or stale.\n- Also give one short recommended next action.\n- Return only valid JSON.\n- Do not wrap the response in markdown fences.\n\nReturn exactly this structure:\n{\n \"momentum_reason\": \"string\",\n \"next_action\": \"string\"\n}\n\nDeal data:\n{{ JSON.stringify($json) }}",
"batching": {},
"messages": {
"messageValues": []
},
"promptType": "define"
},
"typeVersion": 1.9
},
{
"id": "5e101e2e-7232-45cd-a835-6458015bd879",
"name": "Update Deal Momentum",
"type": "n8n-nodes-base.salesforce",
"position": [
3904,
992
],
"parameters": {
"resource": "opportunity",
"operation": "update",
"updateFields": {
"customFieldsUi": {
"customFieldsValues": [
{
"value": "={{ $('Calculate Momentum Score').item.json.momentum_score }}",
"fieldId": "Momentum_Score__c"
},
{
"value": "={{ $('Calculate Momentum Score').item.json.momentum_band }}",
"fieldId": "Momentum_Band__c"
},
{
"value": "={{ $json.momentum_reason }}",
"fieldId": "Momentum_Reason__c"
},
{
"value": "={{$now.toISO()}}",
"fieldId": "Momentum_Last_Updated__c"
}
]
}
},
"opportunityId": "={{$node[\"Calculate Momentum Score\"].json[\"Id\"]}}"
},
"typeVersion": 1
},
{
"id": "85e6af99-e7f4-45c6-b2f2-7cc6d71a33c5",
"name": "Prepare Email Metrics",
"type": "n8n-nodes-base.code",
"position": [
2160,
448
],
"parameters": {
"jsCode": "const emailItems = $items(\"Fetch Deal Emails\");\nconst contactEmail = $item(0).$node[\"Fetch Contact Details\"].json[\"Email\"];\nconst dealId = $item(0).$node[\"Fetch Opportunity Details\"].json[\"Id\"];\n\nconst count = emailItems.length;\n\nlet lastEmailAt = null;\nlet daysSinceLastEmail = 999;\n\nif (count > 0) {\n const dates = emailItems\n .map(item => item.json.internalDate || null)\n .filter(Boolean)\n .map(d => Number(d));\n\n if (dates.length > 0) {\n const latest = Math.max(...dates);\n lastEmailAt = new Date(latest).toISOString();\n daysSinceLastEmail = Math.floor((Date.now() - latest) / (1000 * 60 * 60 * 24));\n }\n}\n\nreturn [\n {\n json: {\n deal_id: dealId,\n contact_email: contactEmail,\n email_count_14d: count,\n last_email_at: lastEmailAt,\n days_since_last_email: daysSinceLastEmail\n }\n }\n];"
},
"typeVersion": 2
},
{
"id": "b5bbd352-aaf9-4772-8803-84fb29cb4342",
"name": "Prepare Meeting Metrics",
"type": "n8n-nodes-base.code",
"position": [
2160,
688
],
"parameters": {
"jsCode": "const meetingItems = $items(\"Fetch Deal Meetings\");\nconst contactEmail = $item(0).$node[\"Fetch Contact Details\"].json[\"Email\"];\nconst dealId = $item(0).$node[\"Fetch Opportunity Details\"].json[\"Id\"];\n\nconst validMeetings = meetingItems.filter(\n item =>\n item.json &&\n (item.json.start?.dateTime ||\n item.json.start?.date ||\n item.json.created)\n);\n\nconst count = validMeetings.length;\n\nlet lastMeetingAt = null;\nlet daysSinceLastMeeting = 999;\n\nif (count > 0) {\n const dates = validMeetings\n .map(item =>\n item.json.start?.dateTime ||\n item.json.start?.date ||\n item.json.created ||\n null\n )\n .filter(Boolean)\n .map(d => new Date(d).getTime());\n\n if (dates.length > 0) {\n const latest = Math.max(...dates);\n lastMeetingAt = new Date(latest).toISOString();\n daysSinceLastMeeting = Math.floor((Date.now() - latest) / (1000 * 60 * 60 * 24));\n }\n}\n\nreturn [\n {\n json: {\n deal_id: dealId,\n contact_email: contactEmail,\n meeting_count_14d: count,\n last_meeting_at: lastMeetingAt,\n days_since_last_meeting: daysSinceLastMeeting\n }\n }\n];"
},
"typeVersion": 2
},
{
"id": "09521188-062c-4a56-babf-98bc4f9c18e0",
"name": "Prepare AI Input",
"type": "n8n-nodes-base.set",
"position": [
3248,
992
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "fbd93088-6889-45e5-823c-5bf5eaaaf0c3",
"name": "deal_name",
"type": "string",
"value": "={{$json[\"Name\"]}}"
},
{
"id": "7559a218-d962-44d2-8ab1-7bd32bcb43a7",
"name": "stage",
"type": "string",
"value": "={{$json[\"StageName\"]}}"
},
{
"id": "7a2e4fe1-cfb0-4b92-b5a7-806adefe776f",
"name": "amount",
"type": "number",
"value": "={{$json[\"Amount\"]}}"
},
{
"id": "720f1030-b210-4548-93e8-8a3d8680c0d7",
"name": "email_count",
"type": "number",
"value": "={{$json[\"email_count_14d\"]}}"
},
{
"id": "3d0cf683-f8b2-4483-ac1a-349ac14aab16",
"name": "meeting_count",
"type": "number",
"value": "={{$json[\"meeting_count_14d\"]}}"
},
{
"id": "3c98e869-2c96-4cd4-829c-1b43abec99c9",
"name": "days_since_last_email",
"type": "number",
"value": "={{$json[\"days_since_last_email\"]}}"
},
{
"id": "42ab0195-7a20-41f6-9edb-68885fa80df1",
"name": "days_since_last_meeting",
"type": "number",
"value": "={{$json[\"days_since_last_meeting\"]}}"
},
{
"id": "5c78663a-eb5b-4e3c-8613-ad5dd417094f",
"name": "momentum_score",
"type": "number",
"value": "={{$json[\"momentum_score\"]}}"
},
{
"id": "8336a372-ccbe-4956-a3c0-93b720e8881f",
"name": "momentum_band",
"type": "string",
"value": "={{$json[\"momentum_band\"]}}"
},
{
"id": "b01f7bc7-9595-47c6-ab6d-48ef9e541c3f",
"name": "positive_drivers",
"type": "array",
"value": "={{$json[\"positive_drivers\"]}}"
},
{
"id": "f29a315a-ff9a-4bb2-9abc-88209d761115",
"name": "negative_drivers",
"type": "array",
"value": "={{$json[\"negative_drivers\"]}}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "4f9eaae2-4a44-4c5f-b01a-89eb77189ef5",
"name": "Parse AI Output",
"type": "n8n-nodes-base.code",
"position": [
3728,
992
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const raw = $json.text;\n\nlet parsed;\n\ntry {\n parsed = JSON.parse(raw);\n} catch (e) {\n parsed = {\n momentum_reason: raw,\n next_action: \"Unable to parse structured response\"\n };\n}\n\nreturn {\n json: {\n ...parsed\n }\n};"
},
"typeVersion": 2
},
{
"id": "4300d69f-0523-4640-8861-9fa5423dd42d",
"name": "Send Momentum Update to Slack",
"type": "n8n-nodes-base.slack",
"position": [
4128,
992
],
"parameters": {
"text": "={{\n`*Deal Momentum Update*\n*Deal:* ${$node[\"Calculate Momentum Score\"].json[\"Name\"]}\n*Score:* ${$node[\"Calculate Momentum Score\"].json[\"momentum_score\"]} (${$node[\"Calculate Momentum Score\"].json[\"momentum_band\"]})\n*Stage:* ${$node[\"Calculate Momentum Score\"].json[\"StageName\"]}\n*Contact:* ${$node[\"Calculate Momentum Score\"].json[\"contact_email\"]}\n\n*Reason:* ${$node[\"Parse AI Output\"].json[\"momentum_reason\"]}\n*Next Action:* ${$node[\"Parse AI Output\"].json[\"next_action\"]}`\n}}",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "C0ASRU4GAV8",
"cachedResultName": "deal-momentum"
},
"otherOptions": {
"includeLinkToWorkflow": false
},
"authentication": "oAuth2"
},
"typeVersion": 2.4
},
{
"id": "cda36d3d-46d2-4e1f-981e-c111a71f6dd2",
"name": "Wait Between Deals",
"type": "n8n-nodes-base.wait",
"position": [
4320,
1296
],
"parameters": {
"unit": "minutes",
"amount": 1
},
"typeVersion": 1.1
},
{
"id": "0ea6e985-6b21-4b72-a1a1-ef463c0a3c32",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-288,
0
],
"parameters": {
"width": 1056,
"height": 784,
"content": "# AI-Based Deal Momentum Score Engine\n\n## How it works:\nThis workflow runs daily to evaluate the momentum of sales opportunities. It fetches deals from Salesforce and processes them individually, retrieving engagement signals such as recent emails and meetings from Gmail and Google Calendar. These signals are transformed into structured metrics and combined with deal stage data to calculate a momentum score, band, and status. An AI model then generates a concise explanation of the deal\u2019s momentum along with a recommended next action. The results are updated back into Salesforce and a Slack notification is sent for visibility, with a wait step between deals to control processing and API usage.\n\n## Setup steps:\n1. Connect Salesforce to fetch and update opportunity data \n2. Connect Gmail and configure email search filters for engagement tracking \n3. Connect Google Calendar to capture recent meeting activity \n4. Configure Gemini API credentials for AI reasoning \n5. Set Slack channel for deal momentum notifications \n6. Create and map custom Salesforce fields for momentum score, band, and reasoning \n7. Configure the schedule trigger time for daily execution \n8. Adjust lookback window (e.g., 14 days) for emails and meetings if needed \n9. Test the workflow with sample opportunities and activate it "
},
"typeVersion": 1
},
{
"id": "e1145cd4-e25a-40d0-a18d-1f0f4b678479",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
848,
624
],
"parameters": {
"color": 7,
"width": 864,
"height": 608,
"content": "## Deal Validation & Enrichment\nFetches detailed opportunity and contact data, validating required fields like Contact ID and email before proceeding to engagement data collection."
},
"typeVersion": 1
},
{
"id": "1aa72d5b-1615-4455-8c44-ac9352dd2afd",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
1792,
192
],
"parameters": {
"color": 7,
"width": 550,
"height": 672,
"content": "## Engagement Collection & Processing\nFetches recent emails and meetings for each deal\u2019s contact and transforms this activity into structured engagement metrics such as counts, recency, and timestamps."
},
"typeVersion": 1
},
{
"id": "a71fbdc3-5f1b-4b2d-a5e8-6d72079dd8d6",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
2432,
352
],
"parameters": {
"color": 7,
"width": 688,
"height": 560,
"content": "## Metrics Aggregation & Scoring\nMerges engagement metrics with deal data and calculates a momentum score, band, and key drivers based on activity levels, recency, and deal stage."
},
"typeVersion": 1
},
{
"id": "56b4ea63-3e74-4ce0-84e7-28af72a2047e",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
3200,
736
],
"parameters": {
"color": 7,
"width": 1296,
"height": 768,
"content": "## AI Insights, Update & Notifications\nGenerates AI-based momentum reasoning and next actions, parses the response, updates Salesforce with insights, and sends Slack notifications with a delay between deals to manage execution flow."
},
"typeVersion": 1
},
{
"id": "b1c00e1f-bf39-41db-9884-5bbc6895d7ca",
"name": "Check ContactId Exists",
"type": "n8n-nodes-base.if",
"position": [
1120,
912
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "7ca6e7b5-887f-4dfe-bf0c-a59012d8ce45",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{$json[\"ContactId\"]}}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.3
},
{
"id": "44518be0-c433-474f-9695-cb92912e269b",
"name": "Check Contact Email Exists",
"type": "n8n-nodes-base.if",
"position": [
1520,
816
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "3a03dbe9-1dcb-4c05-9f4b-7f0816cced82",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{$json[\"Email\"]}}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.3
},
{
"id": "cedb5d7a-183a-4665-8d9f-0ba482081e44",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-16,
848
],
"parameters": {
"color": 7,
"width": 784,
"height": 384,
"content": "## Deal Retrieval & Iteration\nTriggers daily execution, fetches opportunities from Salesforce, and processes each deal individually to ensure controlled and sequential evaluation."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"availableInMCP": false,
"executionOrder": "v1"
},
"versionId": "33033780-2c33-4369-b753-3d002e7e1d85",
"nodeGroups": [],
"connections": {
"Merge Deal Data": {
"main": [
[
{
"node": "Calculate Momentum Score",
"type": "main",
"index": 0
}
]
]
},
"Parse AI Output": {
"main": [
[
{
"node": "Update Deal Momentum",
"type": "main",
"index": 0
}
]
]
},
"Prepare AI Input": {
"main": [
[
{
"node": "Generate Momentum Reasoning",
"type": "main",
"index": 0
}
]
]
},
"Fetch Deal Emails": {
"main": [
[
{
"node": "Prepare Email Metrics",
"type": "main",
"index": 0
}
]
]
},
"Wait Between Deals": {
"main": [
[
{
"node": "Process Deals One by One",
"type": "main",
"index": 0
}
]
]
},
"Fetch Deal Meetings": {
"main": [
[
{
"node": "Prepare Meeting Metrics",
"type": "main",
"index": 0
}
]
]
},
"Fetch Opportunities": {
"main": [
[
{
"node": "Process Deals One by One",
"type": "main",
"index": 0
}
]
]
},
"Update Deal Momentum": {
"main": [
[
{
"node": "Send Momentum Update to Slack",
"type": "main",
"index": 0
}
]
]
},
"Fetch Contact Details": {
"main": [
[
{
"node": "Check Contact Email Exists",
"type": "main",
"index": 0
}
]
]
},
"Prepare Email Metrics": {
"main": [
[
{
"node": "Merge Engagement Metrics",
"type": "main",
"index": 0
}
]
]
},
"Check ContactId Exists": {
"main": [
[
{
"node": "Fetch Contact Details",
"type": "main",
"index": 0
}
],
[
{
"node": "Wait Between Deals",
"type": "main",
"index": 0
}
]
]
},
"Daily Momentum Trigger": {
"main": [
[
{
"node": "Fetch Opportunities",
"type": "main",
"index": 0
}
]
]
},
"Prepare Meeting Metrics": {
"main": [
[
{
"node": "Merge Engagement Metrics",
"type": "main",
"index": 1
}
]
]
},
"Calculate Momentum Score": {
"main": [
[
{
"node": "Prepare AI Input",
"type": "main",
"index": 0
}
]
]
},
"Google Gemini Chat Model": {
"ai_languageModel": [
[
{
"node": "Generate Momentum Reasoning",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Merge Engagement Metrics": {
"main": [
[
{
"node": "Merge Deal Data",
"type": "main",
"index": 0
}
]
]
},
"Process Deals One by One": {
"main": [
[],
[
{
"node": "Fetch Opportunity Details",
"type": "main",
"index": 0
}
]
]
},
"Fetch Opportunity Details": {
"main": [
[
{
"node": "Merge Deal Data",
"type": "main",
"index": 1
},
{
"node": "Check ContactId Exists",
"type": "main",
"index": 0
}
]
]
},
"Check Contact Email Exists": {
"main": [
[
{
"node": "Fetch Deal Emails",
"type": "main",
"index": 0
},
{
"node": "Fetch Deal Meetings",
"type": "main",
"index": 0
}
],
[
{
"node": "Wait Between Deals",
"type": "main",
"index": 0
}
]
]
},
"Generate Momentum Reasoning": {
"main": [
[
{
"node": "Parse AI Output",
"type": "main",
"index": 0
}
]
]
},
"Send Momentum Update to Slack": {
"main": [
[
{
"node": "Wait Between Deals",
"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 runs daily to score Salesforce opportunities based on recent Gmail email activity and Google Calendar meetings, then uses Google Gemini to generate a brief momentum explanation and next action, updates custom fields in Salesforce, and posts a deal momentum update…
Source: https://n8n.io/workflows/18109/ — 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.
Categories Content Creation AI Automation Publishing Social Media
This workflow builds an AI meeting assistant who sends information-dense pre-meeting notifications for a user's upcoming meetings. A scheduled trigger fires hourly and checks for upcoming meetings wit
Automatically identifies overdue sales leads and generates personalized follow-up emails using AI. Runs every weekday Reads leads from Google Sheets Filters leads with no contact for 5+ days Downloads
This workflow runs weekly to find cross-sell white space in Salesforce enterprise accounts by comparing Closed Won products to an ERP product catalog, then uses Google Gemini to generate the top oppor
This workflow runs daily to review Japanese ad copy in Google Sheets using Google Gemini, writes compliance risk results back to the sheet, and alerts the right team in Slack (and drafts a Gmail revis