This workflow corresponds to n8n.io template #17042 — we link there as the canonical source.
This workflow follows the Googlegemini → 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": "JgBAdO2iuEdStmRd",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Multi-System Revenue Leakage Detector",
"tags": [],
"nodes": [
{
"id": "fd7a6a34-7802-4a80-a8c6-beb73b48a86d",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
336,
2176
],
"parameters": {
"color": 7,
"width": 480,
"height": 368,
"content": "## Revenue Intake & Opportunity Fetch\n\nFetches Closed Won deals from Salesforce to analyze finalized revenue data."
},
"typeVersion": 1
},
{
"id": "345bae89-664f-489b-b984-8dce34e4496a",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
832,
2176
],
"parameters": {
"color": 7,
"width": 496,
"height": 368,
"content": "## Batch Processing & Rate Control\n\nSplits data into batches and adds delay to avoid API limits and ensure smooth execution."
},
"typeVersion": 1
},
{
"id": "0974b693-e026-429e-8396-4f0bf3378140",
"name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote",
"position": [
1344,
2064
],
"parameters": {
"color": 7,
"width": 464,
"height": 368,
"content": "## Billing Lookup & Revenue Comparison\n\nRetrieves billing data from Google Sheets and compares expected vs actual revenue to detect gaps."
},
"typeVersion": 1
},
{
"id": "2a480754-ab48-4cdb-8ef9-0cd0d8d31f5d",
"name": "Sticky Note10",
"type": "n8n-nodes-base.stickyNote",
"position": [
1808,
2080
],
"parameters": {
"color": 7,
"width": 464,
"height": 368,
"content": "## AI Revenue Gap Analysis & Parsing\n\nAnalyzes the revenue gap using Google Gemini and converts output into structured JSON."
},
"typeVersion": 1
},
{
"id": "9a26d968-9de0-46ef-9228-7ee1a12784bc",
"name": "Sticky Note12",
"type": "n8n-nodes-base.stickyNote",
"position": [
2320,
2144
],
"parameters": {
"color": 7,
"width": 384,
"height": 384,
"content": "## Gap Validation & Filtering\n\nChecks if the revenue gap is valid (> 0) before proceeding to alerts and logging."
},
"typeVersion": 1
},
{
"id": "b02027c4-ca45-4c28-8e21-f72458ca8385",
"name": "Sticky Note13",
"type": "n8n-nodes-base.stickyNote",
"position": [
2720,
2160
],
"parameters": {
"color": 7,
"width": 384,
"height": 368,
"content": "## Alert Preparation\n\nBuilds the Slack message directly from parsed LLM analysis data."
},
"typeVersion": 1
},
{
"id": "c2530a16-dd4d-4736-8598-a4b1efff5495",
"name": "Sticky Note14",
"type": "n8n-nodes-base.stickyNote",
"position": [
3152,
2144
],
"parameters": {
"color": 7,
"width": 672,
"height": 368,
"content": "## Slack Notification & Audit Logging\n\nSends alert via Slack and updates Salesforce with audit details for tracking."
},
"typeVersion": 1
},
{
"id": "660723b9-91b3-43ee-8fc5-45459e7e5940",
"name": "Inconsistency Found?1",
"type": "n8n-nodes-base.if",
"position": [
2496,
2336
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "1e7e61db-f5a6-4744-8bb4-478da32a2e06",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ Number($('Compare Revenue Totals').item.json.missing) > 0 }}",
"rightValue": 0
}
]
}
},
"typeVersion": 2.3
},
{
"id": "1b2b63e6-f683-4d3f-a5ef-70327267953f",
"name": "Analyze Revenue Gap",
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"position": [
1936,
2288
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "models/gemini-2.5-flash",
"cachedResultName": "models/gemini-2.5-flash"
},
"options": {},
"messages": {
"values": [
{
"content": "=You are a revenue operations analyst. Analyze the following billing discrepancy and return a JSON object ONLY \u2014 no preamble, no markdown, just raw JSON.\n\nDeal Name: {{ $json.dealName }}\nStage: {{ $json.stageName }}\nContracted Amount: ${{ $json.expected }}\nActually Billed: ${{ $json.actual }}\nRevenue Gap: ${{ $json.missing }}\n\nSeverity rules:\n- critical: gap > $10,000\n- medium: gap between $1,000 and $10,000\n- low: gap < $1,000\nAlways return one of these three values for severity. Never return \"unknown\".\n\nReturn this exact JSON structure:\n{\n \"severity\": \"critical | medium | low\",\n \"rootCause\": \"one sentence explaining the likely cause\",\n \"recommendation\": \"one sentence on what the finance team should do\",\n \"auditNote\": \"a 2-3 sentence note suitable for logging in Salesforce\"\n}"
}
]
},
"builtInTools": {}
},
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
},
"executeOnce": true,
"typeVersion": 1.1
},
{
"id": "49f939dd-fb1f-4e26-a816-68c6dfa097a5",
"name": "Fetch Salesforce Opportunities",
"type": "n8n-nodes-base.salesforce",
"position": [
640,
2352
],
"parameters": {
"limit": 20,
"options": {
"fields": "Id, Name, Amount, AccountId, StageName",
"conditionsUi": {
"conditionValues": [
{
"field": "StageName",
"value": "Closed Won"
}
]
}
},
"resource": "opportunity",
"operation": "getAll"
},
"credentials": {
"salesforceOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "3c82009e-e94f-41a9-9f36-579293347b20",
"name": "Iterate Opportunities",
"type": "n8n-nodes-base.splitInBatches",
"position": [
944,
2352
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "8288c757-df64-4e9e-9a2c-5e3c5cccecd1",
"name": "API Rate Limit Delay",
"type": "n8n-nodes-base.wait",
"position": [
1184,
2368
],
"parameters": {
"amount": 3
},
"typeVersion": 1.1
},
{
"id": "3cdb610c-ba82-45f6-b5c7-5549f1a673a2",
"name": "Lookup Billing Record",
"type": "n8n-nodes-base.googleSheets",
"position": [
1408,
2272
],
"parameters": {
"options": {},
"filtersUI": {
"values": [
{
"lookupValue": "={{ $json.AccountId }}",
"lookupColumn": "Deal ID"
}
]
},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/16Uj9IGM3ZETmdlzGJio7sOuAt6ZWXXr3P-8KklwSwhI/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "16Uj9IGM3ZETmdlzGJio7sOuAt6ZWXXr3P-8KklwSwhI",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/16Uj9IGM3ZETmdlzGJio7sOuAt6ZWXXr3P-8KklwSwhI/edit?usp=drivesdk",
"cachedResultName": "ERP_DATA"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "630c4052-988c-4a0f-81f8-affcd7dc8a93",
"name": "Compare Revenue Totals",
"type": "n8n-nodes-base.code",
"position": [
1632,
2272
],
"parameters": {
"jsCode": "const sfData = $('API Rate Limit Delay').item.json;\nconst sheetItems = items;\n\nconst opportunityId = sfData.Id;\nconst dealId = sfData.AccountId;\nconst dealName = sfData.Name;\nconst stageName = sfData.StageName;\n\nconst expected = Number(sfData.Amount) || 0;\n\nlet actual = 0;\nfor (const item of sheetItems) {\n if (item.json['Deal ID'] === dealId) {\n actual += Number(item.json['Billed Amount']) || 0;\n }\n}\n\nconst missing = expected - actual;\n\nif (missing > 0) {\n return [{\n json: {\n opportunityId,\n dealId,\n dealName,\n stageName,\n expected,\n actual,\n missing\n }\n }];\n}\n\nreturn [];"
},
"typeVersion": 2,
"alwaysOutputData": true
},
{
"id": "08a94f78-4a82-4746-92ba-b9fef8b7840e",
"name": "Prepare Slack Message",
"type": "n8n-nodes-base.code",
"position": [
2864,
2336
],
"parameters": {
"jsCode": "const item = $input.first().json;\n\n// Use emojis and bolding for better readability at a glance\nconst slackMessage = ` *Revenue Leakage Detected*\n*Deal:* ${item.dealName}\n*Status:* ${(item.severity || 'unknown').toUpperCase()}\n\n *Financial Gap:* $${item.missing.toLocaleString()}\n(Contracted: $${item.expected.toLocaleString()} | Billed: $${item.actual.toLocaleString()})\n\n*Root Cause:* ${item.rootCause}\n*Action:* ${item.recommendation}`;\n\nreturn {\n ...item,\n slackMessage\n};"
},
"typeVersion": 2
},
{
"id": "f5280d4f-1da2-4ac5-a7e4-fc8e861479ba",
"name": "Alert Revenue Gap",
"type": "n8n-nodes-base.slack",
"position": [
3264,
2352
],
"parameters": {
"text": "={{ $json.slackMessage }}",
"user": {
"__rl": true,
"mode": "list",
"value": "U0AP3B0UTMH",
"cachedResultName": "vishakag21"
},
"select": "user",
"otherOptions": {},
"authentication": "oAuth2"
},
"credentials": {
"slackOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2.4
},
{
"id": "3915a61c-210c-4c40-b95b-207c06a662f3",
"name": "Flag Audit Discrepancy",
"type": "n8n-nodes-base.salesforce",
"position": [
3520,
2336
],
"parameters": {
"resource": "opportunity",
"operation": "update",
"updateFields": {
"description": "=Revenue Leakage Detected {{ $('Format AI Results').item.json.rootCause }}\n\n{{ $('Format AI Results').item.json.auditNote }}\n\nExpected: {{ $('Format AI Results').item.json.expected }}| Billed: {{ $('Format AI Results').item.json.actual }} | Gap: {{ $('Format AI Results').item.json.missing }}",
"customFieldsUi": {
"customFieldsValues": []
}
},
"opportunityId": "={{ $('Format AI Results').item.json.opportunityId }}"
},
"credentials": {
"salesforceOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "88ba9cea-0475-4403-85d0-75cbc000255a",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1120,
1376
],
"parameters": {
"width": 1248,
"height": 1424,
"content": "\n\n# Multi-System Revenue Leakage Detector\n### This workflow automatically detects revenue leakage by comparing expected revenue from Salesforce with actual billed data from Google Sheets. Once Closed Won deals are fetched, the workflow processes each deal, calculates the revenue gap, and uses Google Gemini AI to analyze the discrepancy. It classifies severity, identifies the root cause, and suggests actions. If a valid gap exists, the workflow sends a real-time alert via Slack and updates the deal record in Salesforce with an audit note. This ensures finance and operations teams can quickly detect, investigate, and resolve missing revenue without manual effort.\n\n# Setup Steps\n\n## Connect Accounts in n8n\nAdd credentials for Salesforce, Google Sheets, Google Gemini, and Slack.\n\n## Add Trigger\nStart the workflow manually or schedule it to run at regular intervals (e.g., daily).\n\n## Fetch Closed Won Deals\nUse Salesforce node to retrieve opportunities where StageName is Closed Won.\n\n## Add Batch Processing & Delay\nUse Split in Batches and Wait node to process deals gradually and avoid API limits.\n\n## Lookup Billing Data\nFetch corresponding billing records from Google Sheets using Deal ID or Account ID.\n\n## Compare Revenue Data\nCalculate expected vs actual revenue and determine the missing amount.\n\n## Add AI Analysis\nUse Google Gemini to analyze the revenue gap and generate severity, root cause, recommendation, and audit note.\n\n## Parse AI Output\nConvert the AI response into structured JSON format for further processing.\n\n## Validate Revenue Gap\nUse an IF condition to continue only if the missing revenue is greater than zero.\n\n## Prepare Alert Message\nFormat a detailed Slack message including deal name, gap, severity, and recommendations.\n\n## Send Slack Notification\nNotify the team via Slack with revenue leakage details.\n\n## Update Salesforce Record\nUpdate the opportunity description in Salesforce with audit notes and discrepancy details.\n\n## Loop Through Records\nContinue processing all deals until the workflow is complete."
},
"typeVersion": 1
},
{
"id": "e5bacec6-ba1d-4fab-9b75-96b7698d8d7e",
"name": "Format AI Results",
"type": "n8n-nodes-base.set",
"position": [
2336,
2336
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "1",
"name": "opportunityId",
"type": "string",
"value": "={{ $(\"Compare Revenue Totals\").item.json.opportunityId }}"
},
{
"id": "2",
"name": "dealId",
"type": "string",
"value": "={{ $(\"Compare Revenue Totals\").item.json.dealId }}"
},
{
"id": "3",
"name": "dealName",
"type": "string",
"value": "={{ $(\"Compare Revenue Totals\").item.json.dealName }}"
},
{
"id": "4",
"name": "stageName",
"type": "string",
"value": "={{ $(\"Compare Revenue Totals\").item.json.stageName }}"
},
{
"id": "5",
"name": "severity",
"type": "string",
"value": "={{ JSON.parse($json.content.parts[0].text || \"{}\").severity }}"
},
{
"id": "6",
"name": "rootCause",
"type": "string",
"value": "={{ JSON.parse($json.content.parts[0].text || \"{}\").rootCause }}"
},
{
"id": "7",
"name": "recommendation",
"type": "string",
"value": "={{ JSON.parse($json.content.parts[0].text || \"{}\").recommendation }}"
},
{
"id": "8",
"name": "auditNote",
"type": "string",
"value": "={{ JSON.parse($json.content.parts[0].text || \"{}\").auditNote }}"
},
{
"id": "9",
"name": "expected",
"type": "number",
"value": "={{ $(\"Compare Revenue Totals\").item.json.expected }}"
},
{
"id": "10",
"name": "actual",
"type": "number",
"value": "={{ $(\"Compare Revenue Totals\").item.json.actual }}"
},
{
"id": "11",
"name": "missing",
"type": "number",
"value": "={{ $(\"Compare Revenue Totals\").item.json.missing }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "dade26fb-b284-4a3c-a05f-1cfca3aa950b",
"name": "Start Manual Revenue Audit",
"type": "n8n-nodes-base.manualTrigger",
"position": [
400,
2352
],
"parameters": {},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "384c3199-f33f-49d6-a64a-2407d1b0f717",
"connections": {
"Alert Revenue Gap": {
"main": [
[
{
"node": "Flag Audit Discrepancy",
"type": "main",
"index": 0
}
]
]
},
"Format AI Results": {
"main": [
[
{
"node": "Inconsistency Found?1",
"type": "main",
"index": 0
}
]
]
},
"Analyze Revenue Gap": {
"main": [
[
{
"node": "Format AI Results",
"type": "main",
"index": 0
}
]
]
},
"API Rate Limit Delay": {
"main": [
[
{
"node": "Lookup Billing Record",
"type": "main",
"index": 0
}
]
]
},
"Inconsistency Found?1": {
"main": [
[
{
"node": "Prepare Slack Message",
"type": "main",
"index": 0
}
],
[
{
"node": "Iterate Opportunities",
"type": "main",
"index": 0
}
]
]
},
"Iterate Opportunities": {
"main": [
[],
[
{
"node": "API Rate Limit Delay",
"type": "main",
"index": 0
}
]
]
},
"Lookup Billing Record": {
"main": [
[
{
"node": "Compare Revenue Totals",
"type": "main",
"index": 0
}
]
]
},
"Prepare Slack Message": {
"main": [
[
{
"node": "Alert Revenue Gap",
"type": "main",
"index": 0
}
]
]
},
"Compare Revenue Totals": {
"main": [
[
{
"node": "Analyze Revenue Gap",
"type": "main",
"index": 0
}
]
]
},
"Flag Audit Discrepancy": {
"main": [
[
{
"node": "Iterate Opportunities",
"type": "main",
"index": 0
}
]
]
},
"Start Manual Revenue Audit": {
"main": [
[
{
"node": "Fetch Salesforce Opportunities",
"type": "main",
"index": 0
}
]
]
},
"Fetch Salesforce Opportunities": {
"main": [
[
{
"node": "Iterate Opportunities",
"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.
googlePalmApigoogleSheetsOAuth2ApisalesforceOAuth2ApislackOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow manually audits Closed Won Salesforce opportunities against billed amounts in Google Sheets, uses Google Gemini to analyze any revenue gap, then alerts a Slack user and writes an audit note back to the Salesforce opportunity. Starts when you run the workflow…
Source: https://n8n.io/workflows/17042/ — 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.
N-0121 AI-Based Sales Playbook Compliance Checker. Uses googleSheets, salesforce, googleGemini, slack. Event-driven trigger; 20 nodes.
Ride Booking App Monitoring. Uses slack, googleGemini, httpRequestTool, googleSheets. Event-driven trigger; 26 nodes.
This workflow pulls open Salesforce opportunities and their last-30-days activities, uses Google Gemini to predict escalation risk per deal, then flags high-risk opportunities in Salesforce and notifi
Supplier Sentiment Intelligence. Uses gmailTrigger, googleGemini, slack, googleSheets. Event-driven trigger; 21 nodes.
Smart Inquiry Responder: AI Drafts & Categorization. Uses formTrigger, googleGemini, googleSheets, slack. Event-driven trigger; 15 nodes.