This workflow corresponds to n8n.io template #17293 — we link there as the canonical source.
This workflow follows the Google Sheets → HTTP Request 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": "lpWAQLOGAQF6OhbA",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Intelligent Data Ops Pipeline (ETL + AI Monitoring)",
"tags": [
{
"id": "3uG7zkKJQ0LZ9hAf",
"name": "pdf-intake",
"createdAt": "2026-07-13T21:35:12.540Z",
"updatedAt": "2026-07-13T21:35:12.540Z"
},
{
"id": "GIjZiysRVWgZ5UnE",
"name": "tax-verification",
"createdAt": "2026-07-13T21:35:12.514Z",
"updatedAt": "2026-07-13T21:35:12.514Z"
},
{
"id": "PROuusbhiGmTHZcx",
"name": "google-sheets",
"createdAt": "2026-07-13T21:35:12.592Z",
"updatedAt": "2026-07-13T21:35:12.592Z"
},
{
"id": "V8Tg5cfQ08GamLPG",
"name": "lovable",
"createdAt": "2026-07-13T21:35:12.566Z",
"updatedAt": "2026-07-13T21:35:12.566Z"
},
{
"id": "rEPUTvNtc6oTOvzp",
"name": "groq",
"createdAt": "2026-07-13T21:35:12.485Z",
"updatedAt": "2026-07-13T21:35:12.485Z"
}
],
"nodes": [
{
"id": "1764186e-72b8-4017-82a2-ffca838382a4",
"name": "Hourly Schedule",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-4736,
2960
],
"parameters": {
"rule": {
"interval": [
{
"field": "hours"
}
]
}
},
"typeVersion": 1.3
},
{
"id": "af39ea3c-bbac-4c6e-9e83-747369b3a56c",
"name": "Fetch Stripe Payments",
"type": "n8n-nodes-base.googleSheets",
"position": [
-4528,
2960
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": 485725857,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1BLAZCUAcbZb9zHu0QO5Bo4vDa0UmqMcoTektmYu7jsE/edit#gid=485725857",
"cachedResultName": "stripe_logs_sample.csv"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1BLAZCUAcbZb9zHu0QO5Bo4vDa0UmqMcoTektmYu7jsE",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1BLAZCUAcbZb9zHu0QO5Bo4vDa0UmqMcoTektmYu7jsE/edit?usp=drivesdk",
"cachedResultName": "stripe_logs_sample"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "73e5fd7c-675e-4326-8701-a1f2eb1dbea7",
"name": "Clean & Normalize",
"type": "n8n-nodes-base.code",
"position": [
-4304,
2960
],
"parameters": {
"jsCode": "const rows = $input.all();\n\nreturn rows.map(row => {\n const r = row.json;\n return {\n json: {\n transaction_id: r.transaction_id,\n customer_id: r.customer_id,\n customer_email: r.customer_email,\n amount_usd: parseFloat(r.amount) / 100, // cents \u2192 dollars\n currency: r.currency,\n status: r.status,\n payment_method: r.payment_method,\n country: r.country,\n risk_score: parseInt(r.risk_score),\n created: r.created,\n description: r.description,\n }\n };\n});"
},
"typeVersion": 2
},
{
"id": "785db0fc-bf94-4fd7-af56-8ab7696affc8",
"name": "Store Analytics Record",
"type": "n8n-nodes-base.supabase",
"onError": "continueErrorOutput",
"position": [
-4080,
2960
],
"parameters": {
"tableId": "analytics_data",
"fieldsUi": {
"fieldValues": [
{
"fieldId": "amount",
"fieldValue": "={{ $input.item.json.amount_usd }}"
},
{
"fieldId": "customer_id",
"fieldValue": "={{ $input.item.json.customer_id }}"
},
{
"fieldId": "created_at",
"fieldValue": "={{ $json.created }}"
},
{
"fieldId": "risk_score",
"fieldValue": "={{ $json.risk_score }}"
},
{
"fieldId": "country",
"fieldValue": "={{ $json.country }}"
},
{
"fieldId": "status",
"fieldValue": "={{ $json.status }}"
},
{
"fieldId": "transaction_id",
"fieldValue": "={{ $json.transaction_id }}"
}
]
}
},
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "f75205bb-0ddb-4def-8b3e-4b5b4b88d97d",
"name": "Log Error to Sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
-3408,
3440
],
"parameters": {
"columns": {
"value": {
"severity": "={{ ['Detect Anomalies (AI)', 'Log Incident'].includes($prevNode.name) ? 'high' : 'medium' }}",
"node_name": "={{ $prevNode.name }}",
"timestamp": "={{ new Date().toISOString() }}",
"error_message": "={{ \n $json['error']?.includes('duplicate') || $json['message']?.includes('duplicate') \n ? 'Duplicate ignored \u2014 transaction already exists' \n : $json['error'] ?? $json['message'] ?? 'Unknown error' \n}}",
"transaction_id": "={{ $('Clean & Normalize').first()?.json?.transaction_id ?? $input.first()?.json?.transaction_id ?? 'N/A' }}"
},
"schema": [
{
"id": "timestamp",
"type": "string",
"display": true,
"required": false,
"displayName": "timestamp",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "node_name",
"type": "string",
"display": true,
"required": false,
"displayName": "node_name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "error_message",
"type": "string",
"display": true,
"required": false,
"displayName": "error_message",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "transaction_id",
"type": "string",
"display": true,
"required": false,
"displayName": "transaction_id",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "severity",
"type": "string",
"display": true,
"required": false,
"displayName": "severity",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": 673306969,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1BLAZCUAcbZb9zHu0QO5Bo4vDa0UmqMcoTektmYu7jsE/edit#gid=673306969",
"cachedResultName": "error_logs"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1BLAZCUAcbZb9zHu0QO5Bo4vDa0UmqMcoTektmYu7jsE",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1BLAZCUAcbZb9zHu0QO5Bo4vDa0UmqMcoTektmYu7jsE/edit?usp=drivesdk",
"cachedResultName": "stripe_logs_sample"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "32fe11c0-4750-4dc7-b8d3-2d3f7c57e3f5",
"name": "Build AI Analysis Prompt",
"type": "n8n-nodes-base.code",
"position": [
-3840,
2960
],
"parameters": {
"jsCode": "const rows = $('Store Analytics Record').all().map(i => i.json);\n\nconst prompt = `Analyze these transactions for anomalies:\n${JSON.stringify(rows, null, 2)}\n\nReturn only this JSON structure, no extra text:\n{\n \"anomaly\": true,\n \"type\": \"spike | drop | fraud_pattern | none\",\n \"severity\": \"low | medium | high | none\",\n \"explanation\": \"one sentence summary\"\n}`;\n\nreturn [{\n json: {\n model: \"llama-3.3-70b-versatile\",\n temperature: 0,\n max_tokens: 500,\n messages: [\n {\n role: \"system\",\n content: \"You are a data analyst. Respond only with valid JSON, no extra text, no markdown.\"\n },\n {\n role: \"user\",\n content: prompt\n }\n ]\n }\n}];"
},
"typeVersion": 2
},
{
"id": "074c8599-f79b-49c6-a614-c4ae392e72d8",
"name": "Detect Anomalies (AI)",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueErrorOutput",
"position": [
-3616,
2960
],
"parameters": {
"url": "https://api.groq.com/openai/v1/chat/completions",
"method": "POST",
"options": {},
"jsonBody": "={{ JSON.stringify($json) }}",
"sendBody": true,
"specifyBody": "json",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "groqApi"
},
"credentials": {
"groqApi": {
"name": "<your credential>"
}
},
"typeVersion": 4.4
},
{
"id": "e811e167-a4cd-4d7b-b03e-feedf4575549",
"name": "Parse AI Result",
"type": "n8n-nodes-base.code",
"position": [
-3408,
2960
],
"parameters": {
"jsCode": "const res = $input.item.json ?? {};\n\n// Groq (OpenAI-compatible) returns the text under choices[0].message.content.\nconst raw = res?.choices?.[0]?.message?.content;\n\nif (typeof raw !== 'string' || raw.trim() === '') {\n // No usable AI content \u2014 emit a safe \"no anomaly\" record instead of crashing.\n return [{\n json: {\n anomaly: false,\n type: 'none',\n severity: 'none',\n explanation: 'No analysis returned by the AI model.'\n }\n }];\n}\n\n// Strip markdown fences if Groq adds them despite instructions.\nconst cleaned = raw.replace(/```json|```/g, '').trim();\n\nlet parsed;\ntry {\n parsed = JSON.parse(cleaned);\n} catch (e) {\n parsed = {\n anomaly: false,\n type: 'none',\n severity: 'none',\n explanation: 'Could not parse AI response as JSON: ' + cleaned.slice(0, 200)\n };\n}\n\nreturn [{ json: parsed }];"
},
"typeVersion": 2
},
{
"id": "3472b891-abcb-4c0b-85ee-e8fe1b438702",
"name": "Anomaly Detected?",
"type": "n8n-nodes-base.if",
"position": [
-3184,
2960
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "86edbfe4-0f6d-405a-ba66-43151ac58381",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.anomaly }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.3
},
{
"id": "91401d77-3f5d-4bcb-aa7e-42b4d7c06a05",
"name": "Log Incident",
"type": "n8n-nodes-base.supabase",
"onError": "continueErrorOutput",
"position": [
-2944,
2960
],
"parameters": {
"tableId": "incident_logs",
"fieldsUi": {
"fieldValues": [
{
"fieldId": "anomaly_type",
"fieldValue": "={{ $('Parse AI Result').item.json.type }}"
},
{
"fieldId": "record_id",
"fieldValue": "={{ $('Store Analytics Record').item.json.id }}"
},
{
"fieldId": "ai_label",
"fieldValue": "={{ $('Parse AI Result').item.json.explanation }}"
},
{
"fieldId": "severity",
"fieldValue": "={{ $json.severity }}"
}
]
}
},
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "a399c6f6-9abb-48d5-a266-54ab739e0a7c",
"name": "Send Slack Alert",
"type": "n8n-nodes-base.slack",
"onError": "continueErrorOutput",
"position": [
-2720,
2960
],
"parameters": {
"text": "={{ '\ud83d\udea8 Anomaly detected: ' + $('Parse AI Result').item.json.type + ' | Severity: ' + $('Parse AI Result').item.json.severity + '\\n' + $('Parse AI Result').item.json.explanation }} ",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "C0B0VR1LN3B",
"cachedResultName": "anomally-alerts"
},
"otherOptions": {},
"authentication": "oAuth2"
},
"credentials": {
"slackOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2.4
},
{
"id": "f4616600-c298-4ee9-8c41-21dbe8af1515",
"name": "Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
-4800,
2384
],
"parameters": {
"color": 4,
"width": 2200,
"height": 320,
"content": "## \ud83e\udde0 Intelligent Data Ops Pipeline (ETL + AI Monitoring)\n\nAn hourly ETL pipeline that ingests Stripe payment data, normalizes it, stores it in Supabase, and uses an LLM (Groq) to detect anomalies. Detected anomalies are logged as incidents and pushed to Slack. Every step has error handling that logs failures to a Google Sheet.\n\n**Setup required:** (1) Google Sheets \u2014 source `stripe_logs_sample` sheet + `error_logs` tab \u00b7 (2) Supabase \u2014 tables `analytics_data` and `incident_logs` \u00b7 (3) Groq API credential \u00b7 (4) Slack \u2014 channel for anomaly alerts."
},
"typeVersion": 1
},
{
"id": "0851d916-78dc-43dc-8075-fa52dd789c1b",
"name": "Section 1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-4800,
2752
],
"parameters": {
"color": 5,
"width": 840,
"height": 460,
"content": "## 1 \u00b7 Extract, Clean & Load\n\nRuns hourly. Pulls raw Stripe rows from Google Sheets, normalizes fields (cents \u2192 USD, typed values), then writes each record to the Supabase `analytics_data` table."
},
"typeVersion": 1
},
{
"id": "d8d9ab65-0136-4d5a-a4bf-24f106291eb7",
"name": "Section 2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3920,
2752
],
"parameters": {
"color": 6,
"width": 860,
"height": 460,
"content": "## 2 \u00b7 AI Anomaly Detection\n\nBuilds an analysis prompt from the stored records, sends it to Groq (llama-3.3-70b), parses the JSON response, and branches on whether an anomaly was found."
},
"typeVersion": 1
},
{
"id": "66c9ac21-8c58-43ef-a029-38dc3cf1b3c3",
"name": "Section 3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3024,
2752
],
"parameters": {
"color": 3,
"width": 420,
"height": 460,
"content": "## 3 \u00b7 Log Incident & Alert\n\nWhen an anomaly is detected, the incident is written to Supabase `incident_logs` and a formatted alert is sent to the Slack channel."
},
"typeVersion": 1
},
{
"id": "3eed6a43-d307-4714-b4dd-695016e11849",
"name": "Section 4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3600,
3280
],
"parameters": {
"color": 2,
"width": 520,
"height": 300,
"content": "## 4 \u00b7 Error Handling\n\nEvery critical node routes failures here via its error output. Errors (including deduped duplicates) are appended to the `error_logs` sheet with node name, timestamp and severity."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"availableInMCP": false,
"executionOrder": "v1"
},
"versionId": "f987190e-78ce-4acd-a325-02db7e85b900",
"nodeGroups": [],
"connections": {
"Log Incident": {
"main": [
[
{
"node": "Send Slack Alert",
"type": "main",
"index": 0
}
],
[
{
"node": "Log Error to Sheet",
"type": "main",
"index": 0
}
]
]
},
"Hourly Schedule": {
"main": [
[
{
"node": "Fetch Stripe Payments",
"type": "main",
"index": 0
}
]
]
},
"Parse AI Result": {
"main": [
[
{
"node": "Anomaly Detected?",
"type": "main",
"index": 0
}
]
]
},
"Send Slack Alert": {
"main": [
[],
[
{
"node": "Log Error to Sheet",
"type": "main",
"index": 0
}
]
]
},
"Anomaly Detected?": {
"main": [
[
{
"node": "Log Incident",
"type": "main",
"index": 0
}
]
]
},
"Clean & Normalize": {
"main": [
[
{
"node": "Store Analytics Record",
"type": "main",
"index": 0
}
]
]
},
"Detect Anomalies (AI)": {
"main": [
[
{
"node": "Parse AI Result",
"type": "main",
"index": 0
}
],
[
{
"node": "Log Error to Sheet",
"type": "main",
"index": 0
}
]
]
},
"Fetch Stripe Payments": {
"main": [
[
{
"node": "Clean & Normalize",
"type": "main",
"index": 0
}
]
]
},
"Store Analytics Record": {
"main": [
[
{
"node": "Build AI Analysis Prompt",
"type": "main",
"index": 0
}
],
[
{
"node": "Log Error to Sheet",
"type": "main",
"index": 0
}
]
]
},
"Build AI Analysis Prompt": {
"main": [
[
{
"node": "Detect Anomalies (AI)",
"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.
googleSheetsOAuth2ApigroqApislackOAuth2ApisupabaseApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow runs hourly to read Stripe transaction logs from Google Sheets, normalize and load them into Supabase, then uses Groq (OpenAI-compatible chat completions) to detect anomalies and, when found, records incidents in Supabase and posts an alert to Slack while logging…
Source: https://n8n.io/workflows/17293/ — 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.
Scheduled processes retrieve customer feedback from multiple channels. The system performs sentiment analysis to classify tone, then uses OpenAI models to extract themes, topics, and urgency indicator
This scheduled workflow ingests tender records, checks Google Sheets and Supabase for duplicates, uses Google Gemini to generate an eligibility summary, match score, and bid checklist, then stores the
This workflow functions as an automated "Chief Wellness Officer," helping HR teams and managers prevent employee burnout before it happens. It aggregates data from communication channels and work tool
This workflow acts as a junior finance research analyst for a UK boutique M&A or corporate finance team. It listens for Slack messages, classifies the request, gathers company or market data, and prod
Reddit Monitor Master v3. Uses airtable, supabase, slack, httpRequest. Event-driven trigger; 52 nodes.