This workflow corresponds to n8n.io template #12547 — we link there as the canonical source.
This workflow follows the Gmail → Google Drive 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 →
{
"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
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
gmailOAuth2googleDriveOAuth2ApigoogleSheetsOAuth2ApihtmlcsstopdfApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This is an enterprise-grade Intelligent Document Processing (IDP) hub designed to handle high-volume document ingestion, classification, and departmental routing. It transforms monolithic bulk scans into actionable data streams by leveraging a "Split-Extract-Route" architecture.…
Source: https://n8n.io/workflows/12547/ — 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.
This n8n workflow automates the end-to-end client onboarding process: capturing client details, validating emails, assigning tiers, generating welcome packs, creating tasks, notifying teams, archiving
This is an elite enterprise-grade solution for Accounts Payable and Finance Ops teams. It automates the high-volume extraction of unstructured data from PDF invoices using the HTML to PDF (Parse PDF t
Daily Business Report Generator. Uses googleSheets, httpRequest, slack, gmail. Scheduled trigger; 17 nodes.
Revenue operations teams, SaaS growth managers, and sales directors who need automated weekly insights from their Stripe payment data. Perfect for small to medium businesses tracking subscription reve
This workflow triggers when a HubSpot deal stage changes to Closed Won and automatically generates an invoice. It collects deal and contact data, builds a styled invoice, converts it into a PDF, and s