Automated Invoice Processing Workflow for Small Businesses
Streamline your invoice processing with this comprehensive n8n workflow that handles invoice receipt, data extraction, approval routing, and payment tracking automatically.
Automated Invoice Processing Workflow for Small Businesses
Why This Automation Matters
Manual invoice processing is one of the most time-consuming tasks for small business operations teams. This workflow eliminates hours of repetitive work by automatically:
- Extracting invoice data from email attachments
- Validating invoice information against purchase orders
- Routing invoices through approval workflows based on amount thresholds
- Updating accounting systems and payment schedules
- Sending notifications to relevant stakeholders
By automating these processes, you’ll reduce processing time by 80%, minimize data entry errors, and ensure consistent approval workflows while maintaining complete audit trails.
What You Need Before Starting
Required Integrations
- Email account (Gmail, Outlook, or IMAP)
- Cloud storage (Google Drive, Dropbox, or OneDrive)
- Accounting software (QuickBooks, Xero, or similar with API access)
- Communication tool (Slack, Microsoft Teams, or email)
Required Tools & Services
- OCR service (Google Cloud Vision API or similar)
- PDF processing capability
- Spreadsheet application (Google Sheets or Excel)
Permissions & Access
- Email read/write permissions
- Cloud storage folder access
- Accounting software API credentials
- Admin access to communication channels
Recommended Setup
- Dedicated email folder for invoices
- Structured folder system in cloud storage
- Approval hierarchy defined in your organization
- Chart of accounts ready in your accounting system
Complete Node-by-Node Build Instructions
1. Email Trigger Node
- Node Type: Email Trigger (IMAP)
- Configuration:
- Connect your business email account
- Set folder to monitor: “Invoices” or “Inbox”
- Enable attachment downloading
- Set polling interval: 5 minutes
- Filter for emails containing “invoice” in subject or body
2. Filter Invoices Node
- Node Type: IF Node
- Configuration:
- Condition: Email has attachments AND (subject contains “invoice” OR sender domain in approved vendors)
- True branch: Continue processing
- False branch: Send to manual review folder
3. Extract Attachments Node
- Node Type: Code Node (JavaScript)
- Configuration:
- Extract PDF and image attachments
- Filter for invoice-related file types (.pdf, .png, .jpg)
- Generate unique filename with timestamp
- Pass attachment data to next node
4. Save to Cloud Storage Node
- Node Type: Google Drive Node
- Configuration:
- Action: Upload file
- Folder: “/Invoices/Incoming”
- Filename: Use extracted filename with date prefix
- Create backup copy in “/Invoices/Backup” folder
5. OCR Data Extraction Node
- Node Type: Google Cloud Vision Node
- Configuration:
- Enable text detection
- Process uploaded file from Google Drive
- Extract: Invoice number, date, vendor, amount, tax, line items
- Output structured JSON data
6. Parse Invoice Data Node
- Node Type: Code Node (JavaScript)
- Configuration:
- Parse OCR results using regex patterns
- Extract key fields: invoice_number, vendor_name, invoice_date, total_amount, tax_amount, due_date
- Validate data format (dates, currency amounts)
- Flag any missing critical information
7. Vendor Validation Node
- Node Type: Google Sheets Node
- Configuration:
- Action: Lookup
- Spreadsheet: “Approved Vendors List”
- Search for vendor name/tax ID
- Return vendor details and approval status
- Set default approval rules
8. Purchase Order Matching Node
- Node Type: HTTP Request Node
- Configuration:
- Connect to accounting system API
- Search for matching PO by vendor and amount
- Return PO details if found
- Flag three-way matching status
9. Approval Routing Decision Node
- Node Type: IF Node
- Configuration:
- Condition 1: Amount < $500 → Auto-approve
- Condition 2: Amount $500-$2000 → Manager approval
- Condition 3: Amount > $2000 → Senior management approval
- Missing PO → Special approval workflow
10. Manager Approval Node (for mid-range amounts)
- Node Type: Slack Node
- Configuration:
- Send message to operations manager
- Include invoice summary and approval buttons
- Attach invoice PDF for review
- Set 48-hour timeout for response
11. Senior Management Approval Node (for high amounts)
- Node Type: Microsoft Teams Node
- Configuration:
- Create approval card with invoice details
- Tag finance director and general manager
- Include budget impact analysis
- Require justification for approval
12. Update Accounting System Node
- Node Type: QuickBooks Node
- Configuration:
- Action: Create Bill
- Map extracted data to accounting fields
- Set appropriate expense categories
- Apply correct tax rates and codes
- Link to purchase order if available
13. Payment Scheduling Node
- Node Type: Google Sheets Node
- Configuration:
- Add invoice to “Payment Schedule” sheet
- Calculate payment date based on terms
- Include vendor payment preferences
- Set payment method and priority
14. Notification Node
- Node Type: Email Node
- Configuration:
- Send confirmation to accounts payable team
- Include processing summary and next steps
- Attach processed invoice with metadata
- CC relevant department managers
15. Archive Processing Node
- Node Type: Google Drive Node
- Configuration:
- Move processed invoice to “/Invoices/Processed” folder
- Rename with standard format: “YYYY-MM-DD_VendorName_InvoiceNumber.pdf”
- Update processing log spreadsheet
- Create audit trail entry
16. Error Handling Node
- Node Type: Slack Node
- Configuration:
- Trigger on any workflow errors
- Send detailed error message to IT/Operations
- Include failed invoice information
- Move problematic files to “/Invoices/Errors” folder
Full Workflow JSON Code
{
"name": "Invoice Processing Automation",
"nodes": [
{
"parameters": {
"protocol": "imap",
"server": "imap.gmail.com",
"port": 993,
"secure": true,
"email": "={{ $credentials.email }}",
"password": "={{ $credentials.password }}",
"folder": "INBOX",
"allowUnauthorizedCerts": false,
"markSeen": true,
"downloadAttachments": true
},
"name": "Email Trigger",
"type": "n8n-nodes-base.emailReadImap",
"typeVersion": 2,
"position": [240, 300],
"id": "email-trigger"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "invoice-filter",
"leftValue": "={{ $json.subject.toLowerCase() }}",
"rightValue": "invoice",
"operator": {
"type": "string",
"operation": "contains"
}
},
{
"id": "attachment-check",
"leftValue": "={{ $json.attachments ? $json.attachments.length : 0 }}",
"rightValue": 0,
"operator": {
"type": "number",
"operation": "gt"
}
}
],
"combinator": "and"
}
},
"name": "Filter Invoices",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [460, 300],
"id": "filter-invoices"
},
{
"parameters": {
"jsCode": "// Extract and process attachments\nconst attachments = $input.first().json.attachments || [];\nconst invoiceAttachments = [];\n\nfor (const attachment of attachments) {\n const filename = attachment.filename.toLowerCase();\n if (filename.includes('.pdf') || filename.includes('.png') || filename.includes('.jpg')) {\n const timestamp = new Date().toISOString().split('T')[0];\n const processedFilename = `${timestamp}_${attachment.filename}`;\n \n invoiceAttachments.push({\n originalName: attachment.filename,\n processedName: processedFilename,\n data: attachment.data,\n mimeType: attachment.mimeType,\n size: attachment.size\n });\n }\n}\n\nreturn invoiceAttachments.map(attachment => ({\n json: {\n ...attachment,\n emailSubject: $input.first().json.subject,\n emailFrom: $input.first().json.from,\n emailDate: $input.first().json.date,\n processingId: `INV_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`\n },\n binary: {\n data: {\n data: attachment.data,\n mimeType: attachment.mimeType,\n fileName: attachment.processedName\n }\n }\n}));"
},
"name": "Extract Attachments",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [680, 300],
"id": "extract-attachments"
},
{
"parameters": {
"operation": "upload",
"fileId": {
"__rl": true,
"value": "1234567890abcdef",
"mode": "list",
"cachedResultName": "Invoices/Incoming"
},
"fileName": "={{ $json.processedName }}",
"binaryData": true,
"options": {
"parents": ["1234567890abcdef"]
}
},
"name": "Save to Google Drive",
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [900, 300],
"id": "save-to-drive"
},
{
"parameters": {
"operation": "textDetection",
"binaryPropertyName": "data"
},
"name": "OCR Data Extraction",
"type": "n8n-nodes-base.googleCloudVision",
"typeVersion": 1,
"position": [1120, 300],
"id": "ocr-extraction"
},
{
"parameters": {
"jsCode": "// Parse OCR results and extract invoice data\nconst ocrText = $json.fullTextAnnotation?.text || '';\nconst invoiceData = {\n processingId: $json.processingId,\n rawText: ocrText\n};\n\n// Extract invoice number\nconst invoiceNumberMatch = ocrText.match(/(?:invoice|inv)\\s*#?\\s*:?\\s*([A-Z0-9\\-]+)/i);\ninvoiceData.invoiceNumber = invoiceNumberMatch ? invoiceNumberMatch[1] : null;\n\n// Extract vendor name (usually at the top)\nconst lines = ocrText.split('\\n').filter(line => line.trim());\ninvoiceData.vendorName = lines[0]?.trim() || null;\n\n// Extract total amount\nconst amountMatch = ocrText.match(/(?:total|amount due)\\s*:?\\s*\\$?([0-9,]+\\.\\d{2})/i);\ninvoiceData.totalAmount = amountMatch ? parseFloat(amountMatch[1].replace(',', '')) : null;\n\n// Extract date\nconst dateMatch = ocrText.match(/(\\d{1,2}[-\\/]\\d{1,2}[-\\/]\\d{2,4})/);\ninvoiceData.invoiceDate = dateMatch ? new Date(dateMatch[1]).toISOString().split('T')[0] : null;\n\n// Extract tax amount\nconst taxMatch = ocrText.match(/tax\\s*:?\\s*\\$?([0-9,]+\\.\\d{2})/i);\ninvoiceData.taxAmount = taxMatch ? parseFloat(taxMatch[1].replace(',', '')) : null;\n\n// Validation flags\ninvoiceData.isValid = !!(invoiceData.invoiceNumber && invoiceData.vendorName && invoiceData.totalAmount);\ninvoiceData.missingFields = [];\n\nif (!invoiceData.invoiceNumber) invoiceData.missingFields.push('Invoice Number');\nif (!invoiceData.vendorName) invoiceData.missingFields.push('Vendor Name');\nif (!invoiceData.totalAmount) invoiceData.missingFields.push('Total Amount');\nif (!invoiceData.invoiceDate) invoiceData.missingFields.push('Invoice Date');\n\nreturn [{ json: invoiceData }];"
},
"name": "Parse Invoice Data",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1340, 300],
"id": "parse-invoice-data"
},
{
"parameters": {
"operation": "read",
"documentId": {
"__rl": true,
"value": "1234567890abcdef",
"mode": "list",
"cachedResultName": "Approved Vendors"
},
"sheetName": "Sheet1",
"columns": "A:E",
"filters": {
"conditions": [
{
"column": "Vendor Name",
"condition": "contains",
"value": "={{ $json.vendorName }}"
}
]
}
},
"name": "Vendor Validation",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4,
"position": [1560, 300],
"id": "vendor-validation"
},
{
"parameters": {
"url": "={{ $credentials.quickbooks.baseUrl }}/v3/companyid/{{ $credentials.quickbooks.companyId }}/purchaseorders",
"authentication": "oAuth2Api",
"httpHeaderAuth": {
"__rl": true,
"value": "quickbooks",
"mode": "list",
"cachedResultName": "QuickBooks API"
},
"qs": {
"query": "SELECT * FROM PurchaseOrder WHERE Vendor = '{{ $json.vendorName }}' AND TotalAmt = {{ $json.totalAmount }}"
}
},
"name": "Purchase Order Matching",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [1780, 300],
"id": "po-matching"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive":Want the complete prompt pack?
500+ prompts, organised and ready to use.
Download the Full Pack — $19