This workflow corresponds to n8n.io template #17109 — we link there as the canonical source.
This workflow follows the Agent → 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": "sYmIMBaOF3nZ7GGS",
"name": "Early Payment Opportunity Detection Workflow",
"tags": [],
"nodes": [
{
"id": "56e042df-6956-4be0-a030-05dc7bc2fb17",
"name": "Daily Invoice Scan Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
0,
912
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 9
}
]
}
},
"typeVersion": 1.3
},
{
"id": "4a44dc3e-6792-481e-9c2d-aafbfa1a0d0e",
"name": "Fetch Invoice Data",
"type": "n8n-nodes-base.httpRequest",
"position": [
224,
912
],
"parameters": {
"url": "https://api.npoint.io/21cc33ed45f7427c7eda",
"options": {}
},
"typeVersion": 4.4
},
{
"id": "5c15dff2-194d-49d6-903b-846e6517ee31",
"name": "Calculate Discount ROI",
"type": "n8n-nodes-base.code",
"position": [
448,
912
],
"parameters": {
"jsCode": "return items.map(item => {\n const invoice = item.json;\n const amount = parseFloat(invoice.amount) || 0;\n const discountPercent = parseFloat(invoice.discount_percent) || 0;\n\n const savings = amount * (discountPercent / 100);\n\n const today = new Date();\n const discountDate = new Date(invoice.discount_deadline);\n const dueDate = new Date(invoice.payment_due);\n\n const daysEarly = (dueDate - discountDate) / (1000 * 60 * 60 * 24);\n\n // Annualized ROI of taking the early payment discount\n const roi = daysEarly > 0\n ? (discountPercent / (100 - discountPercent)) * (365 / daysEarly)\n : 0;\n\n // Check if discount deadline is still in the future\n const isEligible = discountDate > today;\n\n invoice.savings = parseFloat(savings.toFixed(2));\n invoice.days_early = parseFloat(daysEarly.toFixed(2));\n invoice.roi = parseFloat(roi.toFixed(4));\n invoice.is_eligible = isEligible;\n invoice.run_date = today.toISOString().split('T')[0];\n\n return { json: invoice };\n});"
},
"typeVersion": 2
},
{
"id": "18cc73dc-2a9c-4ae8-93c8-2eced8d87c33",
"name": "Check Discount Eligibility",
"type": "n8n-nodes-base.if",
"position": [
672,
912
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "eligible-check",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.is_eligible }}",
"rightValue": true
}
]
}
},
"typeVersion": 2.3
},
{
"id": "217d565d-2e49-454d-9804-1176b6a334e3",
"name": "Prepare Invoice Data",
"type": "n8n-nodes-base.set",
"position": [
896,
768
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "94dd04fe-8571-4066-a731-79908c1eb265",
"name": "vendor",
"type": "string",
"value": "={{ $json.vendor }}"
},
{
"id": "010bdf0b-ed4c-4a8c-96d3-9151bf11c1bf",
"name": "amount",
"type": "number",
"value": "={{ $json.amount }}"
},
{
"id": "bfe4990b-2ad4-4c5a-8c4f-afac30641ef3",
"name": "savings",
"type": "number",
"value": "={{ $json.savings }}"
},
{
"id": "8f190b80-dc7a-4633-9bd7-233b91350ec5",
"name": "roi",
"type": "number",
"value": "={{ $json.roi }}"
},
{
"id": "e3052100-1b7c-4543-9263-630b2ce17e4e",
"name": "days_early",
"type": "number",
"value": "={{ $json.days_early }}"
},
{
"id": "ef493a0a-6b5e-4d4c-87d3-1f32444009a4",
"name": "cash_available",
"type": "number",
"value": 500000
},
{
"id": "eb6cff00-0d09-47c7-9c7d-33dfc832a0e5",
"name": "vendor_email",
"type": "string",
"value": "={{ $json.vendor_email }}"
},
{
"id": "run_date_field",
"name": "run_date",
"type": "string",
"value": "={{ $json.run_date }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "2ecfa9c6-0f57-4023-9cf8-a5161ae18e5d",
"name": "AI Payment Decision Engine",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
1120,
656
],
"parameters": {
"text": "=You are a procurement finance analyst.\n\nAnalyze this invoice early payment opportunity:\n\nVendor: {{ $json.vendor }}\nInvoice Amount: ${{ $json.amount }}\nPotential Savings: ${{ $json.savings }}\nAnnualized ROI: {{ ($json.roi * 100).toFixed(2) }}%\nDays Early Payment: {{ $json.days_early }}\nAvailable Cash: ${{ $json.cash_available }}\n\nDecision Rules:\n- HIGH: ROI > 15% AND amount < 50% of available cash\n- MEDIUM: ROI between 5%-15% OR amount is 50-80% of available cash\n- LOW: ROI < 5% OR amount exceeds 80% of available cash\n\nTasks:\n1. Decide whether early payment is recommended (YES or NO).\n2. Assign priority: HIGH, MEDIUM, or LOW.\n3. Write a concise one-sentence reason using ROI and cash availability.\n\nIMPORTANT:\n- Return ONLY valid raw JSON\n- No markdown\n- No ```json\n- No explanation\n- No extra text\n- No line breaks\n\nRequired JSON schema:\n{\n \"recommendation\": \"YES\",\n \"priority\": \"HIGH\",\n \"reason\": \"High annualized ROI exceeds threshold and invoice amount is comfortably within available cash reserves.\"\n}",
"options": {},
"promptType": "define"
},
"typeVersion": 3.1
},
{
"id": "98ff813c-08fe-4786-b9ea-4e56b59617ef",
"name": "Groq AI Model",
"type": "@n8n/n8n-nodes-langchain.lmChatGroq",
"position": [
1200,
880
],
"parameters": {
"model": "llama-3.3-70b-versatile",
"options": {}
},
"typeVersion": 1
},
{
"id": "4c14e6d7-9a0e-4e96-b829-cc85ef6436eb",
"name": "Clean AI Response",
"type": "n8n-nodes-base.code",
"position": [
1472,
768
],
"parameters": {
"jsCode": "return items.map(item => {\n let output = item.json.output || '';\n\n // Strip markdown code fences if present\n output = output\n .replace(/```json\\s*/gi, '')\n .replace(/```\\s*/g, '')\n .trim();\n\n // Extract first JSON object found in string (handles extra text)\n const jsonMatch = output.match(/\\{[\\s\\S]*?\\}/);\n if (!jsonMatch) {\n // Fallback: assign MEDIUM if parsing fails\n return {\n json: {\n ...item.json,\n recommendation: 'NO',\n priority: 'LOW',\n reason: 'Could not parse AI response - defaulting to LOW priority.'\n }\n };\n }\n\n let parsed;\n try {\n parsed = JSON.parse(jsonMatch[0]);\n } catch (e) {\n parsed = {\n recommendation: 'NO',\n priority: 'LOW',\n reason: 'JSON parse error - defaulting to LOW priority.'\n };\n }\n\n // Normalize values to uppercase\n parsed.recommendation = (parsed.recommendation || 'NO').toUpperCase().trim();\n parsed.priority = (parsed.priority || 'LOW').toUpperCase().trim();\n\n return {\n json: {\n ...item.json,\n ...parsed\n }\n };\n});"
},
"typeVersion": 2
},
{
"id": "f44a69e9-6093-4bfe-8b28-df173d994d6f",
"name": "Save Eligible Invoice Log",
"type": "n8n-nodes-base.googleSheets",
"position": [
1696,
768
],
"parameters": {
"columns": {
"value": {
"ROI": "={{ $('Prepare Invoice Data').item.json.roi }}",
"Date": "={{ $('Prepare Invoice Data').item.json.run_date }}",
"Amount": "={{ $('Prepare Invoice Data').item.json.amount }}",
"Reason": "={{ $json.reason }}",
"Vendor": "={{ $('Prepare Invoice Data').item.json.vendor }}",
"Savings": "={{ $('Prepare Invoice Data').item.json.savings }}",
"Priority": "={{ $json.priority }}",
"Vendor_Email": "={{ $('Prepare Invoice Data').item.json.vendor_email }}",
"Recommendation": "={{ $json.recommendation }}"
},
"schema": [
{
"id": "Date",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Date",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Vendor",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Vendor",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Amount",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Amount",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Savings",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Savings",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "ROI",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "ROI",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Priority",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Priority",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Vendor_Email",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Vendor_Email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Recommendation",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Recommendation",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Reason",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Reason",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": true
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1kJDVxtoJ3OkTQtjR9VY8sgJuW-8E85JK1bxFwO-MEpM/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1kJDVxtoJ3OkTQtjR9VY8sgJuW-8E85JK1bxFwO-MEpM",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1kJDVxtoJ3OkTQtjR9VY8sgJuW-8E85JK1bxFwO-MEpM/edit?usp=drivesdk",
"cachedResultName": "Track Savings"
}
},
"typeVersion": 4.7
},
{
"id": "26153b21-8394-491f-b396-480361812342",
"name": "Check High Priority Opportunity",
"type": "n8n-nodes-base.if",
"position": [
1920,
768
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "priority-high-check",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $('Clean AI Response').item.json.priority }}",
"rightValue": "HIGH"
},
{
"id": "recommendation-yes-check",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $('Clean AI Response').item.json.recommendation }}",
"rightValue": "=YES"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "0798bf8f-e235-4ef5-aba8-ff5f4fd55b1f",
"name": "Combine High Priority Invoices",
"type": "n8n-nodes-base.aggregate",
"position": [
2144,
768
],
"parameters": {
"options": {},
"aggregate": "aggregateAllItemData"
},
"typeVersion": 1
},
{
"id": "ca77547f-6ded-4c07-b593-f900a740e356",
"name": "Mark Expired Invoices",
"type": "n8n-nodes-base.code",
"position": [
896,
1216
],
"parameters": {
"jsCode": "// Mark expired invoices and log skip reason\n\nconst today = new Date();\nconst runDate = today.toISOString().split('T')[0];\n\nreturn items.map(item => {\n return {\n json: {\n ...item.json,\n invoice_status: 'EXPIRED',\n skip_reason: 'Discount deadline has passed',\n action_taken: 'NO_ACTION',\n run_date: runDate\n }\n };\n});"
},
"typeVersion": 2
},
{
"id": "65b000f5-ae57-417c-bb06-def00c22e778",
"name": "Save Expired Invoice Log",
"type": "n8n-nodes-base.googleSheets",
"position": [
1376,
1216
],
"parameters": {
"columns": {
"value": {
"Date": "={{ $json.run_date }}",
"Amount": "={{ $json.amount }}",
"Vendor": "={{ $json.vendor }}",
"Deadline": "={{ $json.discount_deadline }}",
"Discount %": "={{ $json.discount_percent }}",
"Skip Reason": "={{ $json.skip_reason }}"
},
"schema": [
{
"id": "Date",
"type": "string",
"display": true,
"required": false,
"displayName": "Date",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Vendor",
"type": "string",
"display": true,
"required": false,
"displayName": "Vendor",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Amount",
"type": "string",
"display": true,
"required": false,
"displayName": "Amount",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Discount %",
"type": "string",
"display": true,
"required": false,
"displayName": "Discount %",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Deadline",
"type": "string",
"display": true,
"required": false,
"displayName": "Deadline",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Skip Reason",
"type": "string",
"display": true,
"required": false,
"displayName": "Skip Reason",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": 197677669,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1kJDVxtoJ3OkTQtjR9VY8sgJuW-8E85JK1bxFwO-MEpM/edit#gid=197677669",
"cachedResultName": "Sheet2"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1kJDVxtoJ3OkTQtjR9VY8sgJuW-8E85JK1bxFwO-MEpM",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1kJDVxtoJ3OkTQtjR9VY8sgJuW-8E85JK1bxFwO-MEpM/edit?usp=drivesdk",
"cachedResultName": "Track Savings"
}
},
"typeVersion": 4.7
},
{
"id": "a063364f-c87d-48b3-9e40-00583ef000a7",
"name": "Create Summary Email",
"type": "n8n-nodes-base.code",
"position": [
2368,
768
],
"parameters": {
"jsCode": "// Build a digest email body from all HIGH priority invoices\nconst allItems = items[0].json.data || [];\n\nif (allItems.length === 0) {\n return [{ json: { emailBody: 'No HIGH priority early payment opportunities found today.', count: 0 } }];\n}\n\nlet totalSavings = 0;\nlet rows = '';\n\nallItems.forEach((inv, idx) => {\n const amount = parseFloat(inv.amount) || 0;\n const savings = parseFloat(inv.savings) || 0;\n const roi = parseFloat(inv.roi) || 0;\n totalSavings += savings;\n\n rows += `\n#${idx + 1} | Vendor: ${inv.vendor}\n Invoice Amount : $${amount.toLocaleString()}\n Potential Savings : $${savings.toFixed(2)}\n Annualized ROI : ${(roi * 100).toFixed(2)}%\n Days Early : ${inv.days_early}\n Reason : ${inv.reason}\n Vendor Email : ${inv.vendor_email || 'N/A'}\n`;\n});\n\nconst emailBody = `\n\n EARLY PAYMENT OPPORTUNITY DIGEST \u2014 ${new Date().toDateString()}\n\n\nTotal HIGH Priority Opportunities : ${allItems.length}\nTotal Potential Savings Today : $${totalSavings.toFixed(2)}\nAvailable Cash : $500,000\n\nOPPORTUNITIES:\n${rows}\n\nThis digest was generated automatically by your Early Payment Detection workflow.\n`;\n\nreturn [{ json: { emailBody, count: allItems.length, totalSavings: totalSavings.toFixed(2) } }];"
},
"typeVersion": 2
},
{
"id": "229a830e-bcf4-4345-9934-e5ff442b3429",
"name": "Email Finance Summary",
"type": "n8n-nodes-base.gmail",
"position": [
2592,
768
],
"parameters": {
"message": "={{ $json.emailBody }}",
"options": {},
"subject": "= Early Payment Digest: {{ $json.count }} HIGH ROI Opportunities | Savings ${{ $json.totalSavings }} \u2014 {{ $now.format('YYYY-MM-DD') }}",
"emailType": "text"
},
"typeVersion": 2.2
},
{
"id": "198f1dc6-736b-4296-87d3-5032f0b96e38",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-48,
176
],
"parameters": {
"width": 848,
"height": 496,
"content": "## EARLY PAYMENT DETECTION WORKFLOW\n\n## How it works:\nThis workflow runs daily to analyze vendor invoices and detect early payment discount opportunities. It fetches invoice data from an API, calculates savings, ROI, and checks discount eligibility based on deadlines. The data is then structured and passed to an AI engine that classifies each invoice as HIGH, MEDIUM, or LOW priority based on financial rules and cash availability. Results are cleaned, stored in Google Sheets, and HIGH priority opportunities are aggregated into a final summary. A consolidated finance report is then automatically emailed to the finance team.\n\n## Setup steps:\n1. Connect Google Sheets credentials\n2. Connect Groq AI credentials\n3. Update invoice API URL\n4. Configure finance email recipient\n5. Activate workflow schedule\n\n"
},
"typeVersion": 1
},
{
"id": "2d9ce191-2a47-4ff8-b40f-0b70b811ccf5",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-64,
768
],
"parameters": {
"color": 7,
"width": 880,
"height": 368,
"content": "## Invoice Ingestion & ROI Calculation\n\nHandles invoice fetching, calculates savings/ROI, and filters only valid discount opportunities for further processing."
},
"typeVersion": 1
},
{
"id": "61bd5ef9-b610-4fcf-b4f2-4fb9e2b6ec51",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
848,
512
],
"parameters": {
"color": 7,
"width": 1008,
"height": 496,
"content": "## Data Preparation & AI Decision Engine\nPrepares structured invoice data, sends it to AI for evaluation, cleans response, and stores approved opportunities."
},
"typeVersion": 1
},
{
"id": "df7c6aa1-32a7-4069-9800-9b8b05751bc5",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1888,
592
],
"parameters": {
"color": 7,
"width": 880,
"height": 384,
"content": "## High Priority Filter & Reporting Pipeline\nCollects HIGH ROI invoices, aggregates them, and generates a finance email digest for decision-makers."
},
"typeVersion": 1
},
{
"id": "fbb50923-fcfb-4ce5-9f5e-a4329c2587f6",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
848,
1072
],
"parameters": {
"color": 7,
"width": 752,
"height": 304,
"content": "## Expired Invoice Tracking\nSeparates expired invoices for audit tracking and records skipped opportunities."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "ccf2e37b-5dca-4e39-b5d0-fa575f4a973d",
"connections": {
"Groq AI Model": {
"ai_languageModel": [
[
{
"node": "AI Payment Decision Engine",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Clean AI Response": {
"main": [
[
{
"node": "Save Eligible Invoice Log",
"type": "main",
"index": 0
}
]
]
},
"Fetch Invoice Data": {
"main": [
[
{
"node": "Calculate Discount ROI",
"type": "main",
"index": 0
}
]
]
},
"Create Summary Email": {
"main": [
[
{
"node": "Email Finance Summary",
"type": "main",
"index": 0
}
]
]
},
"Prepare Invoice Data": {
"main": [
[
{
"node": "AI Payment Decision Engine",
"type": "main",
"index": 0
}
]
]
},
"Mark Expired Invoices": {
"main": [
[
{
"node": "Save Expired Invoice Log",
"type": "main",
"index": 0
}
]
]
},
"Calculate Discount ROI": {
"main": [
[
{
"node": "Check Discount Eligibility",
"type": "main",
"index": 0
}
]
]
},
"Save Eligible Invoice Log": {
"main": [
[
{
"node": "Check High Priority Opportunity",
"type": "main",
"index": 0
}
]
]
},
"AI Payment Decision Engine": {
"main": [
[
{
"node": "Clean AI Response",
"type": "main",
"index": 0
}
]
]
},
"Check Discount Eligibility": {
"main": [
[
{
"node": "Prepare Invoice Data",
"type": "main",
"index": 0
}
],
[
{
"node": "Mark Expired Invoices",
"type": "main",
"index": 0
}
]
]
},
"Daily Invoice Scan Trigger": {
"main": [
[
{
"node": "Fetch Invoice Data",
"type": "main",
"index": 0
}
]
]
},
"Combine High Priority Invoices": {
"main": [
[
{
"node": "Create Summary Email",
"type": "main",
"index": 0
}
]
]
},
"Check High Priority Opportunity": {
"main": [
[
{
"node": "Combine High Priority Invoices",
"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 fetch invoice data from an HTTP endpoint, calculate early-payment savings and annualized ROI, and use Groq (Llama 3.3) to prioritize eligible discounts. It logs eligible and expired invoices to Google Sheets and emails a digest of HIGH-priority…
Source: https://n8n.io/workflows/17109/ — 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 automates invoicing and payment follow-ups using Google Sheets, PDFShift, Groq (LLM), Gmail, and Telegram, sending initial invoices with PDF attachments, scheduling overdue reminders at
This scheduled workflow pulls recent resolved procurement tickets and chat queries, clusters similar issues, and uses Groq LLMs to analyze patterns and draft Markdown knowledge base articles, then ema
This workflow automatically monitors competitor product prices stored in Google Sheets. It scrapes product pages, extracts pricing and offer data using AI, and compares it with historical values. Base
Supplier Capacity Risk Alert Workflow. Uses httpRequest, lmChatGroq, agent, gmail. Scheduled trigger; 28 nodes.
This workflow automatically generates 2–3 high-quality Indian stock investment ideas daily by combining trending stock data + latest market news, processing it with AI (Groq/OpenAI), avoiding duplicat