AutomationFlowsAI & RAG › Track Expenses From Receipt Photos with Ai, Google Sheets & Slack Reports

Track Expenses From Receipt Photos with Ai, Google Sheets & Slack Reports

Bytakuma @takuma on n8n.io

This template is for individuals or households who want to: Easily digitize and track their spending from physical receipts. Automate budget management and financial reporting. Get quick insights into their spending habits on a daily and monthly basis. Stay informed about their…

Webhook trigger★★★★☆ complexityAI-powered18 nodesGoogle SheetsSlackOpenRouter ChatAgent
AI & RAG Trigger: Webhook Nodes: 18 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #10970 — we link there as the canonical source.

This workflow follows the Agent → 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 →

Download .json
{
  "id": "tXIAVEIkPlrlzwO9",
  "name": "Photo Receipt AI for Budget & Slack Alerts",
  "tags": [],
  "nodes": [
    {
      "id": "d9e934b1-3ddb-4312-8556-03e0bc75f001",
      "name": "Receipt Photo Upload",
      "type": "n8n-nodes-base.webhook",
      "position": [
        0,
        256
      ],
      "parameters": {
        "path": "receipt Text",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 2.1
    },
    {
      "id": "be58ef74-b716-4f12-875d-aa09572f9b3c",
      "name": "Add to Budget Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        800,
        256
      ],
      "parameters": {
        "columns": {
          "value": {
            "Date": "={{ $json[\"Date\"] }}",
            "Items": "={{ $json[\"Items\"] }}",
            "Store": "={{ $json[\"Store\"] }}",
            "Amount": "={{ $json[\"Amount\"] }}"
          },
          "schema": [
            {
              "id": "Date",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Store",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Store",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Items",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Items",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Amount",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Amount",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Data"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1hzLc4ioe2H24wTG3TSQNMinFPhoi6LdlIJkyCb0yxNk/edit#gid=0",
          "cachedResultName": "Budget"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1hzLc4ioe2H24wTG3TSQNMinFPhoi6LdlIJkyCb0yxNk",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1hzLc4ioe2H24wTG3TSQNMinFPhoi6LdlIJkyCb0yxNk/edit?usp=drivesdk",
          "cachedResultName": "Budget"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "51956fcd-1422-4e1c-88fb-ea7a5f921fb2",
      "name": "Code in JavaScript2",
      "type": "n8n-nodes-base.code",
      "position": [
        1024,
        256
      ],
      "parameters": {
        "jsCode": "// Get all rows from the previous Google Sheets (Read) node\nconst items = $items();\n\n// If the sheet is empty, do nothing\nif (items.length === 0) {\n  return [{\n    json: {\n      message: 'No household budget data yet.'\n    }\n  }];\n}\n\n// Function to convert date strings \"YYYY/MM/DD\" or \"YYYY-MM-DD\" to Date objects\nfunction parseDate(str) {\n  if (!str) return null;\n  const parts = str.split(/[\\/\\-]/).map(Number);\n  if (parts.length < 3) return null;\n  const [y, m, d] = parts;\n  return new Date(y, m - 1, d);\n}\n\n// Assume the last row is the newly added record and aggregate by its month\nconst lastRow = items[items.length - 1].json;\nconst lastDateStr = lastRow['Date'];\nconst targetDate = parseDate(lastDateStr) || new Date();\n\n// Target Year-Month (e.g., \"2025-04\")\nconst targetYm = `${targetDate.getFullYear()}-${('0' + (targetDate.getMonth() + 1)).slice(-2)}`;\n\n// Calculate total for the current month\nlet total = 0;\nfor (const { json: row } of items) {\n  const ds = row['Date'];\n  const d = parseDate(ds);\n  if (!d) continue;\n\n  const ym = `${d.getFullYear()}-${('0' + (d.getMonth() + 1)).slice(-2)}`;\n  if (ym === targetYm) {\n    const price = Number(row['Amount'] || 0);\n    if (!isNaN(price)) {\n      total += price;\n    }\n  }\n}\n\n// \u2605 Monthly budget (change this to your desired amount)\nconst budget = 30000;\n\nconst remaining = budget - total;\n\n// Assemble message for Slack\nconst lines = [\n  `\ud83d\udcca Household Budget for ${targetYm}`,\n  `Budget: ${budget.toLocaleString()} JPY`,\n  `Total Spent: ${total.toLocaleString()} JPY`,\n  `Remaining Budget: ${remaining.toLocaleString()} JPY`,\n];\n\nif (remaining < 0) {\n  lines.push(`\u26a0\ufe0f You have exceeded your budget by ${Math.abs(remaining).toLocaleString()} JPY.`);\n}\n\nreturn [{\n  json: {\n    month: targetYm,\n    budget: budget,\n    totalSpent: total,\n    remainingBudget: remaining,\n    message: lines.join('\\n')\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "8a6fa2ef-887e-4695-94d3-7275b5dd2410",
      "name": "Send a message",
      "type": "n8n-nodes-base.slack",
      "position": [
        1600,
        256
      ],
      "parameters": {
        "text": "={{ $json.output }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C09SMMZGXMM",
          "cachedResultName": ""
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "typeVersion": 2.3
    },
    {
      "id": "843ef5f2-cba2-4629-bde0-f19355739d93",
      "name": "OpenRouter Chat Model1",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        992,
        -64
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "c0ea41d0-15e8-461f-8162-f6762444f75b",
      "name": "Code in JavaScript",
      "type": "n8n-nodes-base.code",
      "position": [
        576,
        256
      ],
      "parameters": {
        "jsCode": "// Assuming AI Agent output is in $json.output\nlet raw = $json.output;\n\n// Convert to JSON if it's a string\nif (typeof raw === 'string') {\n  raw = raw.trim();\n\n  // Remove ```json ~ ``` if present\n  raw = raw.replace(/^```(?:json)?/i, '').replace(/```$/i, '').trim();\n\n  try {\n    raw = JSON.parse(raw);\n  } catch (error) {\n    throw new Error('Could not parse AI Agent output as JSON: ' + error.message + '\\nraw=' + raw);\n  }\n}\n\n// Ensure it's an array, even if only one item\nconst data = Array.isArray(raw) ? raw : [raw];\n\n// Format into n8n's items array structure\nconst items = data.map(entry => {\n  return {\n    json: {\n      Date: entry['Date'],\n      Store: entry['Store'],\n      Items: entry['Items'],\n      Amount: entry['Amount']\n    }\n  };\n});\n\nreturn items;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "fe796bfc-a632-4d8c-9ba3-2c76b768655d",
      "name": "OpenRouter Chat Model2",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        296,
        480
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "a204a1cb-3cb6-4ea0-8e79-cbd6b301cd30",
      "name": "Parse Receipt",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        224,
        256
      ],
      "parameters": {
        "text": "={{ $json.body[''].text }}",
        "options": {
          "systemMessage": "You are an expert in receipt analysis. From the following text, accurately extract the date, store name, purchased items, and amount, and output them in the following format:\n\n[\n  {\n    \"Date\": \"YYYY/MM/DD\",\n    \"Store\": \"Store Name\",\n    \"Items\": \"Item Name\",\n    \"Amount\": Amount\n  }\n]\n\nIf there are multiple items, return multiple objects. Do not include any extra comments or sentences, return only JSON. Group items with the same date into a single entry."
        },
        "promptType": "define"
      },
      "typeVersion": 3
    },
    {
      "id": "8515213f-2fe5-4334-bc8c-7f6b6739c85d",
      "name": "Report Budget",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1248,
        256
      ],
      "parameters": {
        "text": "={{ $json[\"message\"] }}",
        "options": {
          "systemMessage": "You are a specialist in drafting polished content for Slack. Summarize the total amount spent and the remaining budget. Example:\nCurrent spending: 1000 JPY\nRemaining budget this month: 10000 JPY"
        },
        "promptType": "define"
      },
      "typeVersion": 3
    },
    {
      "id": "78cafaa5-7ef7-4441-affb-ca5494ce10eb",
      "name": "Daily Report Trigger",
      "type": "n8n-nodes-base.cron",
      "position": [
        240,
        -288
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "4baa9d88-2664-4dbf-b59c-d673c743564b",
      "name": "Get Budget Sheet (Daily)",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        464,
        -288
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1hzLc4ioe2H24wTG3TSQNMinFPhoi6LdlIJkyCb0yxNk/edit#gid=0",
          "cachedResultName": "Budget"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1hzLc4ioe2H24wTG3TSQNMinFPhoi6LdlIJkyCb0yxNk",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1hzLc4ioe2H24wTG3TSQNMinFPhoi6LdlIJkyCb0yxNk/edit#gid=0",
          "cachedResultName": "Budget"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "a1e12212-03c5-4f11-bf73-4191b7a52c41",
      "name": "Monthly Analysis",
      "type": "n8n-nodes-base.code",
      "position": [
        688,
        -288
      ],
      "parameters": {
        "jsCode": "// All rows from the sheet\nconst items = $items();\nif (items.length === 0) {\n  return [{\n    json: {\n      message: 'No household budget data yet.'\n    }\n  }];\n}\n\n// Date parsing function\nfunction parseDate(str) {\n  if (!str) return null;\n  const parts = str.split(/[\\/\\-]/).map(Number);\n  if (parts.length < 3) return null;\n  const [y, m, d] = parts;\n  return new Date(y, m - 1, d);\n}\n\n// The month of the latest row's date will be the target month\nconst lastRow = items[items.length - 1].json;\nconst lastDate = parseDate(lastRow['Date']) || new Date();\nconst targetYm = `${lastDate.getFullYear()}-${('0' + (lastDate.getMonth() + 1)).slice(-2)}`;\n\n// For aggregation\nlet total = 0;\nconst byStore = {};  // Per store\nconst byItem = {};   // Per item\nconst byDate = {};   // Per date\n\nfor (const { json: row } of items) {\n  const d = parseDate(row['Date']);\n  if (!d) continue;\n\n  const ym = `${d.getFullYear()}-${('0' + (d.getMonth() + 1)).slice(-2)}`;\n  if (ym !== targetYm) continue; // Skip if not current month\n\n  const price = Number(row['Amount'] || 0);\n  if (isNaN(price)) continue;\n\n  const dateKey = row['Date'];\n  const store = row['Store'] || 'Unknown Store';\n  const item = row['Items'] || 'Unknown Item';\n\n  total += price;\n\n  byDate[dateKey] = (byDate[dateKey] || 0) + price;\n  byStore[store] = (byStore[store] || 0) + price;\n  byItem[item] = (byItem[item] || 0) + price;\n}\n\n// Daily average\nconst days = Object.keys(byDate).length || 1;\nconst avgPerDay = Math.round(total / days);\n\n// Top N ranking function\nfunction topN(map, n) {\n  return Object.entries(map)\n    .sort((a, b) => b[1] - a[1])\n    .slice(0, n)\n    .map(([key, value]) => ({\n      name: key,\n      amount: value\n    }));\n}\n\nconst topStores = topN(byStore, 3);\nconst topItems = topN(byItem, 3);\nconst topDays = topN(byDate, 3);\n\nreturn [{\n  json: {\n    month: targetYm,\n    monthlyTotal: total,\n    dailyAverage: avgPerDay,\n    top3Stores: topStores,\n    top3Items: topItems,\n    top3SpendingDays: topDays\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "ca04ca63-5377-4f27-bc2f-87cfecee94d1",
      "name": "Monthly Report",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        912,
        -288
      ],
      "parameters": {
        "text": "={{`Create a report from this month's (${ $json[\"month\"] }) household budget data.\n\n- Total monthly expenditure: ${$json[\"monthlyTotal\"]} JPY\n- Daily average: ${$json[\"dailyAverage\"]} JPY\n- Top 3 stores by spending: ${JSON.stringify($json[\"top3Stores\"])}\n- Top 3 items by spending: ${JSON.stringify($json[\"top3Items\"])}\n- Top 3 days with most spending: ${JSON.stringify($json[\"top3SpendingDays\"])}\n\nSummarize this month's trends and provide simple advice in English, using bullet points and short comments. Keep it to a readable length and use a polite, informal tone.`}}",
        "options": {
          "systemMessage": "You are an assistant that analyzes household budget data and writes clear English reports. Based on the given numbers, describe the trends and offer simple advice. Keep it to a readable length and use a polite, informal tone."
        },
        "promptType": "define"
      },
      "typeVersion": 3
    },
    {
      "id": "6d340421-5082-4dc2-98e8-8aadb85acbc9",
      "name": "Send monthly report",
      "type": "n8n-nodes-base.slack",
      "position": [
        1264,
        -288
      ],
      "parameters": {
        "text": "={{ $json.output }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C09SMMZGXMM",
          "cachedResultName": ""
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "typeVersion": 2.3
    },
    {
      "id": "1e37b78a-f1ce-46e6-94c2-3efd52f140b1",
      "name": "OpenRouter Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        1320,
        480
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "fc9c9473-198e-4d57-9968-f6f69030a385",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -656,
        -272
      ],
      "parameters": {
        "width": 496,
        "height": 592,
        "content": "## What it does\nThis workflow automates your household budget tracking in several steps:\n1.  **Receipt Photo Upload**: You upload a photo of your receipt to a webhook.\n2.  **Parse Receipt**: An AI agent extracts the date, store name, purchased items, and total amount from the receipt text.\n3.  **Add to Budget Sheet**: The extracted data is then appended to your designated Google Sheet.\n4.  **Daily Budget Report**: After each receipt entry, the workflow calculates your current month's spending, remaining budget, and sends a summary to Slack.\n5.  **Monthly Budget Report**: Once a day (triggered by a cron job), it reads all budget data for the current month from Google Sheets, performs an analysis (total spending, daily average, top stores, items, and spending days), and sends a comprehensive report to Slack.\n### Requirements\n-   **n8n Account**: Self-hosted or Cloud.\n-   **Google Sheets**: A Google Sheet set up with columns for \"Date\", \"Store\", \"Items\", and \"Amount\".\n-   **Slack Workspace**: A Slack channel where you want to receive budget updates.\n-   **OpenRouter Account**: An API key for OpenRouter to use their chat models."
      },
      "typeVersion": 1
    },
    {
      "id": "017435d3-43cb-4322-9697-024de9511297",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        48,
        64
      ],
      "parameters": {
        "color": 7,
        "width": 1600,
        "height": 464,
        "content": "### Steps\n1.  **Google Sheets Setup**: Create/use a Google Sheet, share with n8n credentials, and select it in the workflow nodes.\n2.  **OpenRouter Credentials**: Get an API key from OpenRouter and configure n8n credentials for all chat model nodes.\n3.  **Slack Credentials**: Connect your Slack workspace in n8n and select your desired channels for messages.\n4.  **Webhook URL**: Copy the 'Receipt Photo Upload' webhook URL after activation.\n5.  **Monthly Budget Adjustment**: Edit `const budget = 30000;` in 'Code in JavaScript2' to set your budget."
      },
      "typeVersion": 1
    },
    {
      "id": "53e317a2-b71a-466d-a94c-d389dccf9b83",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        288,
        -496
      ],
      "parameters": {
        "color": 7,
        "width": 1024,
        "height": 480,
        "content": "## How to customize the workflow\n\n-   **Daily Report Trigger**: Adjust the cron settings for frequency.\n-   **AI Model**: Change AI models in 'OpenRouter Chat Model' nodes to other LLMs.\n-   **Output Formatting**: Customize Slack message content and tone in 'Report Budget' and 'Monthly Report' nodes.\n-   **Additional Integrations**: Expand with more nodes for other accounting software, notifications, or advanced analysis."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "4e51c46b-3d65-4634-b5a6-ccd423cc3c4c",
  "connections": {
    "Parse Receipt": {
      "main": [
        [
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Report Budget": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Monthly Report": {
      "main": [
        [
          {
            "node": "Send monthly report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Monthly Analysis": {
      "main": [
        [
          {
            "node": "Monthly Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript": {
      "main": [
        [
          {
            "node": "Add to Budget Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add to Budget Sheet": {
      "main": [
        [
          {
            "node": "Code in JavaScript2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript2": {
      "main": [
        [
          {
            "node": "Report Budget",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daily Report Trigger": {
      "main": [
        [
          {
            "node": "Get Budget Sheet (Daily)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Receipt Photo Upload": {
      "main": [
        [
          {
            "node": "Parse Receipt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Report Budget",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model1": {
      "ai_languageModel": [
        [
          {
            "node": "Monthly Report",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model2": {
      "ai_languageModel": [
        [
          {
            "node": "Parse Receipt",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Get Budget Sheet (Daily)": {
      "main": [
        [
          {
            "node": "Monthly Analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This template is for individuals or households who want to: Easily digitize and track their spending from physical receipts. Automate budget management and financial reporting. Get quick insights into their spending habits on a daily and monthly basis. Stay informed about their…

Source: https://n8n.io/workflows/10970/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

This template is perfect for: Market Researchers tracking industry trends. Tech Teams wanting to stay updated on specific technologies (e.g., "AI", "Cybersecurity"). Content Creators looking for curat

HTTP Request, Agent, OpenRouter Chat +5
AI & RAG

This n8n workflow orchestrates a powerful suite of AI Agents and automations to manage and optimize various aspects of an e-commerce operation, particularly for platforms like Shopify. It leverages La

Google Sheets, HTTP Request, Slack +10
AI & RAG

Enhance your support, onboarding, and internal knowledge workflows with an intelligent RAG-powered chatbot that responds using live data stored in Google Sheets. 🤖📚 Built for teams that rely on struct

Chat Trigger, Output Parser Structured, Memory Buffer Window +6
AI & RAG

leads. Uses supabase, gmail, formTrigger, httpRequest. Webhook trigger; 62 nodes.

Supabase, Gmail, Form Trigger +13
AI & RAG

Fully automates your service order pipeline from incoming booking to supplier confirmation — with built-in SLA enforcement and automatic escalation if a supplier goes silent. 📥 Receives orders via web

HTTP Request, Google Sheets, Agent +4