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 →
{
"name": "Bank Statement Analyzer & Budget Tracker",
"nodes": [
{
"parameters": {
"content": "## \ud83c\udfe6 Bank Statement Analyzer\n\n### What this workflow does\n1. Monitors Gmail for bank statements\n2. Extracts all transactions\n3. Categorizes spending\n4. Calculates savings rate\n5. Identifies large transactions\n6. Sends monthly summary\n\n### Setup steps\n1. Connect Gmail OAuth2 credentials\n2. Get PDF Vector API key from pdfvector.com/api-keys\n3. Create Google Sheet with columns listed below\n4. Update Sheet ID in the Sheets node\n5. Connect Slack and set your channel\n\n### Sheet columns needed\nPeriod Start, Period End, Opening Balance, Closing Balance, Total Income, Total Spending, Savings Rate %, Transaction Count, Processed Date\n\n### Perfect for\n- Personal finance tracking\n- Small business bookkeeping\n- Budget analysis",
"height": 520,
"width": 340,
"color": 5
},
"id": "e4782803-0619-4a8f-8a3a-8072b6f91ea2",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
528,
1408
]
},
{
"parameters": {
"content": "## \ud83d\udcca Spending Categories\n\n- Income\n- Utilities\n- Groceries\n- Dining\n- Shopping\n- Transport\n- Travel\n- Subscriptions\n- Healthcare\n- Entertainment\n- Transfer\n- Other",
"height": 296,
"width": 200
},
"id": "52a1dcbb-1878-4408-a8e9-475c8fafc484",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
1344,
1216
]
},
{
"parameters": {
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
},
"filters": {
"includeSpamTrash": false
}
},
"id": "7bd136e2-5b16-4905-9c4f-59ae210649b3",
"name": "Gmail Trigger",
"type": "n8n-nodes-base.gmailTrigger",
"typeVersion": 1,
"position": [
832,
1552
],
"notes": "Monitor for bank statement emails"
},
{
"parameters": {
"operation": "get",
"messageId": "={{ $json.id }}",
"simple": false,
"options": {
"downloadAttachments": true
}
},
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.2,
"position": [
1008,
1552
],
"id": "117a5e79-3c13-4edf-875e-902f4e66e977",
"name": "Get Statement Attachment",
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "extract",
"inputType": "file",
"prompt": "Extract bank statement data as flat fields. accountNumber, statementStartDate (YYYY-MM-DD), statementEndDate (YYYY-MM-DD), openingBalance (number), closingBalance (number), totalDeposits (number), totalWithdrawals (number), totalIncome (number), totalSpending (number), transactionCount (number), topSpendingCategory (string), topSpendingAmount (number), largeTransactions (comma-separated transactions over $500), groceriesTotal (number), diningTotal (number), transportTotal (number), subscriptionsTotal (number), shoppingTotal (number), utilitiesTotal (number).",
"schema": "{\"type\":\"object\",\"properties\":{\"accountNumber\":{\"type\":\"string\"},\"statementStartDate\":{\"type\":\"string\"},\"statementEndDate\":{\"type\":\"string\"},\"openingBalance\":{\"type\":\"number\"},\"closingBalance\":{\"type\":\"number\"},\"totalDeposits\":{\"type\":\"number\"},\"totalWithdrawals\":{\"type\":\"number\"},\"totalIncome\":{\"type\":\"number\"},\"totalSpending\":{\"type\":\"number\"},\"transactionCount\":{\"type\":\"number\"},\"topSpendingCategory\":{\"type\":\"string\"},\"topSpendingAmount\":{\"type\":\"number\"},\"largeTransactions\":{\"type\":\"string\"},\"groceriesTotal\":{\"type\":\"number\"},\"diningTotal\":{\"type\":\"number\"},\"transportTotal\":{\"type\":\"number\"},\"subscriptionsTotal\":{\"type\":\"number\"},\"shoppingTotal\":{\"type\":\"number\"},\"utilitiesTotal\":{\"type\":\"number\"}},\"additionalProperties\":false}"
},
"id": "96d8d63b-8440-417c-a03d-2ab67b167eb3",
"name": "PDF Vector - Extract Statement",
"type": "n8n-nodes-pdfvector.pdfVector",
"typeVersion": 2,
"position": [
1216,
1552
],
"credentials": {
"pdfVectorApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const data = ($input.first().json?.data || $input.first().json) || {};\n\nconst totalIncome = parseFloat(data.totalIncome) || 0;\nconst totalSpending = parseFloat(data.totalSpending) || 0;\n\nconst savingsRate = totalIncome > 0\n ? ((totalIncome - totalSpending) / totalIncome * 100).toFixed(1)\n : '0.0';\n\nconst categories = [\n { name: 'Groceries', amount: parseFloat(data.groceriesTotal) || 0 },\n { name: 'Dining', amount: parseFloat(data.diningTotal) || 0 },\n { name: 'Transport', amount: parseFloat(data.transportTotal) || 0 },\n { name: 'Subscriptions', amount: parseFloat(data.subscriptionsTotal) || 0 },\n { name: 'Shopping', amount: parseFloat(data.shoppingTotal) || 0 },\n { name: 'Utilities', amount: parseFloat(data.utilitiesTotal) || 0 }\n].filter(c => c.amount > 0).sort((a, b) => b.amount - a.amount);\n\nconst categoryBreakdown = categories.length > 0\n ? categories.map(c =>\n `${c.name}: $${c.amount.toFixed(2)}${totalSpending > 0 ? ` (${((c.amount/totalSpending)*100).toFixed(1)}%)` : ''}`\n ).join('\\n')\n : 'No category breakdown available';\n\nreturn [{ json: {\n accountNumber: data.accountNumber || 'N/A',\n statementStartDate: data.statementStartDate || 'N/A',\n statementEndDate: data.statementEndDate || 'N/A',\n openingBalance: parseFloat(data.openingBalance) || 0,\n closingBalance: parseFloat(data.closingBalance) || 0,\n totalIncome,\n totalSpending,\n savingsRate,\n transactionCount: parseInt(data.transactionCount) || 0,\n topSpendingCategory: data.topSpendingCategory || 'N/A',\n largeTransactions: data.largeTransactions || 'None',\n categoryBreakdown,\n processedAt: new Date().toISOString()\n}}];"
},
"id": "941f6e3a-7ce7-4d61-a252-d7c1da166e6e",
"name": "Analyze Spending",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1408,
1552
]
},
{
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "YOUR_SPREADSHEET_ID",
"mode": "list"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list",
"cachedResultName": "Monthly Summaries"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"Period Start": "={{ $json.statementStartDate }}",
"Period End": "={{ $json.statementEndDate }}",
"Opening Balance": "={{ $json.openingBalance }}",
"Closing Balance": "={{ $json.closingBalance }}",
"Total Income": "={{ $json.totalIncome }}",
"Total Spending": "={{ $json.totalSpending }}",
"Savings Rate %": "={{ $json.savingsRate }}",
"Transaction Count": "={{ $json.transactionCount }}",
"Processed Date": "={{ $json.processedAt.split('T')[0] }}"
},
"schema": [
{
"id": "Period Start",
"displayName": "Period Start",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Period End",
"displayName": "Period End",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Opening Balance",
"displayName": "Opening Balance",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Closing Balance",
"displayName": "Closing Balance",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Total Income",
"displayName": "Total Income",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Total Spending",
"displayName": "Total Spending",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Savings Rate %",
"displayName": "Savings Rate %",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Transaction Count",
"displayName": "Transaction Count",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Processed Date",
"displayName": "Processed Date",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
}
]
},
"options": {}
},
"id": "f3978334-9ab7-4444-8309-a19c4d072cc4",
"name": "Log Monthly Summary",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.4,
"position": [
1616,
1552
]
},
{
"parameters": {
"authentication": "oAuth2",
"select": "channel",
"channelId": {
"__rl": true,
"value": "YOUR_SLACK_CHANNEL_ID",
"mode": "list",
"cachedResultName": "#finance-updates"
},
"text": "=\ud83d\udcca *Monthly Bank Statement Analysis*\n\n*Period:* {{ $('Analyze Spending').item.json.statementStartDate }} \u2014 {{ $('Analyze Spending').item.json.statementEndDate }}\n*Account:* {{ $('Analyze Spending').item.json.accountNumber }}\n\n\ud83d\udcb0 *Summary*\n\u2022 Income: ${{ $('Analyze Spending').item.json.totalIncome }}\n\u2022 Spending: ${{ $('Analyze Spending').item.json.totalSpending }}\n\u2022 Savings Rate: {{ $('Analyze Spending').item.json.savingsRate }}%\n\u2022 Transactions: {{ $('Analyze Spending').item.json.transactionCount }}\n\n\ud83d\udcc8 *Spending by Category*\n{{ $('Analyze Spending').item.json.categoryBreakdown }}\n\n\u26a0\ufe0f *Large Transactions (>$500)*\n{{ $('Analyze Spending').item.json.largeTransactions }}",
"otherOptions": {}
},
"id": "d410c276-d555-4304-9f0a-f4692ce53b19",
"name": "Send Analysis Report",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.2,
"position": [
1824,
1552
]
}
],
"connections": {
"Gmail Trigger": {
"main": [
[
{
"node": "Get Statement Attachment",
"type": "main",
"index": 0
}
]
]
},
"Get Statement Attachment": {
"main": [
[
{
"node": "PDF Vector - Extract Statement",
"type": "main",
"index": 0
}
]
]
},
"PDF Vector - Extract Statement": {
"main": [
[
{
"node": "Analyze Spending",
"type": "main",
"index": 0
}
]
]
},
"Analyze Spending": {
"main": [
[
{
"node": "Log Monthly Summary",
"type": "main",
"index": 0
}
]
]
},
"Log Monthly Summary": {
"main": [
[
{
"node": "Send Analysis Report",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1",
"binaryMode": "separate"
},
"meta": {
"templateCredsSetupCompleted": false
},
"tags": [
{
"name": "Finance"
},
{
"name": "PDF Vector"
},
{
"name": "Bank Statement"
}
]
}
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.
gmailOAuth2pdfVectorApi
About this workflow
Bank Statement Analyzer & Budget Tracker. Uses stickyNote, gmailTrigger, gmail, n8n-nodes-pdfvector. Event-driven trigger; 8 nodes.
Source: https://github.com/khanhduyvt0101/workflows/blob/main/n8n-workflows/bank-statement-analyzer.json — original creator credit. Request a take-down →