{
  "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
          }
        ]
      ]
    }
  }
}