This workflow corresponds to n8n.io template #17380 — 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": "ilmh26XJsIWqQESo",
"name": "Extract receipts and invoices from Gmail with AI and log them to Google Sheets",
"tags": [],
"nodes": [
{
"id": "overview",
"name": "Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1264,
48
],
"parameters": {
"width": 696,
"height": 752,
"content": "## Receipt & invoice tracker \u2192 Google Sheets\n\nFind receipt and invoice emails, let AI pull out the vendor, amount, date, category and invoice number, and append each one as a row in a Google Sheet, so your expense log builds itself.\n\n### How it works\nOn a schedule it searches Gmail for receipt-like emails in your chosen window. A Code node flattens each message to plain text, then a single AI call reads them all and returns structured fields per email, including whether it is really a receipt (so marketing emails are dropped), the total amount, currency, purchase date, a category from your list, and any invoice or order number. The workflow turns the confirmed receipts into rows and appends them to your expense sheet.\n\n### Setup\n- Edit the config node: your Gmail search query, default currency and category list.\n- Add credentials in the canvas: Gmail, Anthropic on the extract HTTP node, and Google Sheets pointed at your expense sheet.\n- The Schedule Trigger runs daily; match the look-back window to the schedule.\n\n### Customization\nSwap Google Sheets for Airtable, Notion or a database; add attachment parsing for PDF invoices; or branch high-value expenses to Slack for approval.\n\nBuilt by **nocode.expert** \u2014 done-for-you automation and AI workflows. https://nocode.expert"
},
"typeVersion": 1
},
{
"id": "section-find-receipts",
"name": "Section: Find receipts",
"type": "n8n-nodes-base.stickyNote",
"position": [
64,
304
],
"parameters": {
"color": 7,
"width": 508,
"height": 480,
"content": "## 1. Find receipt emails\nSearch Gmail for invoices and receipts in your window, then flatten each to plain text."
},
"typeVersion": 1
},
{
"id": "section-extract-fields",
"name": "Section: Extract fields",
"type": "n8n-nodes-base.stickyNote",
"position": [
672,
304
],
"parameters": {
"color": 7,
"width": 448,
"height": 480,
"content": "## 2. Extract with AI\nOne AI call returns vendor, amount, currency, date, category and invoice number per email, and drops non-receipts."
},
"typeVersion": 1
},
{
"id": "section-log-to-sheet",
"name": "Section: Log to Sheet",
"type": "n8n-nodes-base.stickyNote",
"position": [
1232,
304
],
"parameters": {
"color": 7,
"width": 872,
"height": 480,
"content": "## 3. Log to Sheets\nConfirmed receipts become rows appended to your expense sheet. Swap for Airtable or Notion."
},
"typeVersion": 1
},
{
"id": "every-day",
"name": "Every day",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-128,
480
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 9
}
]
}
},
"typeVersion": 1.2
},
{
"id": "set-config-search-categories",
"name": "Set config: search & categories",
"type": "n8n-nodes-base.set",
"position": [
208,
480
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "a1",
"name": "gmailQuery",
"type": "string",
"value": "(invoice OR receipt OR \"order confirmation\") newer_than:2d"
},
{
"id": "a2",
"name": "defaultCurrency",
"type": "string",
"value": "USD"
},
{
"id": "a3",
"name": "categories",
"type": "string",
"value": "Software, Hosting, Advertising, Travel, Meals, Office, Contractors, Other"
},
{
"id": "a4",
"name": "model",
"type": "string",
"value": "claude-haiku-4-5"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "search-receipt-emails",
"name": "Search receipt emails",
"type": "n8n-nodes-base.gmail",
"position": [
400,
480
],
"parameters": {
"filters": {
"q": "={{ $('Set config: search & categories').first().json.gmailQuery }}"
},
"operation": "getAll",
"returnAll": true
},
"typeVersion": 2.1
},
{
"id": "prepare-email-records",
"name": "Prepare email records",
"type": "n8n-nodes-base.code",
"position": [
752,
480
],
"parameters": {
"jsCode": "// Flatten each matched email into a compact record the model can read.\nconst strip = (h) => String(h || '').replace(/<[^>]+>/g, ' ').replace(/&[a-z#0-9]+;/g, ' ').replace(/\\s+/g, ' ').trim();\nconst out = [];\nlet i = 0;\nfor (const it of $input.all()) {\n const j = it.json;\n const from = j.from?.value?.[0]?.address || j.From || j.from || '';\n const subject = j.subject || j.Subject || '';\n const date = j.date || j.internalDate || j.Date || '';\n const body = strip(j.text || j.textPlain || j.snippet || j.textAsHtml || '');\n out.push({ json: { i: i++, id: j.id || '', from, subject, date, body: body.slice(0, 1200) } });\n}\nreturn out;"
},
"typeVersion": 2
},
{
"id": "build-ai-extract-request",
"name": "Build AI extract request",
"type": "n8n-nodes-base.code",
"position": [
960,
480
],
"parameters": {
"jsCode": "const cfg = $('Set config: search & categories').first().json;\nconst emails = $input.all().map((it) => it.json);\nconst schema = { type: 'object', additionalProperties: false, required: ['receipts'], properties: {\n receipts: { type: 'array', items: { type: 'object', additionalProperties: false,\n required: ['i', 'is_receipt', 'vendor', 'amount', 'currency', 'date', 'category', 'invoice_no'], properties: {\n i: { type: 'integer' }, is_receipt: { type: 'boolean' }, vendor: { type: 'string' },\n amount: { type: 'number' }, currency: { type: 'string' }, date: { type: 'string' },\n category: { type: 'string' }, invoice_no: { type: 'string' } } } } } };\nconst system = 'You extract structured expense data from receipt and invoice emails. For each email decide is_receipt (true only if it is an actual purchase receipt/invoice, not marketing). Extract vendor, total amount (number only), currency (ISO code, default ' + cfg.defaultCurrency + '), the purchase date as YYYY-MM-DD, an invoice/order number if present (else \"\"), and category chosen from: ' + cfg.categories + '. If a field is unknown use \"\" or 0. Do not invent amounts.';\nconst user = 'EMAILS (reference each by index i):\\n' +\n emails.map((e) => '[' + e.i + '] From: ' + e.from + ' | Subject: ' + e.subject + ' | Date: ' + e.date + '\\n' + e.body).join('\\n\\n');\nreturn [{ json: { body: {\n model: cfg.model, max_tokens: 2000, system,\n messages: [{ role: 'user', content: user }],\n output_config: { format: { type: 'json_schema', schema } },\n} } }];"
},
"typeVersion": 2
},
{
"id": "extract-receipts-with-claude",
"name": "Extract receipts with Claude",
"type": "n8n-nodes-base.httpRequest",
"position": [
1360,
480
],
"parameters": {
"url": "https://api.anthropic.com/v1/messages",
"method": "POST",
"options": {},
"jsonBody": "={{ JSON.stringify($json.body) }}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"headerParameters": {
"parameters": [
{
"name": "x-api-key",
"value": "={{ $env.ANTHROPIC_API_KEY }}"
},
{
"name": "anthropic-version",
"value": "2023-06-01"
},
{
"name": "content-type",
"value": "application/json"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "build-expense-rows",
"name": "Build expense rows",
"type": "n8n-nodes-base.code",
"position": [
1616,
480
],
"parameters": {
"jsCode": "const emails = $('Prepare email records').all().map((it) => it.json);\nconst blocks = ($json.content || []);\nconst text = (blocks.find((b) => b.type === 'text') || {}).text || '{\"receipts\":[]}';\nlet receipts = [];\ntry { receipts = (JSON.parse(text).receipts) || []; } catch (e) {}\nconst rows = receipts.filter((r) => r.is_receipt && (r.amount || r.vendor)).map((r) => {\n const src = emails[r.i] || {};\n return { json: {\n date: r.date || '', vendor: r.vendor || '', amount: r.amount ?? '', currency: r.currency || '',\n category: r.category || 'Other', invoice_no: r.invoice_no || '',\n email_subject: src.subject || '', email_from: src.from || '',\n } };\n});\nreturn rows.length ? rows : [];"
},
"typeVersion": 2
},
{
"id": "append-to-expense-sheet",
"name": "Append to expense sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
1792,
480
],
"parameters": {
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": ""
},
"documentId": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"typeVersion": 4.5
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "ee8acf9f-22c1-4bee-8a65-5004805f43a7",
"nodeGroups": [],
"connections": {
"Every day": {
"main": [
[
{
"node": "Set config: search & categories",
"type": "main",
"index": 0
}
]
]
},
"Build expense rows": {
"main": [
[
{
"node": "Append to expense sheet",
"type": "main",
"index": 0
}
]
]
},
"Prepare email records": {
"main": [
[
{
"node": "Build AI extract request",
"type": "main",
"index": 0
}
]
]
},
"Search receipt emails": {
"main": [
[
{
"node": "Prepare email records",
"type": "main",
"index": 0
}
]
]
},
"Build AI extract request": {
"main": [
[
{
"node": "Extract receipts with Claude",
"type": "main",
"index": 0
}
]
]
},
"Extract receipts with Claude": {
"main": [
[
{
"node": "Build expense rows",
"type": "main",
"index": 0
}
]
]
},
"Set config: search & categories": {
"main": [
[
{
"node": "Search receipt emails",
"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 search Gmail for receipt and invoice emails, uses Anthropic Claude to extract structured expense fields, and appends the confirmed expenses to a Google Sheets log. Runs every day on a schedule. Searches Gmail using the configured query to find recent…
Source: https://n8n.io/workflows/17380/ — 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.
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
This workflow automates a 3-step cold email sequence from Gmail using leads in Google Sheets, generates personalized copy with Anthropic Claude, enforces a gradual daily sending cap, schedules follow-
This n8n template allows you to automatically monitor your company's budget by comparing live Bexio accounting data against targets defined in Google Sheets, sending automated weekly email reports. It
This workflow streamlines HR outreach by fetching contact data, validating emails, enforcing daily sending limits, and sending personalized emails with attachments, all while logging activity. Read HR