AutomationFlowsAI & RAG › Financial Document Extraction From Gmail to Google Sheets

Financial Document Extraction From Gmail to Google Sheets

ByGtaras @tarasidis on n8n.io

Manual financial reconciliation is tedious and prone to error. This workflow functions as an AI Financial Controller, automatically monitoring your inbox for invoices, receipts, and bills, extracting the data using OCR, and syncing it to Google Sheets for approval.

Event trigger★★★★☆ complexityAI-powered26 nodesGmail TriggerGmailGuardrailsAgentGoogle SheetsOpenAI ChatGoogle Gemini Chat
AI & RAG Trigger: Event Nodes: 26 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #11290 — 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 →

Download .json
{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "dabf59bf-864c-47e2-8289-69fba6c42bd0",
      "name": "Look for Invoices",
      "type": "n8n-nodes-base.gmailTrigger",
      "position": [
        -768,
        368
      ],
      "parameters": {
        "filters": {
          "labelIds": [
            "INBOX"
          ]
        },
        "pollTimes": {
          "item": [
            {
              "mode": "everyHour"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "bf4111ad-870d-4a37-82ac-343828f3c7b9",
      "name": "Get Email Content",
      "type": "n8n-nodes-base.gmail",
      "position": [
        -544,
        368
      ],
      "parameters": {
        "limit": 1,
        "simple": false,
        "filters": {},
        "options": {
          "downloadAttachments": true
        },
        "operation": "getAll"
      },
      "typeVersion": 2.1
    },
    {
      "id": "b5bcf67f-528a-493a-947a-c3070ebf8b66",
      "name": "Guardrail: Is Finance?",
      "type": "@n8n/n8n-nodes-langchain.guardrails",
      "position": [
        -336,
        368
      ],
      "parameters": {
        "text": "={{ $('Get Email Content').item.json.text }}",
        "guardrails": {
          "topicalAlignment": {
            "value": {
              "prompt": "=You are a financial controller AI. \n\nSCOPE:\n- The text MUST contain evidence of a financial transaction, invoice, receipt, or bill.\n- It is ON-TOPIC if it mentions payments, amounts owed, subscription renewals with prices, or attached invoices.\n- It is OFF-TOPIC if it is a newsletter, a marketing email without a specific transaction, or a personal conversation.",
              "threshold": 0.8
            }
          }
        }
      },
      "typeVersion": 1
    },
    {
      "id": "4f653917-7633-4e1b-b3ad-726ae5824cd9",
      "name": "Configuration: User Settings",
      "type": "n8n-nodes-base.set",
      "position": [
        0,
        0
      ],
      "parameters": {
        "values": {
          "string": [
            {
              "name": "gSheetID",
              "value": "ENTER_YOUR_GOOGLE_SHEET_ID"
            },
            {
              "name": "adminEmailForErrors",
              "value": "user@example.com"
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "fdf114cb-d6f1-421d-8a54-bbbdd6a9154b",
      "name": "IF (Guardrail Passed)",
      "type": "n8n-nodes-base.if",
      "position": [
        208,
        336
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "check-aligned",
              "operator": {
                "type": "boolean",
                "operation": "false",
                "singleValue": true
              },
              "leftValue": "={{ $json.checks.triggered }}",
              "rightValue": ""
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "75c2ee44-d59d-4029-804e-14b8a365dfb2",
      "name": "Filter Finance Keywords",
      "type": "n8n-nodes-base.filter",
      "position": [
        592,
        432
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "filter-invoice",
              "operator": {
                "type": "string",
                "operation": "contains"
              },
              "leftValue": "={{ $('Get Email Content').item.json.subject }}",
              "rightValue": "Invoice"
            },
            {
              "id": "filter-receipt",
              "operator": {
                "type": "string",
                "operation": "contains"
              },
              "leftValue": "={{ $('Get Email Content').item.json.subject }}",
              "rightValue": "Receipt"
            },
            {
              "id": "filter-bill",
              "operator": {
                "type": "string",
                "operation": "contains"
              },
              "leftValue": "={{ $('Get Email Content').item.json.subject }}",
              "rightValue": "Bill"
            },
            {
              "id": "filter-payment",
              "operator": {
                "type": "string",
                "operation": "contains"
              },
              "leftValue": "={{ $('Get Email Content').item.json.subject }}",
              "rightValue": "Payment Confirmation"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "92f7d2c2-9adb-4eb0-8aa0-66e1e4cfb36a",
      "name": "Check for Attachment",
      "type": "n8n-nodes-base.if",
      "position": [
        -576,
        1040
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "check-attachment",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ Object.keys($binary || {}).length }}",
              "rightValue": 0
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "6a74d0c3-ba12-4c9a-9db2-fa2b52f12e96",
      "name": "Extract PDF Data",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        -240,
        816
      ],
      "parameters": {
        "options": {},
        "operation": "pdf",
        "binaryPropertyName": "={{ Object.keys($json.binary || {})[0] || 'attachment_0' }}"
      },
      "typeVersion": 1
    },
    {
      "id": "a09fb13e-14e1-4a55-b4af-1aba901ae6bf",
      "name": "AI Agent (PDF OCR)",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -32,
        800
      ],
      "parameters": {
        "text": "={{ $json.text }}",
        "options": {
          "systemMessage": "=You are a Financial OCR Expert. Extract invoice data from the PDF text.\n\nCRITICAL INSTRUCTIONS:\n1. Return ONLY a JSON object.\n2. Normalize dates to YYYY-MM-DD.\n3. Clean currency symbols from amounts (return numbers).\n\nREQUIRED JSON STRUCTURE:\n{\n  \"vendor_name\": \"string\",\n  \"invoice_date\": \"YYYY-MM-DD\",\n  \"invoice_id\": \"string\",\n  \"total_amount\": number,\n  \"tax_amount\": number,\n  \"currency\": \"USD/EUR/GBP\",\n  \"items_summary\": \"string (brief description)\",\n  \"vendor_tax_id\": \"string or null\"\n}"
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 1.9,
      "continueOnFail": true
    },
    {
      "id": "2a0195e7-aa9d-460f-8a91-9752b80a3005",
      "name": "AI Agent (Email OCR)",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -32,
        1088
      ],
      "parameters": {
        "text": "={{ $('Get Email Content').item.json.text }}",
        "options": {
          "systemMessage": "=You are a Financial OCR Expert. Extract receipt data from the email body text.\n\nCRITICAL INSTRUCTIONS:\n1. Return ONLY a JSON object.\n2. Normalize dates to YYYY-MM-DD.\n3. Clean currency symbols from amounts (return numbers).\n\nREQUIRED JSON STRUCTURE:\n{\n  \"vendor_name\": \"string\",\n  \"invoice_date\": \"YYYY-MM-DD\",\n  \"invoice_id\": \"string (or 'EMAIL-RECEIPT' if missing)\",\n  \"total_amount\": number,\n  \"tax_amount\": number,\n  \"currency\": \"USD/EUR/GBP\",\n  \"items_summary\": \"string (brief description)\",\n  \"vendor_tax_id\": \"string or null\"\n}"
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 1.9,
      "continueOnFail": true
    },
    {
      "id": "59761143-4f1a-42a4-b3e7-acad993d9858",
      "name": "Validate Extraction",
      "type": "n8n-nodes-base.code",
      "position": [
        528,
        992
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Check if AI extraction succeeded\nif ($json.error) {\n\treturn {\n\t\tjson: {\n\t\t\terror_occurred: true,\n\t\t\terror_type: \"AI_EXTRACTION_FAILED\",\n\t\t\terror_message: $json.error.message || \"AI extraction failed\",\n\t\t\ttimestamp: new Date().toISOString()\n\t\t}\n\t};\n}\n\nlet data;\ntry {\n\tdata = typeof $json.output === 'string' ? JSON.parse($json.output) : $json.output;\n} catch (e) {\n\treturn {\n\t\tjson: {\n\t\t\terror_occurred: true,\n\t\t\terror_type: \"JSON_PARSE_ERROR\",\n\t\t\terror_message: e.message,\n\t\t\ttimestamp: new Date().toISOString()\n\t\t}\n\t};\n}\n\n// Validation Logic\nconst required = ['vendor_name', 'total_amount', 'invoice_date'];\nconst missing = required.filter(field => !data[field]);\n\nif (missing.length > 0) {\n\treturn {\n\t\tjson: {\n\t\t\terror_occurred: true,\n\t\t\terror_type: \"MISSING_FIELDS\",\n\t\t\terror_message: `Missing: ${missing.join(', ')}`,\n\t\t\textracted_data: data,\n\t\t\ttimestamp: new Date().toISOString()\n\t\t}\n\t};\n}\n\n// Amount Validation\nif (isNaN(parseFloat(data.total_amount))) {\n\treturn {\n\t\tjson: {\n\t\t\terror_occurred: true,\n\t\t\terror_type: \"INVALID_AMOUNT\",\n\t\t\terror_message: \"Total Amount is not a number\",\n\t\t\textracted_data: data,\n\t\t\ttimestamp: new Date().toISOString()\n\t\t}\n\t};\n}\n\nreturn {\n\tjson: {\n\t\terror_occurred: false,\n\t\tvalidated_data: data\n\t}\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "c298c89e-3bf2-496c-a5eb-1cf0c5c4265a",
      "name": "Check for Errors",
      "type": "n8n-nodes-base.if",
      "position": [
        800,
        992
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "check-error",
              "operator": {
                "type": "boolean",
                "operation": "false"
              },
              "leftValue": "={{ $json.error_occurred }}",
              "rightValue": ""
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "befb7153-1a70-4265-8472-0fa9acca2620",
      "name": "Apply Finance Rules",
      "type": "n8n-nodes-base.code",
      "position": [
        1040,
        672
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const data = $json.validated_data;\n\n// Business Logic for GL Coding\nlet glCategory = \"Uncategorized\";\n// Convert vendor to lowercase for easier matching\nconst vendor = (data.vendor_name || \"\").toLowerCase();\n\n// FIX 1: Added \"amazon\" to the check so it catches \"Amazon Web Services\"\nif (vendor.includes(\"aws\") || vendor.includes(\"amazon\") || vendor.includes(\"google cloud\") || vendor.includes(\"digitalocean\")) {\n\tglCategory = \"6000 - Software & Hosting\";\n} else if (vendor.includes(\"uber\") || vendor.includes(\"lyft\") || vendor.includes(\"airline\") || vendor.includes(\"flight\")) {\n\tglCategory = \"5000 - Travel & Meals\";\n} else if (vendor.includes(\"wework\") || vendor.includes(\"office\")) {\n\tglCategory = \"4000 - Rent & Utilities\";\n}\n\n// Approval Thresholds\nlet approvalStatus = \"Auto-Approved\";\n// Ensure amount is a number\nconst amount = parseFloat(data.total_amount);\n\n// FIX 2: Check the HIGHEST value first.\n// If we checked > 1000 first, 5200 would trigger that and stop.\nif (amount > 5000) {\n\tapprovalStatus = \"VP Approval Needed\";\n} else if (amount > 1000) {\n\tapprovalStatus = \"Manager Approval Needed\";\n}\n\nreturn {\n\tjson: {\n\t\t...data,\n\t\tgl_category: glCategory,\n\t\tapproval_status: approvalStatus,\n\t\tcase_id: `INV-${Date.now()}`,\n\t\tprocessed_at: new Date().toISOString()\n\t}\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "9ad1658b-9257-45a7-b049-ef6bfdcc38d7",
      "name": "Log to Invoices Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1376,
        672
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [
            {
              "id": "invoice_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "invoice_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "vendor_name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "vendor_name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "invoice_date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "invoice_date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "total_amount",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "total_amount",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "currency",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "currency",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "tax_amount",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "tax_amount",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "gl_category",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "gl_category",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "approval_status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "approval_status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "case_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "case_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "items_summary",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "items_summary",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "vendor_tax_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "vendor_tax_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "processed_at",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "processed_at",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1260157166,
          "cachedResultUrl": "",
          "cachedResultName": "Invoices"
        },
        "documentId": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $('Configuration: User Settings').item.json.gSheetID }}"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.5,
      "waitBetweenTries": 2000
    },
    {
      "id": "9d54059e-7fc6-4954-bbc5-7f6b492a8f67",
      "name": "Send Confirmation Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1632,
        672
      ],
      "parameters": {
        "sendTo": "={{ $('Configuration: User Settings').item.json.adminEmailForErrors }}",
        "message": "=<html>\n<body>\n<h3>Invoice Processed Successfully</h3>\n<p><strong>Vendor:</strong> {{ $json.vendor_name }}</p>\n<p><strong>Amount:</strong> {{ $json.currency }} {{ $json.total_amount }}</p>\n<p><strong>GL Category:</strong> {{ $json.gl_category }}</p>\n<p><strong>Status:</strong> {{ $json.approval_status }}</p>\n<br>\n<p>Check the Google Sheet for details.</p>\n</body>\n</html>",
        "options": {
          "appendAttribution": false
        },
        "subject": "=\ud83d\udcb0 Invoice Processed: {{ $json.vendor_name }} - {{ $json.approval_status }}"
      },
      "typeVersion": 2.1
    },
    {
      "id": "78f48238-71b3-49e9-a492-b23f6600dcdd",
      "name": "Log Error to Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1088,
        1248
      ],
      "parameters": {
        "columns": {
          "value": {
            "timestamp": "={{ $json.timestamp }}",
            "error_type": "={{ $json.error_type }}",
            "error_message": "={{ $json.error_message }}",
            "original_subject": "={{ $('Look for Invoices').item.json.subject }}",
            "workflow_execution_id": "={{ $workflow.id }}"
          },
          "schema": [
            {
              "id": "timestamp",
              "type": "string",
              "displayName": "timestamp"
            },
            {
              "id": "error_type",
              "type": "string",
              "displayName": "error_type"
            },
            {
              "id": "error_message",
              "type": "string",
              "displayName": "error_message"
            },
            {
              "id": "original_subject",
              "type": "string",
              "displayName": "original_subject"
            },
            {
              "id": "workflow_execution_id",
              "type": "string",
              "displayName": "workflow_execution_id"
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "Error Logs",
          "cachedResultName": "Error Logs"
        },
        "documentId": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $('Configuration: User Settings').item.json.gSheetID }}"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.5,
      "waitBetweenTries": 2000
    },
    {
      "id": "bde3beda-0e4f-4d62-ba00-ab44f9d92aff",
      "name": "Send Error Notification",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1424,
        1248
      ],
      "parameters": {
        "sendTo": "={{ $('Configuration: User Settings').item.json.adminEmailForErrors }}",
        "message": "=<html>\n<body>\n<h3>Workflow Failed</h3>\n<p><strong>Error:</strong> {{ $json.error_message }}</p>\n<p><strong>Original Email:</strong> {{ $json.original_subject }}</p>\n</body>\n</html>",
        "options": {},
        "subject": "=\u26a0\ufe0f Finance Workflow Error: {{ $json.error_type }}"
      },
      "typeVersion": 2.1
    },
    {
      "id": "7d9dd7c1-9d9a-450f-bca3-ae146cfc44f0",
      "name": "Sticky Note Guardrail",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -368,
        240
      ],
      "parameters": {
        "color": 4,
        "width": 288,
        "height": 288,
        "content": "## 1. Finance Guardrails\nUses Gemini/LangChain to detect if the email is actually a financial document."
      },
      "typeVersion": 1
    },
    {
      "id": "339c869a-f63b-438f-9d56-fcc79b62cf6d",
      "name": "Sticky Note Extraction",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -272,
        656
      ],
      "parameters": {
        "color": 5,
        "width": 480,
        "height": 608,
        "content": "## 2. Dual Extraction Path\nIf Attachment -> **PDF OCR Agent**\nIf No Attachment -> **Email Body Agent**\n\nBoth normalize data into the same JSON structure."
      },
      "typeVersion": 1
    },
    {
      "id": "b9ac3d47-1b42-4679-a4d8-d4566cdba90a",
      "name": "Sticky Note Logic",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        912,
        544
      ],
      "parameters": {
        "color": 7,
        "width": 352,
        "height": 320,
        "content": "## 3. Business Logic\n- **GL Coding**: Auto-categorizes vendors (Uber -> Travel).\n- **Thresholds**: Flags high amounts (> $1000) for approval."
      },
      "typeVersion": 1
    },
    {
      "id": "c4c483cb-489d-44dd-955b-2b9f852b40d3",
      "name": "gpt 4o mini",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        -32,
        960
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini"
        },
        "options": {
          "temperature": 0.2,
          "responseFormat": "json_object"
        }
      },
      "notesInFlow": false,
      "typeVersion": 1.2
    },
    {
      "id": "465751b4-3b8c-4a9c-b393-41ff173d79c0",
      "name": "Gemini 2.5 flash",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        -336,
        480
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "69b67a16-43f8-402f-a764-3b222e2e083f",
      "name": "Log Success Metrics",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1904,
        672
      ],
      "parameters": {
        "columns": {
          "value": {
            "amount": "={{ $('Apply Finance Rules').item.json.total_amount }}",
            "vendor": "={{ $('Apply Finance Rules').item.json.vendor_name }}",
            "case_id": "={{ $('Apply Finance Rules').item.json.case_id }}",
            "timestamp": "={{ $today }}",
            "email_sent": "Yes",
            "gl_category": "={{ $('Apply Finance Rules').item.json.gl_category }}",
            "approval_status": "={{ $('Apply Finance Rules').item.json.approval_status }}",
            "processing_time_seconds": "={{ Math.round(($execution.startedAt ? (Date.now() - new Date($execution.startedAt).getTime()) / 1000 : 0)) }}"
          },
          "schema": [
            {
              "id": "timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "case_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "case_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "processing_time_seconds",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "processing_time_seconds",
              "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": "gl_category",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "gl_category",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "approval_status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "approval_status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email_sent",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "email_sent",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 2011163382,
          "cachedResultUrl": "",
          "cachedResultName": "Success Metrics"
        },
        "documentId": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $('Configuration: User Settings').item.json.gSheetID }}"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.5,
      "waitBetweenTries": 2000
    },
    {
      "id": "cc699a3b-f602-4f56-ad0a-60d2cc66f96d",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1344,
        176
      ],
      "parameters": {
        "width": 512,
        "height": 1216,
        "content": "## Overview\n### **[Gtaras](https://n8n.io/creators/tarasidis/)**  \n\nManual financial reconciliation is tedious and prone to error. This workflow functions as an AI Financial Controller, automatically monitoring your inbox for invoices, receipts, and bills, extracting the data using OCR, and syncing it to Google Sheets for approval.\n\nUnlike simple scrapers, this workflow uses a \"Guardrail\" AI agent to filter out non-financial emails (like newsletters) before they are processed, ensuring only actual transactions are recorded.\n\n## Who is it for?\n* **Finance Teams:** To automate the collection of vendor invoices.\n* **Freelancers:** To track expenses and receipts for tax season.\n* **Operations Managers:** To monitor budget spend and categorize costs automatically.\n\n## How it works\n1.  **Ingest:** The workflow watches a specific Gmail label (e.g., \"INBOX\") for new emails.\n2.  **Guardrail:** A Gemini-powered AI agent analyzes the email text to determine if it is a valid financial transaction. If not, the workflow stops.\n3.  **Extraction (OCR):**\n    * **If an attachment exists:** An AI Agent (GPT-4o) extracts data from the PDF.\n    * **If no attachment:** An AI Agent extracts data directly from the email body.\n4.  **Validation:** Code nodes check for missing fields or invalid amounts.\n5.  **Business Logic:** The system automatically assigns General Ledger (GL) categories (e.g., \"Uber\" -> \"Travel\") and sets approval statuses based on amount thresholds.\n6.  **Sync:** Validated data is logged to Google Sheets, and a confirmation email is sent. Errors are logged to a separate error sheet.\n\n## How to set up\n1.  **Google Sheets:** Copy [this Google Sheet template](https://docs.google.com/spreadsheets/d/1IaovDHswLKbcQdEyfw-2JSYHOVX1pLNcTIYlJQzxYGU/copy) to your drive. It contains the necessary tabs (Invoices, Error Logs, Success Metrics).\n2.  **Configure Workflow:**\n    * Open the node named **\"Configuration: User Settings\"**.\n    * Paste your Google Sheet ID (found in the URL of your new sheet).\n    * Enter the Admin Email address where you want to receive error notifications.\n3.  **Credentials:**\n    * Connect your **Gmail** account.\n    * Connect your **Google Sheets** account.\n    * Connect your **OpenAI** (for OCR) and **Google Gemini/PaLM** (for Guardrails) accounts.\n\n## Requirements\n* n8n version 1.0 or higher.\n* Gmail account.\n* OpenAI API Key.\n* Google Gemini (PaLM) API Key."
      },
      "typeVersion": 1
    },
    {
      "id": "199af4d5-b569-4220-9748-be6ebd1d9afd",
      "name": "Sticky Note21",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1024,
        1152
      ],
      "parameters": {
        "color": 2,
        "width": 640,
        "height": 304,
        "content": "## \u26a0\ufe0f Error Handling Flow\n### Any failures are logged and admins are notified"
      },
      "typeVersion": 1
    },
    {
      "id": "c2cf3003-6430-4970-812e-b96ba5d98f10",
      "name": "Sticky Note17",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1792,
        608
      ],
      "parameters": {
        "color": 6,
        "width": 352,
        "height": 248,
        "content": "## \ud83d\udcca Success Metrics Logger"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "gpt 4o mini": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent (PDF OCR)",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "AI Agent (Email OCR)",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Check for Errors": {
      "main": [
        [
          {
            "node": "Apply Finance Rules",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log Error to Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract PDF Data": {
      "main": [
        [
          {
            "node": "AI Agent (PDF OCR)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini 2.5 flash": {
      "ai_languageModel": [
        [
          {
            "node": "Guardrail: Is Finance?",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Get Email Content": {
      "main": [
        [
          {
            "node": "Guardrail: Is Finance?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Look for Invoices": {
      "main": [
        [
          {
            "node": "Get Email Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent (PDF OCR)": {
      "main": [
        [
          {
            "node": "Validate Extraction",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Error to Sheet": {
      "main": [
        [
          {
            "node": "Send Error Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Apply Finance Rules": {
      "main": [
        [
          {
            "node": "Log to Invoices Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Extraction": {
      "main": [
        [
          {
            "node": "Check for Errors",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent (Email OCR)": {
      "main": [
        [
          {
            "node": "Validate Extraction",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check for Attachment": {
      "main": [
        [
          {
            "node": "Extract PDF Data",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "AI Agent (Email OCR)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF (Guardrail Passed)": {
      "main": [
        [
          {
            "node": "Filter Finance Keywords",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log to Invoices Sheet": {
      "main": [
        [
          {
            "node": "Send Confirmation Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Guardrail: Is Finance?": {
      "main": [
        [
          {
            "node": "Configuration: User Settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Finance Keywords": {
      "main": [
        [
          {
            "node": "Check for Attachment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Confirmation Email": {
      "main": [
        [
          {
            "node": "Log Success Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Configuration: User Settings": {
      "main": [
        [
          {
            "node": "IF (Guardrail Passed)",
            "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

Manual financial reconciliation is tedious and prone to error. This workflow functions as an AI Financial Controller, automatically monitoring your inbox for invoices, receipts, and bills, extracting the data using OCR, and syncing it to Google Sheets for approval.

Source: https://n8n.io/workflows/11290/ — 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 workflow is for hotel managers, travel agencies, and hospitality teams who receive booking requests via email. It eliminates the need for manual data entry by automatically parsing emails and att

Google Sheets, Gmail, Agent +4
AI & RAG

&gt; Note: This workflow uses sticky notes extensively to document each logical section of the automation. Sticky notes are mandatory and already included to explain OCR, AI parsing, folder logic, dup

QuickBooks, Google Sheets, Google Drive +5
AI & RAG

This template and YouTube video goes over 5 different implementations of evaluations within n8n. Categorization Correctness Tools used String similarity Helpfulness

Evaluation, Evaluation Trigger, Google Gemini Chat +8
AI & RAG

Turn a simple email workflow into a LinkedIn content machine. Generate post ideas, draft full posts, and auto-publish to LinkedIn all controlled by replying to emails.

Agent, Google Gemini Chat, Gmail +3
AI & RAG

Enterprise-grade resume screening automation built for production environments. This workflow combines intelligent AI analysis with comprehensive error handling to ensure reliable processing of candid

Gmail Trigger, Google Drive, HTTP Request +6