{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "2a4677dd-411b-4ce5-a338-451aa90389f4",
      "name": "AI: Extract & Classify",
      "type": "n8n-nodes-base.code",
      "position": [
        2416,
        4016
      ],
      "parameters": {
        "jsCode": "// Advanced AI-powered document classification with data extraction\nconst item = $input.first().json;\nconst text = item.text || '';\n\n// Initialize document metadata\nlet docType = 'unknown';\nlet extractedData = {};\nlet confidence = 0;\n\n// Invoice Detection & Extraction\nif (text.match(/\\b(invoice|bill|payment due|total amount)\\b/i)) {\n    docType = 'invoice';\n    confidence = 0.95;\n    \n    // Extract invoice number\n    const invoiceMatch = text.match(/invoice[\\s#:]*([A-Z0-9-]+)/i);\n    extractedData.invoiceNumber = invoiceMatch ? invoiceMatch[1] : 'N/A';\n    \n    // Extract amount with multiple currency formats\n    const amountMatch = text.match(/(?:total|amount|due)[:\\s$\u20ac\u00a3]*([0-9,]+\\.?[0-9]{0,2})/i);\n    extractedData.amount = amountMatch ? parseFloat(amountMatch[1].replace(/,/g, '')) : 0;\n    \n    // Extract vendor name\n    const vendorMatch = text.match(/(?:from|vendor|supplier)[:\\s]*([A-Z][a-z\\s&]+)/i);\n    extractedData.vendor = vendorMatch ? vendorMatch[1].trim() : 'Unknown';\n    \n    // Extract due date\n    const dateMatch = text.match(/due[\\s:]*([0-9]{1,2}[\\/\\-][0-9]{1,2}[\\/\\-][0-9]{2,4})/i);\n    extractedData.dueDate = dateMatch ? dateMatch[1] : null;\n}\n// Payroll Detection & Extraction\nelse if (text.match(/\\b(payroll|salary|wage|timesheet|hours worked)\\b/i)) {\n    docType = 'payroll';\n    confidence = 0.92;\n    \n    // Extract employee ID\n    const empMatch = text.match(/(?:employee|emp)[\\s#:]*([A-Z0-9-]+)/i);\n    extractedData.employeeId = empMatch ? empMatch[1] : 'N/A';\n    \n    // Extract hours worked\n    const hoursMatch = text.match(/(?:hours|hrs)[\\s:]*([0-9]+\\.?[0-9]*)/i);\n    extractedData.hoursWorked = hoursMatch ? parseFloat(hoursMatch[1]) : 0;\n    \n    // Extract pay period\n    const periodMatch = text.match(/period[\\s:]*([0-9]{1,2}[\\/\\-][0-9]{1,2}[\\s-to-]*[0-9]{1,2}[\\/\\-][0-9]{1,2})/i);\n    extractedData.payPeriod = periodMatch ? periodMatch[1] : null;\n    \n    extractedData.amount = 0; // Default for routing logic\n}\n// Legal Document Detection\nelse if (text.match(/\\b(contract|agreement|NDA|terms and conditions|legal notice)\\b/i)) {\n    docType = 'legal';\n    confidence = 0.88;\n    \n    // Extract contract type\n    const contractMatch = text.match(/([A-Z][a-z]+)\\s+(?:contract|agreement)/i);\n    extractedData.contractType = contractMatch ? contractMatch[1] : 'General';\n    \n    // Extract parties involved\n    const partiesMatch = text.match(/between[\\s:]*([A-Z][a-z\\s&,]+)\\s+and\\s+([A-Z][a-z\\s&,]+)/i);\n    if (partiesMatch) {\n        extractedData.party1 = partiesMatch[1].trim();\n        extractedData.party2 = partiesMatch[2].trim();\n    }\n    \n    // Extract effective date\n    const effDateMatch = text.match(/effective[\\s:]*([0-9]{1,2}[\\/\\-][0-9]{1,2}[\\/\\-][0-9]{2,4})/i);\n    extractedData.effectiveDate = effDateMatch ? effDateMatch[1] : null;\n    \n    extractedData.amount = 0;\n}\n// Purchase Order Detection\nelse if (text.match(/\\b(purchase order|PO|requisition|procurement)\\b/i)) {\n    docType = 'purchase_order';\n    confidence = 0.90;\n    \n    const poMatch = text.match(/(?:PO|purchase order)[\\s#:]*([A-Z0-9-]+)/i);\n    extractedData.poNumber = poMatch ? poMatch[1] : 'N/A';\n    \n    const amountMatch = text.match(/(?:total|amount)[:\\s$\u20ac\u00a3]*([0-9,]+\\.?[0-9]{0,2})/i);\n    extractedData.amount = amountMatch ? parseFloat(amountMatch[1].replace(/,/g, '')) : 0;\n}\n\n// Apply extracted data to item\nitem.type = docType;\nitem.confidence = confidence;\nitem.extractedData = extractedData;\nitem.amount = extractedData.amount || 0;\nitem.processedAt = new Date().toISOString();\nitem.requiresReview = confidence < 0.85;\n\n// Add metadata for tracking\nitem.wordCount = text.split(/\\s+/).length;\nitem.pageLength = text.length;\n\nreturn { json: item };"
      },
      "typeVersion": 2
    },
    {
      "id": "0bf0d913-02bb-49f7-901d-37a705a4e4ab",
      "name": "Notion: Create Approval Task",
      "type": "n8n-nodes-base.notion",
      "position": [
        3072,
        3968
      ],
      "parameters": {
        "resource": "database",
        "operation": "createPage"
      },
      "typeVersion": 2
    },
    {
      "id": "0d8b7384-375a-4d57-9f3d-7d4ef622a208",
      "name": "IF: Quality Check Required?",
      "type": "n8n-nodes-base.if",
      "position": [
        2880,
        4144
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "combineOperation": "any"
          },
          "combinator": "or",
          "conditions": [
            {
              "operator": {
                "type": "number",
                "operation": "lt"
              },
              "leftValue": "={{ $json.confidence }}",
              "rightValue": 0.85
            },
            {
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.requiresReview }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "110c2204-a997-40fd-bc0c-1c9ed3cdbd5c",
      "name": "Slack: Alert Team",
      "type": "n8n-nodes-base.slack",
      "position": [
        3056,
        4256
      ],
      "parameters": {
        "text": "=\ud83d\udccb *Manual Review Required*\n\n*Document Type:* {{ $json.type }}\n*Confidence:* {{ ($json.confidence * 100).toFixed(0) }}%\n*Extracted Amount:* ${{ $json.amount }}\n*Invoice:* {{ $json.extractedData?.invoiceNumber || 'N/A' }}\n*Vendor:* {{ $json.extractedData?.vendor || 'Unknown' }}\n\n\u26a0\ufe0f Low confidence score - please verify extracted data manually.",
        "otherOptions": {}
      },
      "typeVersion": 2.1
    },
    {
      "id": "3808cc7a-c6b1-4572-b73a-434d83f575cd",
      "name": "Log to Audit Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        3280,
        4128
      ],
      "parameters": {
        "columns": {
          "value": {
            "Amount": "={{ $json.amount }}",
            "Status": "Processed",
            "Vendor": "={{ $json.extractedData?.vendor || 'N/A' }}",
            "Due Date": "={{ $json.extractedData?.dueDate || 'N/A' }}",
            "Escalated": "={{ $json.amount > 5000 ? 'Yes' : 'No' }}",
            "Timestamp": "={{ $json.processedAt }}",
            "Confidence": "={{ ($json.confidence * 100).toFixed(2) }}%",
            "Document Type": "={{ $json.type }}",
            "Invoice Number": "={{ $json.extractedData?.invoiceNumber || 'N/A' }}",
            "Requires Review": "={{ $json.requiresReview ? 'Yes' : 'No' }}"
          },
          "schema": [
            {
              "id": "Timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Document Type",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Document Type",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Amount",
              "type": "number",
              "display": true,
              "required": false,
              "displayName": "Amount",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": true,
          "convertFieldsToString": true
        },
        "options": {},
        "operation": "append",
        "sheetName": "Sheet1",
        "documentId": "YOUR_SHEET_ID"
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "c3b9b463-65c1-4404-85db-18fb46236119",
      "name": "Sticky Note: Intake",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1312,
        3888
      ],
      "parameters": {
        "color": 7,
        "width": 380,
        "height": 340,
        "content": "### \ud83d\udce5 INTAKE LAYER\nScheduled & webhook triggers feed documents into the system"
      },
      "typeVersion": 1
    },
    {
      "id": "196562aa-9cc4-46a9-bb15-8689c7d16817",
      "name": "Sticky Note: AI Processing",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1744,
        3888
      ],
      "parameters": {
        "color": 7,
        "width": 580,
        "height": 340,
        "content": "### \ud83d\udd2c EXTRACTION & INTELLIGENCE\nOCR extracts text, AI classifies documents and extracts structured data with confidence scoring"
      },
      "typeVersion": 1
    },
    {
      "id": "b30da001-a411-4483-b075-318722c6a0e7",
      "name": "Sticky Note: Smart Routing",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2368,
        3888
      ],
      "parameters": {
        "color": 7,
        "width": 680,
        "height": 340,
        "content": "### \ud83c\udfaf ROUTING & APPROVAL\nDepartmental routing with multi-tier approval workflows and quality checks"
      },
      "typeVersion": 1
    },
    {
      "id": "db6ce707-cdf2-4d12-a8ba-a5fba21df300",
      "name": "Sticky Note: Output Layer",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3040,
        3664
      ],
      "parameters": {
        "color": 7,
        "width": 412,
        "height": 804,
        "content": "### \u2705 AUDIT & ALERTS\nNotion tasks, Gmail escalations, Slack alerts, and comprehensive audit logging"
      },
      "typeVersion": 1
    },
    {
      "id": "18774067-9adc-400a-95b1-b040b0ef3194",
      "name": "Trigger: Daily Schedule1",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        1344,
        3984
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours"
            }
          ]
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "7f03b5eb-1dfd-4919-95e0-ef595842bd0a",
      "name": "Trigger: Webhook Scan1",
      "type": "n8n-nodes-base.webhook",
      "position": [
        1344,
        4096
      ],
      "parameters": {
        "path": "manual-trigger-scan",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 1
    },
    {
      "id": "9601d3e4-47c3-428d-b277-e43d8b8b6db8",
      "name": "Fetch Bulk Scan from Drive1",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        1584,
        4016
      ],
      "parameters": {
        "fileId": "={{ $json.body?.fileId || 'YOUR_DEFAULT_ID' }}",
        "options": {},
        "operation": "download"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "5daa3796-51b8-4c4c-85c9-d360dc682a4c",
      "name": "Verify File Integrity1",
      "type": "n8n-nodes-base.code",
      "position": [
        1792,
        4016
      ],
      "parameters": {
        "jsCode": "const binary = $input.first().binary;\nif (!binary || !binary.data) throw new Error('No valid binary data.');\nreturn { json: { status: 'Ready', receivedAt: new Date().toISOString() } };"
      },
      "typeVersion": 2
    },
    {
      "id": "4de9a2bc-9362-48df-a14f-05163d5b34d5",
      "name": "Split PDF into Pages1",
      "type": "n8n-nodes-htmlcsstopdf.htmlcsstopdf",
      "position": [
        2000,
        4016
      ],
      "parameters": {
        "resource": "pdfManipulation",
        "operation": "splitPdf"
      },
      "credentials": {
        "htmlcsstopdfApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "a459d73f-9717-4e2b-b880-6a865047f84c",
      "name": "Route by Department1",
      "type": "n8n-nodes-base.switch",
      "position": [
        2640,
        4016
      ],
      "parameters": {
        "rules": {
          "rules": [
            {
              "value2": "invoice"
            },
            {
              "output": 1,
              "value2": "payroll"
            },
            {
              "output": 2,
              "value2": "legal"
            },
            {
              "output": 3,
              "value2": "purchase_order"
            }
          ]
        },
        "value1": "={{ $json.type }}",
        "dataType": "string"
      },
      "typeVersion": 1
    },
    {
      "id": "060b9e8d-07ff-47ac-8da3-d8415f120602",
      "name": "Escalate High Value?1",
      "type": "n8n-nodes-base.if",
      "position": [
        2880,
        3904
      ],
      "parameters": {
        "conditions": {
          "number": [
            {
              "value1": "={{ $json.amount }}",
              "value2": 5000,
              "operation": "larger"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "fab141d7-35d2-4542-87af-0179ef88ba90",
      "name": "Escalate to CFO (Gmail)1",
      "type": "n8n-nodes-base.gmail",
      "position": [
        3280,
        3792
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "=<h2>Invoice Approval Required</h2>\n<p><strong>Invoice Number:</strong> {{ $json.extractedData?.invoiceNumber }}</p>\n<p><strong>Vendor:</strong> {{ $json.extractedData?.vendor }}</p>\n<p><strong>Amount:</strong> ${{ $json.amount }}</p>\n<p><strong>Due Date:</strong> {{ $json.extractedData?.dueDate || 'Not specified' }}</p>\n<p><strong>Confidence:</strong> {{ ($json.confidence * 100).toFixed(0) }}%</p>\n<hr>\n<p>Please review in Notion and approve or reject.</p>",
        "options": {},
        "subject": "=\u26a0\ufe0f High Value Invoice Approval Required - ${{ $json.amount }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "b31bcab7-def8-48a6-84fd-3c6ebc04a4b9",
      "name": "Main Documentation1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1264,
        3264
      ],
      "parameters": {
        "color": 6,
        "width": 500,
        "height": 580,
        "content": "## \ud83d\udd0d How it works\nThis intelligent document processing workflow automatically handles bulk scans from Google Drive. After splitting PDFs page-by-page, it uses **OCR technology** to extract text, then applies **AI-powered classification** to identify invoices, payroll, legal docs, and purchase orders. The system extracts key data (amounts, vendor names, dates, invoice numbers) with confidence scoring.\n\n**Smart Routing:** Documents are routed by department, high-value invoices trigger CFO approval via Gmail + Notion, and low-confidence extractions alert the team via Slack for manual review. Every action is logged to Google Sheets for complete audit trails.\n\n## \ud83d\ude80 Setup steps\n1. **Google Drive**: Connect your account and specify the folder ID for incoming scans\n2. **HTML to PDF Split**: Configure to explode multi-page PDFs into individual pages\n3. **OCR Node**: Enable text extraction from scanned images\n4. **AI Classification**: Customize extraction patterns in the Code node for your document types\n5. **Notion Database**: Create approval workflow database with required properties\n6. **Gmail**: Set CFO email and customize escalation message\n7. **Slack**: Connect workspace and set the review channel name\n8. **Audit Sheet**: Link your tracking spreadsheet with proper column headers"
      },
      "typeVersion": 1
    },
    {
      "id": "b8641448-abae-4cec-9f81-d4965de22ee5",
      "name": "Extract from File",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        2208,
        4016
      ],
      "parameters": {
        "options": {},
        "operation": "pdf"
      },
      "typeVersion": 1.1
    }
  ],
  "connections": {
    "Extract from File": {
      "main": [
        [
          {
            "node": "AI: Extract & Classify",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack: Alert Team": {
      "main": [
        [
          {
            "node": "Log to Audit Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Department1": {
      "main": [
        [
          {
            "node": "Escalate High Value?1",
            "type": "main",
            "index": 0
          },
          {
            "node": "IF: Quality Check Required?",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log to Audit Sheet",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log to Audit Sheet",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Escalate High Value?1",
            "type": "main",
            "index": 0
          },
          {
            "node": "IF: Quality Check Required?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Escalate High Value?1": {
      "main": [
        [
          {
            "node": "Notion: Create Approval Task",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log to Audit Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split PDF into Pages1": {
      "main": [
        [
          {
            "node": "Extract from File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI: Extract & Classify": {
      "main": [
        [
          {
            "node": "Route by Department1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Trigger: Webhook Scan1": {
      "main": [
        [
          {
            "node": "Fetch Bulk Scan from Drive1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Verify File Integrity1": {
      "main": [
        [
          {
            "node": "Split PDF into Pages1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Escalate to CFO (Gmail)1": {
      "main": [
        [
          {
            "node": "Log to Audit Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Trigger: Daily Schedule1": {
      "main": [
        [
          {
            "node": "Fetch Bulk Scan from Drive1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Bulk Scan from Drive1": {
      "main": [
        [
          {
            "node": "Verify File Integrity1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF: Quality Check Required?": {
      "main": [
        [
          {
            "node": "Slack: Alert Team",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log to Audit Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notion: Create Approval Task": {
      "main": [
        [
          {
            "node": "Escalate to CFO (Gmail)1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}