This workflow corresponds to n8n.io template #16091 — we link there as the canonical source.
This workflow follows the Chainllm → 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": "aOPi3CmdNllBaGKi",
"name": "AI Invoice and Receipt Processor: Extract, Classify and Log from Gmail with Claude",
"tags": [],
"nodes": [
{
"id": "e984b474-aa96-40b2-b310-d6c44de57e54",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
16,
-96
],
"parameters": {
"color": 0,
"width": 500,
"height": 1240,
"content": "## AI Invoice and Receipt Processor using Claude and Gmail\n\nStop manually logging invoices and receipts. This workflow monitors your Gmail inbox every 15 minutes, reads every email that looks like an invoice or receipt, uses Claude AI to extract all the financial details, and logs everything to Google Sheets automatically -- with Slack alerts for high-value or overdue invoices.\n\nWorks for vendor invoices, client receipts, expense receipts, and subscriptions. No manual file uploading. No copy-pasting into spreadsheets. Every financial document handled the moment it lands in your inbox.\n\n### How it works\n\n1. A schedule trigger polls Gmail every 15 minutes for unread emails matching invoice or receipt keywords in the subject line.\n2. Each matching email is fetched and any PDF attachment text is extracted and combined with the email body.\n3. Claude Sonnet reads the combined text and extracts 12 financial fields: document type, vendor name, invoice number, invoice date, due date, currency, subtotal, tax, total amount, payment status, line items summary, and confidence level.\n4. A duplicate check searches your Google Sheet for the same invoice number from the same vendor before logging -- preventing double entries.\n5. New records are appended to Google Sheets with all 12 fields plus a processed timestamp.\n6. Overdue invoices and invoices above your high-value threshold trigger a Slack alert so nothing slips through.\n7. The processed Gmail email is marked as read automatically to keep your inbox clean.\n\n### Setup steps\n\n- [ ] **Gmail trigger** -- Connect your Gmail account in Check Gmail for Invoices. The workflow polls every 15 minutes by default -- change this in the schedule settings.\n- [ ] **Settings node** -- Open Configure Settings and set your HIGH_VALUE_THRESHOLD (invoices above this amount trigger a Slack alert), SLACK_CHANNEL, and LOG_SHEET_ID.\n- [ ] **Claude AI** -- Click the Claude Sonnet sub-node under Extract Invoice Data with Claude, add a new Anthropic credential from console.anthropic.com.\n- [ ] **Gmail mark read** -- Connect your Gmail account in Mark Email as Read.\n- [ ] **Slack** -- Connect your Slack account in Notify - High Value or Overdue and set your channel. Right-click and Disable if unused.\n- [ ] **Google Sheets** -- Create a sheet called Invoice Log with columns: Timestamp, From, Subject, Invoice Type, Vendor Name, Invoice Number, Invoice Date, Due Date, Currency, Subtotal, Tax, Total Amount, Payment Status, Line Items, Confidence, Email ID. Connect your Google account in both the duplicate check node and Log Invoice to Sheets.\n- [ ] Activate the workflow and forward a test invoice email to your inbox to verify extraction works.\n\n### Customization\n\nChange the Gmail subject filter in Configure Settings to match the keywords in your invoice emails. Raise or lower HIGH_VALUE_THRESHOLD to control when Slack alerts fire. Add a second branch to auto-forward extracted invoice data to your accountant via Gmail."
},
"typeVersion": 1
},
{
"id": "2e17122a-2ffb-4f57-b2fa-6ae06e47d8b8",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
576,
-96
],
"parameters": {
"color": 7,
"width": 460,
"height": 540,
"content": "## Fetch and prepare invoice emails\n\nPolls Gmail every 15 minutes for unread emails with invoice or receipt keywords. Extracts the email body and any PDF attachment text, then combines them into a single document for Claude to read."
},
"typeVersion": 1
},
{
"id": "28d74230-a94e-4a47-87c9-b9e791f8a5ae",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
1104,
-96
],
"parameters": {
"color": 7,
"width": 472,
"height": 620,
"content": "## Extract and classify with Claude AI\n\nClaude Sonnet reads the full email and attachment text and extracts 12 financial fields: document type, vendor, invoice number, dates, currency, subtotal, tax, total, payment status, line items summary, and confidence.\n\nReturns structured JSON that the next node parses and validates."
},
"typeVersion": 1
},
{
"id": "6f29d59b-f4ed-4048-891e-9ff26c9d1afb",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1616,
-96
],
"parameters": {
"color": 7,
"width": 444,
"height": 620,
"content": "## Deduplicate before logging\n\nSearches the Google Sheet for an existing row with the same invoice number and vendor name before writing. Prevents duplicate entries when the same invoice email arrives twice."
},
"typeVersion": 1
},
{
"id": "eeaa0914-0184-4940-afaf-e2a5e09d207f",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
2144,
-176
],
"parameters": {
"color": 7,
"width": 892,
"height": 672,
"content": "## Log, alert and clean up\n\nAppends the extracted invoice data to Google Sheets. Fires a Slack alert if the invoice is overdue or above your high-value threshold. Marks the Gmail email as read so your inbox stays clean.\n\nSlack is optional -- right-click Notify - High Value or Overdue and Disable if unused."
},
"typeVersion": 1
},
{
"id": "02b9683e-b10b-4b6c-81a4-e166e3234eea",
"name": "Check Gmail for Invoices",
"type": "n8n-nodes-base.gmailTrigger",
"position": [
624,
192
],
"parameters": {
"filters": {
"readStatus": "unread"
},
"pollTimes": {
"item": [
{
"mode": "everyX",
"unit": "minutes",
"value": 15
}
]
}
},
"typeVersion": 1.2
},
{
"id": "d58291cf-9605-4d12-8037-2844a7d8da4c",
"name": "Configure Settings",
"type": "n8n-nodes-base.code",
"position": [
864,
192
],
"parameters": {
"jsCode": "// ================================================\n// EDIT THIS NODE -- all your settings live here\n// ================================================\n\nvar HIGH_VALUE_THRESHOLD = 1000; // Invoices above this amount trigger a Slack alert\nvar SLACK_CHANNEL = 'YOUR_SLACK_CHANNEL_ID';\nvar LOG_SHEET_ID = 'YOUR_LOG_SHEET_ID';\nvar LOG_SHEET_NAME = 'Invoice Log';\n\n// ================================================\n// DO NOT EDIT BELOW THIS LINE\n// ================================================\n\nvar email = $input.first().json;\n\nvar fromEmail = email.from || email.From || '';\nvar subject = email.subject || email.Subject || '';\nvar emailBody = email.text || email.snippet || email.body || '';\nvar emailId = email.id || email.messageId || '';\nvar receivedDate = email.date || new Date().toISOString();\n\n// Check for PDF attachment\nvar hasAttachment = email.attachments && email.attachments.length > 0;\nvar attachmentText = '';\nif (email.attachments && email.attachments.length > 0) {\n email.attachments.forEach(function(att) {\n if (att.content) attachmentText += att.content + ' ';\n });\n}\n\nreturn [{\n json: {\n high_value_threshold: HIGH_VALUE_THRESHOLD,\n slack_channel: SLACK_CHANNEL,\n log_sheet_id: LOG_SHEET_ID,\n log_sheet_name: LOG_SHEET_NAME,\n from_email: fromEmail,\n subject: subject,\n email_body: emailBody.slice(0, 3000),\n attachment_text: attachmentText.slice(0, 3000),\n email_id: emailId,\n received_date: receivedDate,\n timestamp: new Date().toISOString()\n }\n}];"
},
"typeVersion": 2
},
{
"id": "6382c010-67e3-4c6b-8d9c-c3fd2906b7d1",
"name": "Extract Invoice Data with Claude",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
1136,
192
],
"parameters": {
"text": "=You are an expert financial document analyst. Read the email and any attachment text below and extract all invoice or receipt data.\n\nEMAIL DETAILS:\nFrom: {{ $json.from_email }}\nSubject: {{ $json.subject }}\nDate: {{ $json.received_date }}\nEmail body: {{ $json.email_body }}\nAttachment text: {{ $json.attachment_text }}\n\nCLASSIFICATION GUIDE:\nVENDOR_INVOICE -- An invoice you received from a supplier or vendor that you need to pay\nCLIENT_RECEIPT -- A payment confirmation or receipt from a client paying you\nEXPENSE -- A personal or business expense receipt (travel, software, food, equipment)\nSUBSCRIPTION -- A recurring subscription charge (SaaS, software, monthly service)\nUNKNOWN -- Cannot determine the type from the content\n\nReturn ONLY a valid JSON object -- no markdown, no explanation:\n{\"invoice_type\":\"VENDOR_INVOICE\",\"vendor_name\":\"Company name\",\"invoice_number\":\"INV-001\",\"invoice_date\":\"2026-01-15\",\"due_date\":\"2026-02-15\",\"currency\":\"USD\",\"subtotal\":0,\"tax_amount\":0,\"total_amount\":0,\"payment_status\":\"unpaid\",\"line_items_summary\":\"Brief summary of what was purchased\",\"confidence\":\"high\"}\n\nRULES:\n- payment_status must be: paid, unpaid, overdue, or unknown\n- confidence must be: high, medium, or low\n- All amounts must be numbers, not strings\n- If a field cannot be found, use null for objects and 0 for numbers\n- invoice_date and due_date must be YYYY-MM-DD format or null\n- vendor_name should be the company or person name, not their email address",
"promptType": "define"
},
"typeVersion": 1.4
},
{
"id": "87301f10-ed4d-44ce-924a-5d2467e267b3",
"name": "Claude Sonnet",
"type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
"position": [
1136,
384
],
"parameters": {
"model": "claude-sonnet-4-6",
"options": {
"temperature": 0
}
},
"typeVersion": 1.3
},
{
"id": "dc704ac6-8761-4bca-9fd3-6855faf79b19",
"name": "Parse and Validate Extraction",
"type": "n8n-nodes-base.code",
"position": [
1408,
192
],
"parameters": {
"jsCode": "var text = $input.first().json.text || '';\nvar cfg = $('Configure Settings').first().json;\n\nvar extracted = {\n invoice_type: 'UNKNOWN',\n vendor_name: cfg.from_email,\n invoice_number: 'UNKNOWN-' + Date.now(),\n invoice_date: null,\n due_date: null,\n currency: 'USD',\n subtotal: 0,\n tax_amount: 0,\n total_amount: 0,\n payment_status: 'unknown',\n line_items_summary: '',\n confidence: 'low'\n};\n\ntry {\n var match = text.match(/\\{[\\s\\S]*\\}/);\n if (match) {\n var parsed = JSON.parse(match[0]);\n if (parsed.vendor_name) extracted = Object.assign(extracted, parsed);\n }\n} catch(e) {}\n\n// Validate types\nextracted.subtotal = parseFloat(extracted.subtotal) || 0;\nextracted.tax_amount = parseFloat(extracted.tax_amount) || 0;\nextracted.total_amount = parseFloat(extracted.total_amount) || 0;\n\nvar today = new Date().toISOString().split('T')[0];\nvar isOverdue = extracted.due_date && extracted.due_date < today && extracted.payment_status === 'unpaid';\nvar isHighValue = extracted.total_amount >= cfg.high_value_threshold;\n\nreturn [{ json: Object.assign({}, cfg, extracted, {\n is_overdue: isOverdue,\n is_high_value: isHighValue,\n needs_alert: isOverdue || isHighValue\n}) }];"
},
"typeVersion": 2
},
{
"id": "14533269-f1e9-4ec2-97cc-900fef723677",
"name": "Check for Duplicate",
"type": "n8n-nodes-base.googleSheets",
"position": [
1680,
192
],
"parameters": {
"options": {},
"filtersUI": {
"values": [
{
"lookupValue": "={{ $json.invoice_number }}",
"lookupColumn": "Invoice Number"
}
]
},
"sheetName": {
"__rl": true,
"mode": "name",
"value": "={{ $json.log_sheet_name }}"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "={{ $json.log_sheet_id }}"
}
},
"typeVersion": 4.5
},
{
"id": "5e41f102-7463-421f-8ebb-ace621977fda",
"name": "Is New Invoice",
"type": "n8n-nodes-base.if",
"position": [
1920,
192
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "f82ed3ea-64d6-4bf9-83fa-d6a337c4f13b",
"operator": {
"type": "array",
"operation": "empty",
"singleValue": true
},
"leftValue": "={{ $json }}"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "3f7a01ca-5dc2-4154-9a33-5970dc315e15",
"name": "Log Invoice to Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
2176,
96
],
"parameters": {
"columns": {
"value": {
"Tax": "={{ $('Parse and Validate Extraction').item.json.tax_amount }}",
"From": "={{ $('Parse and Validate Extraction').item.json.from_email }}",
"Subject": "={{ $('Parse and Validate Extraction').item.json.subject }}",
"Currency": "={{ $('Parse and Validate Extraction').item.json.currency }}",
"Due Date": "={{ $('Parse and Validate Extraction').item.json.due_date }}",
"Email ID": "={{ $('Parse and Validate Extraction').item.json.email_id }}",
"Subtotal": "={{ $('Parse and Validate Extraction').item.json.subtotal }}",
"Timestamp": "={{ $('Parse and Validate Extraction').item.json.timestamp }}",
"Confidence": "={{ $('Parse and Validate Extraction').item.json.confidence }}",
"Line Items": "={{ $('Parse and Validate Extraction').item.json.line_items_summary }}",
"Vendor Name": "={{ $('Parse and Validate Extraction').item.json.vendor_name }}",
"Invoice Date": "={{ $('Parse and Validate Extraction').item.json.invoice_date }}",
"Invoice Type": "={{ $('Parse and Validate Extraction').item.json.invoice_type }}",
"Total Amount": "={{ $('Parse and Validate Extraction').item.json.total_amount }}",
"Invoice Number": "={{ $('Parse and Validate Extraction').item.json.invoice_number }}",
"Payment Status": "={{ $('Parse and Validate Extraction').item.json.payment_status }}"
},
"mappingMode": "defineBelow"
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "={{ $('Parse and Validate Extraction').item.json.log_sheet_name }}"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Parse and Validate Extraction').item.json.log_sheet_id }}"
}
},
"typeVersion": 4.5
},
{
"id": "6f99ba0e-320d-43c1-86d2-63af03584a6f",
"name": "Needs Slack Alert",
"type": "n8n-nodes-base.if",
"position": [
2416,
96
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "7b00b2b1-6691-4b90-a809-9d0cfdd3d075",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $('Parse and Validate Extraction').item.json.needs_alert }}"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "77373aa2-9b7d-4543-9580-ffce9170d66e",
"name": "Notify - High Value or Overdue",
"type": "n8n-nodes-base.slack",
"position": [
2656,
-16
],
"parameters": {
"text": "={{ ($('Parse and Validate Extraction').item.json.is_overdue ? 'OVERDUE INVOICE' : 'HIGH VALUE INVOICE') + '\\n\\nVendor: ' + $('Parse and Validate Extraction').item.json.vendor_name + '\\nInvoice: ' + $('Parse and Validate Extraction').item.json.invoice_number + '\\nAmount: ' + $('Parse and Validate Extraction').item.json.currency + ' ' + $('Parse and Validate Extraction').item.json.total_amount + '\\nDue date: ' + ($('Parse and Validate Extraction').item.json.due_date || 'not specified') + '\\nStatus: ' + $('Parse and Validate Extraction').item.json.payment_status + '\\nSubject: ' + $('Parse and Validate Extraction').item.json.subject }}",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Parse and Validate Extraction').item.json.slack_channel }}"
},
"otherOptions": {}
},
"typeVersion": 2.2
},
{
"id": "b28587b0-d6b1-4430-8acb-10adc1689e85",
"name": "Mark Email as Read",
"type": "n8n-nodes-base.gmail",
"position": [
2768,
240
],
"parameters": {
"messageId": "={{ $('Configure Settings').item.json.email_id }}",
"operation": "markAsRead"
},
"typeVersion": 2.1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "d0f72a67-4f02-4fe6-a22c-fb4f66b35388",
"connections": {
"Claude Sonnet": {
"ai_languageModel": [
[
{
"node": "Extract Invoice Data with Claude",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Is New Invoice": {
"main": [
[
{
"node": "Log Invoice to Sheets",
"type": "main",
"index": 0
}
],
[
{
"node": "Mark Email as Read",
"type": "main",
"index": 0
}
]
]
},
"Needs Slack Alert": {
"main": [
[
{
"node": "Notify - High Value or Overdue",
"type": "main",
"index": 0
}
],
[
{
"node": "Mark Email as Read",
"type": "main",
"index": 0
}
]
]
},
"Configure Settings": {
"main": [
[
{
"node": "Extract Invoice Data with Claude",
"type": "main",
"index": 0
}
]
]
},
"Check for Duplicate": {
"main": [
[
{
"node": "Is New Invoice",
"type": "main",
"index": 0
}
]
]
},
"Log Invoice to Sheets": {
"main": [
[
{
"node": "Needs Slack Alert",
"type": "main",
"index": 0
}
]
]
},
"Check Gmail for Invoices": {
"main": [
[
{
"node": "Configure Settings",
"type": "main",
"index": 0
}
]
]
},
"Parse and Validate Extraction": {
"main": [
[
{
"node": "Check for Duplicate",
"type": "main",
"index": 0
}
]
]
},
"Notify - High Value or Overdue": {
"main": [
[
{
"node": "Mark Email as Read",
"type": "main",
"index": 0
}
]
]
},
"Extract Invoice Data with Claude": {
"main": [
[
{
"node": "Parse and Validate Extraction",
"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 polls Gmail every 15 minutes for unread invoice or receipt emails, uses Anthropic Claude (Sonnet) to extract key financial fields, logs new documents to Google Sheets, sends optional Slack alerts for overdue or high-value invoices, and marks processed emails as…
Source: https://n8n.io/workflows/16091/ — 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.
Your inbox shouldn't run your day. This workflow checks Gmail every 15 minutes, uses Claude AI to classify every new email into Urgent, Needs Reply, FYI Only, Automated, or Spam — then takes the right
This workflow monitors a Gmail inbox for unread support emails, uses Anthropic Claude to classify and draft replies, routes urgent tickets to Slack and a Gmail draft, auto-replies to normal tickets, a
Stop spending 20 minutes writing each Upwork proposal from scratch. This workflow reads your Vollna job alert emails, scores every job against your skills and budget preferences, and uses Claude to wr
Stop wasting time on leads that will never convert. This workflow scores every inbound form submission 1-10 using Claude AI, then automatically replies and routes based on fit — hot leads get an insta
This workflow monitors a Google Sheets patient intake log, uses Anthropic Claude to decide the right follow-up action and draft an email, sends the message via Gmail, optionally notifies a clinic mana