This workflow corresponds to n8n.io template #16198 — 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": "TBf9mHiBHoxoBlTf",
"name": "Cloud Cost Spike Detector & Alert",
"tags": [
{
"id": "2V3HXFbv2wqNGm6s",
"name": "Dev",
"createdAt": "2025-06-17T05:42:41.949Z",
"updatedAt": "2025-06-17T05:42:41.949Z"
}
],
"nodes": [
{
"id": "abfa49a7-ced7-442d-ad31-10a720317f85",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-752,
80
],
"parameters": {
"width": 496,
"height": 688,
"content": "## Cloud Cost Spike Detector & Alert\nThis workflow monitors daily cloud spend, compares it against a rolling baseline, and raises an alert whenever costs spike beyond an acceptable threshold. It pulls current spend from the cloud billing API, calculates the percentage increase versus the trailing average, classifies the severity, logs every run, and notifies the finance and DevOps teams before the monthly bill balloons. This prevents surprise invoices and catches runaway resources early.\n\n### How it Works\n\n\t-\tRuns automatically every morning on a schedule.\n\t-\tFetches the latest daily cloud cost from the billing API.\n\t-\tReads the historical baseline from the tracking sheet.\n\t-\tCalculates the percentage change versus the rolling average.\n\t-\tFilters out days where data is missing or zero.\n\t-\tChecks whether the spike crosses the alert threshold.\n\t-\tClassifies severity as Watch, High, or Critical.\n\t-\tCreates an incident ticket for engineering review.\n\t-\tSends a detailed Slack alert and an email summary.\n\t-\tLogs every run back to the sheet for the next baseline.\n\n### Setup Steps\n\n\t1.\tAdd your cloud billing API key as an HTTP header credential.\n\t2.\tConnect the Google Sheets account for baseline and run logging.\n\t3.\tSet the spike threshold percentage in the Calculate node.\n\t4.\tConnect your Slack account and select the alert channel.\n\t5.\tConnect Gmail for the finance email summary.\n\t6.\tConnect Jira for incident ticket creation.\n\t7.\tAdjust the schedule time to match your billing cycle.\n\t8.\tActivate the workflow so it runs automatically."
},
"typeVersion": 1
},
{
"id": "5244795a-4117-4164-9e87-d9d5d560925d",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-240,
80
],
"parameters": {
"color": 7,
"width": 624,
"height": 688,
"content": "## Step 1: Fetch Daily Cost Data\n\nThis section runs on a daily schedule and pulls the latest cloud spend from the billing API, then loads the historical baseline from the tracking sheet. Together these provide the raw numbers needed to detect a spike."
},
"typeVersion": 1
},
{
"id": "a237f882-71dc-4fe6-a0a8-bf68854c0a02",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
400,
80
],
"parameters": {
"color": 7,
"width": 864,
"height": 688,
"content": "## Step 2: Analyze & Detect Spike\n\nThese nodes clean the data, calculate the percentage change versus the rolling baseline, drop invalid rows, and decide whether the increase crosses the alert threshold. Only genuine spikes continue past this phase."
},
"typeVersion": 1
},
{
"id": "0fa44e8b-6aea-4f97-81ec-d6ec08ebc664",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1280,
80
],
"parameters": {
"color": 7,
"width": 832,
"height": 688,
"content": "## Step 3: Alert, Ticket & Log\n\nWhen a spike is confirmed, the workflow classifies the severity, opens an incident ticket for engineering, sends a Slack alert and an email summary to finance, and writes the run back to the sheet so it feeds the next baseline."
},
"typeVersion": 1
},
{
"id": "2a9af70e-b624-4fd8-97c1-46b612340a31",
"name": "Daily Schedule",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-176,
496
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 8
}
]
}
},
"typeVersion": 1.2
},
{
"id": "8643569b-d395-4373-b064-0d584a16f216",
"name": "Fetch Daily Cost",
"type": "n8n-nodes-base.httpRequest",
"position": [
16,
496
],
"parameters": {
"url": "https://api.cloudbilling.example.com/v1/cost/daily",
"options": {},
"sendHeaders": true,
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer YOUR_TOKEN_HERE"
}
]
}
},
"typeVersion": 4.3
},
{
"id": "a41a26f3-817a-4841-b064-3841fdfc81c6",
"name": "Read Cost Baseline",
"type": "n8n-nodes-base.googleSheets",
"position": [
224,
496
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "Baseline",
"cachedResultName": "Baseline"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "YOUR_GOOGLE_SHEET_ID"
}
},
"typeVersion": 4.5
},
{
"id": "7721f373-850f-4edb-95bb-5768dd5d1ac6",
"name": "Calculate Spike",
"type": "n8n-nodes-base.code",
"position": [
448,
496
],
"parameters": {
"jsCode": "// ================================================\n// COST SPIKE ANALYZER\n// Compares today's spend to the rolling baseline\n// ================================================\n\nconst billing = $('Fetch Daily Cost').first().json;\nconst baselineRows = $input.all().map(i => i.json);\n\n// Current spend from the billing API\nconst todayCost = Number(billing.amount || billing.cost || billing.total || 0);\nconst currency = billing.currency || 'USD';\nconst service = billing.service || 'All Services';\nconst account = billing.account_id || billing.accountId || 'primary';\n\n// Build rolling baseline from historical rows (trailing average)\nconst costs = baselineRows\n .map(r => Number(r.dailyCost || r.cost || 0))\n .filter(v => v > 0);\n\nconst sampleSize = costs.length;\nconst baselineAvg = sampleSize > 0\n ? costs.reduce((a, b) => a + b, 0) / sampleSize\n : 0;\n\n// Percentage change vs baseline\nconst pctChange = baselineAvg > 0\n ? ((todayCost - baselineAvg) / baselineAvg) * 100\n : 0;\n\nconst threshold = 25; // alert if spend rises more than 25 percent\n\nconst isValid = todayCost > 0 && baselineAvg > 0 && sampleSize >= 3;\nconst isSpike = isValid && pctChange >= threshold;\n\nreturn [{\n json: {\n runDate: new Date().toISOString().split('T')[0],\n processedAt: new Date().toISOString(),\n service,\n account,\n currency,\n todayCost: Number(todayCost.toFixed(2)),\n baselineAvg: Number(baselineAvg.toFixed(2)),\n pctChange: Number(pctChange.toFixed(1)),\n threshold,\n sampleSize,\n isValid,\n isSpike,\n deltaAmount: Number((todayCost - baselineAvg).toFixed(2))\n }\n}];"
},
"typeVersion": 2
},
{
"id": "3c09b57c-c56d-4cd7-b68b-1bb94f5ab4e6",
"name": "Valid Data",
"type": "n8n-nodes-base.if",
"position": [
640,
496
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "check-valid",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.isValid }}",
"rightValue": true
}
]
}
},
"typeVersion": 2
},
{
"id": "7e2051c5-9bb8-4b5e-a3a5-467786b62dad",
"name": "Is Spike",
"type": "n8n-nodes-base.if",
"position": [
864,
496
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "check-spike",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.isSpike }}",
"rightValue": true
}
]
}
},
"typeVersion": 2
},
{
"id": "66e6c375-d50b-4fe9-8f3c-560df78774ca",
"name": "Classify Severity",
"type": "n8n-nodes-base.set",
"position": [
1104,
496
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "a-sev",
"name": "severity",
"type": "string",
"value": "={{ $json.pctChange >= 100 ? 'Critical' : $json.pctChange >= 50 ? 'High' : 'Watch' }}"
},
{
"id": "a-emoji",
"name": "severityEmoji",
"type": "string",
"value": "={{ $json.pctChange >= 100 ? 'RED' : $json.pctChange >= 50 ? 'ORANGE' : 'YELLOW' }}"
},
{
"id": "a-prio",
"name": "ticketPriority",
"type": "string",
"value": "={{ $json.pctChange >= 100 ? 'Highest' : $json.pctChange >= 50 ? 'High' : 'Medium' }}"
},
{
"id": "a-summary",
"name": "alertSummary",
"type": "string",
"value": "=Cloud cost spike on {{ $json.service }}: {{ $json.todayCost }} {{ $json.currency }} vs baseline {{ $json.baselineAvg }} {{ $json.currency }} (up {{ $json.pctChange }} percent)"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "3a86b68f-7505-4d5a-a6de-fc30790a63ee",
"name": "Create Jira Incident",
"type": "n8n-nodes-base.jira",
"position": [
1344,
496
],
"parameters": {
"project": {
"__rl": true,
"mode": "id",
"value": "YOUR_JIRA_PROJECT_ID"
},
"summary": "={{ $json.severity }} cloud cost spike on {{ $('Calculate Spike').item.json.service }} - up {{ $('Calculate Spike').item.json.pctChange }} percent",
"issueType": {
"__rl": true,
"mode": "id",
"value": "10004"
},
"additionalFields": {
"description": "=Cloud Cost Spike Detected\n\nService: {{ $('Calculate Spike').item.json.service }}\nAccount: {{ $('Calculate Spike').item.json.account }}\nToday Cost: {{ $('Calculate Spike').item.json.todayCost }} {{ $('Calculate Spike').item.json.currency }}\nBaseline Average: {{ $('Calculate Spike').item.json.baselineAvg }} {{ $('Calculate Spike').item.json.currency }}\nChange: {{ $('Calculate Spike').item.json.pctChange }} percent\nDelta: {{ $('Calculate Spike').item.json.deltaAmount }} {{ $('Calculate Spike').item.json.currency }}\nSeverity: {{ $json.severity }}\n\nPlease review active resources and recent deployments for this account."
}
},
"typeVersion": 1
},
{
"id": "230db8cd-b27e-4c6e-9dcf-d4ea9bfaffa5",
"name": "Slack FinOps Alert",
"type": "n8n-nodes-base.slack",
"position": [
1536,
496
],
"parameters": {
"text": "=CLOUD COST SPIKE DETECTED\n\nSeverity: {{ $('Classify Severity').item.json.severityEmoji }} {{ $('Classify Severity').item.json.severity }}\n\n----------------------------------------\n\nService: {{ $('Calculate Spike').item.json.service }}\nAccount: {{ $('Calculate Spike').item.json.account }}\nToday Cost: {{ $('Calculate Spike').item.json.todayCost }} {{ $('Calculate Spike').item.json.currency }}\nBaseline Avg: {{ $('Calculate Spike').item.json.baselineAvg }} {{ $('Calculate Spike').item.json.currency }}\nIncrease: {{ $('Calculate Spike').item.json.pctChange }} percent\nExtra Spend: {{ $('Calculate Spike').item.json.deltaAmount }} {{ $('Calculate Spike').item.json.currency }}\n\n----------------------------------------\n\nIncident ticket created for engineering review.\nAuto-generated by n8n at {{ $('Calculate Spike').item.json.processedAt }}",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "YOUR_SLACK_CHANNEL_ID",
"cachedResultName": "finops-alerts"
},
"otherOptions": {
"includeLinkToWorkflow": false
}
},
"typeVersion": 2.2
},
{
"id": "b7e57876-0351-4919-b5a8-603ed94899ad",
"name": "Email Finance Summary",
"type": "n8n-nodes-base.gmail",
"position": [
1728,
496
],
"parameters": {
"sendTo": "user@example.com",
"message": "=A cloud cost spike has been detected.\n\nSeverity: {{ $('Classify Severity').item.json.severity }}\nService: {{ $('Calculate Spike').item.json.service }}\nAccount: {{ $('Calculate Spike').item.json.account }}\n\nToday Cost: {{ $('Calculate Spike').item.json.todayCost }} {{ $('Calculate Spike').item.json.currency }}\nBaseline Average: {{ $('Calculate Spike').item.json.baselineAvg }} {{ $('Calculate Spike').item.json.currency }}\nIncrease: {{ $('Calculate Spike').item.json.pctChange }} percent\nExtra Spend: {{ $('Calculate Spike').item.json.deltaAmount }} {{ $('Calculate Spike').item.json.currency }}\n\nAn incident ticket has been opened for engineering review. Please monitor the billing dashboard for further movement.\n\nAuto-generated by n8n at {{ $('Calculate Spike').item.json.processedAt }}",
"options": {},
"subject": "={{ $('Classify Severity').item.json.severity }} Cloud Cost Spike - {{ $('Calculate Spike').item.json.service }}",
"emailType": "text"
},
"typeVersion": 2.1
},
{
"id": "b0ad2514-2861-467e-b15a-a88eeba2dbea",
"name": "Log Run to Sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
1920,
496
],
"parameters": {
"columns": {
"value": {
"runDate": "={{ $('Calculate Spike').item.json.runDate }}",
"service": "={{ $('Calculate Spike').item.json.service }}",
"severity": "={{ $('Classify Severity').item.json.severity }}",
"dailyCost": "={{ $('Calculate Spike').item.json.todayCost }}",
"pctChange": "={{ $('Calculate Spike').item.json.pctChange }}",
"baselineAvg": "={{ $('Calculate Spike').item.json.baselineAvg }}"
},
"mappingMode": "defineBelow"
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "Baseline",
"cachedResultName": "Baseline"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "YOUR_GOOGLE_SHEET_ID"
}
},
"typeVersion": 4.5
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "9b4f2830-4731-4c55-bbe0-ca27175c34df",
"connections": {
"Is Spike": {
"main": [
[
{
"node": "Classify Severity",
"type": "main",
"index": 0
}
]
]
},
"Valid Data": {
"main": [
[
{
"node": "Is Spike",
"type": "main",
"index": 0
}
]
]
},
"Daily Schedule": {
"main": [
[
{
"node": "Fetch Daily Cost",
"type": "main",
"index": 0
}
]
]
},
"Calculate Spike": {
"main": [
[
{
"node": "Valid Data",
"type": "main",
"index": 0
}
]
]
},
"Fetch Daily Cost": {
"main": [
[
{
"node": "Read Cost Baseline",
"type": "main",
"index": 0
}
]
]
},
"Classify Severity": {
"main": [
[
{
"node": "Create Jira Incident",
"type": "main",
"index": 0
}
]
]
},
"Read Cost Baseline": {
"main": [
[
{
"node": "Calculate Spike",
"type": "main",
"index": 0
}
]
]
},
"Slack FinOps Alert": {
"main": [
[
{
"node": "Email Finance Summary",
"type": "main",
"index": 0
}
]
]
},
"Create Jira Incident": {
"main": [
[
{
"node": "Slack FinOps Alert",
"type": "main",
"index": 0
}
]
]
},
"Email Finance Summary": {
"main": [
[
{
"node": "Log Run to Sheet",
"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 pull cloud spend from a billing API, compare it to a Google Sheets rolling baseline, and alert on cost spikes by creating a Jira incident, posting to Slack, emailing Finance via Gmail, and logging the run back to Google Sheets. Runs every day at 08:00…
Source: https://n8n.io/workflows/16198/ — 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 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
What This Flow Does