Home/ Workflows/ Finance/ Workflow Automation
Workflow · Finance

n8n Invoice Approval Workflow Automation for Finance Teams

Streamline your finance operations with an automated invoice approval workflow using n8n. Route invoices for approval, track status, and maintain audit trails.

n8n Invoice Approval Workflow Automation for Finance Teams

Why This Automation Matters

Manual invoice approval processes create bottlenecks, delays, and compliance risks in finance operations. This n8n automation workflow eliminates these issues by:

Finance teams using automated approval workflows report 60% faster processing times and 40% fewer approval-related errors.

What You Need Before Starting

Required Tools and Access

Prerequisites

Sample Data Structure

Your invoice data should include:

Complete Node-by-Node Build Instructions

Step 1: Create the Webhook Trigger

  1. Add a Webhook node as your starting point
  2. Set the HTTP method to POST
  3. Configure the webhook path as /invoice-approval
  4. Set response mode to “Respond Immediately”
  5. Configure the response body to return success confirmation

Step 2: Add Data Validation

  1. Add a Code node after the webhook
  2. Name it “Validate Invoice Data”
  3. Add validation logic for required fields:
    • Invoice amount (must be numeric)
    • Vendor name (must exist)
    • Invoice date (valid date format)
    • Department code (valid department)

Step 3: Determine Approval Route

  1. Add an IF node for approval routing
  2. Configure conditions based on invoice amount:
    • Less than $1,000: Auto-approve
    • $1,000-$10,000: Department manager approval
    • Over $10,000: Finance director + department manager

Step 4: Store Invoice in Database

  1. Add a PostgreSQL (or your preferred database) node
  2. Configure INSERT operation
  3. Create invoice record with status “Pending Approval”
  4. Store all invoice details and generate tracking ID

Step 5: Send Approval Notifications

  1. Add Gmail node (or preferred email service)
  2. Configure dynamic recipient based on approval route
  3. Include invoice details, approval links, and attachments
  4. Set professional email template with company branding

Step 6: Wait for Approval Response

  1. Add Wait node configured for webhook response
  2. Set timeout period (e.g., 5 business days)
  3. Configure approval webhook endpoints for approve/reject actions

Step 7: Process Approval Decision

  1. Add Switch node to handle different responses
  2. Configure cases for: Approved, Rejected, Timeout
  3. Route each case to appropriate next steps

Step 8: Update Database Status

  1. Add PostgreSQL node for each approval outcome
  2. Update invoice status in database
  3. Log approval decision with timestamp and approver details
  4. Maintain complete audit trail

Step 9: Send Final Notifications

  1. Add Gmail nodes for outcome notifications
  2. Notify invoice submitter of decision
  3. Send approved invoices to accounts payable
  4. Alert finance team of rejections or timeouts

Step 10: Handle Escalations

  1. Add IF node to check for timeout scenarios
  2. Configure escalation to higher approval authority
  3. Send urgent notification to finance director
  4. Update database with escalation status

Full Workflow JSON Code Block

{
  "name": "Invoice Approval Workflow",
  "nodes": [
    {
      "parameters": {
        "path": "/invoice-approval",
        "responseMode": "respondImmediately",
        "options": {}
      },
      "name": "Invoice Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [240, 300],
      "webhookId": "invoice-approval-webhook"
    },
    {
      "parameters": {
        "jsCode": "// Validate required invoice fields\nconst invoice = $input.first().json;\n\nif (!invoice.amount || isNaN(invoice.amount)) {\n  throw new Error('Invalid invoice amount');\n}\n\nif (!invoice.vendorName || invoice.vendorName.trim() === '') {\n  throw new Error('Vendor name is required');\n}\n\nif (!invoice.invoiceDate || !Date.parse(invoice.invoiceDate)) {\n  throw new Error('Invalid invoice date');\n}\n\nif (!invoice.department) {\n  throw new Error('Department is required');\n}\n\n// Add validation timestamp\ninvoice.validatedAt = new Date().toISOString();\ninvoice.status = 'validated';\n\nreturn { json: invoice };"
      },
      "name": "Validate Invoice Data",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [460, 300]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "auto-approve",
              "leftValue": "={{ $json.amount }}",
              "rightValue": 1000,
              "operator": {
                "type": "number",
                "operation": "lt"
              }
            },
            {
              "id": "manager-approval",
              "leftValue": "={{ $json.amount }}",
              "rightValue": 10000,
              "operator": {
                "type": "number",
                "operation": "lte"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "name": "Determine Approval Route",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [680, 300]
    },
    {
      "parameters": {
        "operation": "insert",
        "schema": {
          "__rl": true,
          "value": "public",
          "mode": "list"
        },
        "table": {
          "__rl": true,
          "value": "invoices",
          "mode": "list"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "invoice_number": "={{ $json.invoiceNumber }}",
            "amount": "={{ $json.amount }}",
            "vendor_name": "={{ $json.vendorName }}",
            "invoice_date": "={{ $json.invoiceDate }}",
            "department": "={{ $json.department }}",
            "status": "pending_approval",
            "approval_route": "={{ $json.approvalRoute }}",
            "created_at": "={{ new Date().toISOString() }}"
          }
        }
      },
      "name": "Store Invoice",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [900, 300],
      "credentials": {
        "postgres": {
          "id": "postgres-credentials",
          "name": "Finance Database"
        }
      }
    },
    {
      "parameters": {
        "sendTo": "={{ $json.approverEmail }}",
        "subject": "Invoice Approval Required - {{ $json.invoiceNumber }}",
        "message": "=Hello,\n\nA new invoice requires your approval:\n\nInvoice Number: {{ $json.invoiceNumber }}\nVendor: {{ $json.vendorName }}\nAmount: ${{ $json.amount }}\nDate: {{ $json.invoiceDate }}\nDepartment: {{ $json.department }}\n\nApproval Actions:\n✅ Approve: {{ $json.approveUrl }}\n❌ Reject: {{ $json.rejectUrl }}\n\nPlease review and take action within 5 business days.\n\nBest regards,\nFinance Automation System"
      },
      "name": "Send Approval Request",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [1120, 300],
      "credentials": {
        "gmailOAuth2": {
          "id": "gmail-credentials",
          "name": "Finance Gmail"
        }
      }
    },
    {
      "parameters": {
        "resume": "webhook",
        "limit": 432000,
        "limitType": "seconds"
      },
      "name": "Wait for Approval",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [1340, 300]
    },
    {
      "parameters": {
        "dataType": "string",
        "value1": "={{ $json.decision }}",
        "rules": {
          "rules": [
            {
              "value2": "approved",
              "output": 0
            },
            {
              "value2": "rejected",
              "output": 1
            },
            {
              "value2": "timeout",
              "output": 2
            }
          ]
        }
      },
      "name": "Process Decision",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3,
      "position": [1560, 300]
    },
    {
      "parameters": {
        "operation": "update",
        "schema": {
          "__rl": true,
          "value": "public",
          "mode": "list"
        },
        "table": {
          "__rl": true,
          "value": "invoices",
          "mode": "list"
        },
        "updateKey": "invoice_number",
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "status": "approved",
            "approved_by": "={{ $json.approverEmail }}",
            "approved_at": "={{ new Date().toISOString() }}",
            "approval_notes": "={{ $json.notes || '' }}"
          }
        }
      },
      "name": "Update Status - Approved",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [1780, 180],
      "credentials": {
        "postgres": {
          "id": "postgres-credentials",
          "name": "Finance Database"
        }
      }
    },
    {
      "parameters": {
        "sendTo": "={{ $json.submitterEmail }}",
        "subject": "Invoice Approved - {{ $json.invoiceNumber }}",
        "message": "=Good news!\n\nYour invoice has been approved:\n\nInvoice Number: {{ $json.invoiceNumber }}\nAmount: ${{ $json.amount }}\nApproved by: {{ $json.approverEmail }}\nApproved at: {{ $json.approvedAt }}\n\nThe invoice will now be processed for payment.\n\nBest regards,\nFinance Team"
      },
      "name": "Notify Approval",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [2000, 180],
      "credentials": {
        "gmailOAuth2": {
          "id": "gmail-credentials",
          "name": "Finance Gmail"
        }
      }
    }
  ],
  "connections": {
    "Invoice Webhook": {
      "main": [
        [
          {
            "node": "Validate Invoice Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Invoice Data": {
      "main": [
        [
          {
            "node": "Determine Approval Route",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Determine Approval Route": {
      "main": [
        [
          {
            "node": "Store Invoice",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store Invoice": {
      "main": [
        [
          {
            "node": "Send Approval Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Approval Request": {
      "main": [
        [
          {
            "node": "Wait for Approval",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait for Approval": {
      "main": [
        [
          {
            "node": "Process Decision",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Decision": {
      "main": [
        [
          {
            "node": "Update Status - Approved",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Status - Approved": {
      "main": [
        [
          {
            "node": "Notify Approval",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "invoice-approval-v1.0"
}

FAQ

How do I customize approval thresholds for different departments?

You can modify the approval routing logic in the “Determine Approval Route” IF node. Add department-specific conditions by creating additional branches that check both $json.department and $json.amount. For example, IT department might have different thresholds than Marketing. Create separate approval rules for each department and route to appropriate department managers or budget owners.

What happens if an approver doesn’t respond within the timeout period?

The workflow includes automatic escalation handling. When the Wait node times out (default 5 business days), the workflow triggers an escalation branch that notifies the next level manager or finance director. The invoice status updates to “escalated” in the database, and urgent notifications are sent. You can customize the escalation hierarchy and timeout periods based on your organization’s policies.

Can I integrate this workflow with our existing ERP system?

Yes, the workflow is designed to integrate with various systems through n8n’s extensive node library. Replace the database nodes with HTTP Request nodes to connect to your ERP’s API endpoints. Most modern ERP systems (SAP, NetSuite, QuickBooks) provide REST APIs for invoice management. You’ll need to modify the data mapping to match your ERP’s field requirements and authentication methods.


Ready to transform your finance operations? Start automating with n8n Cloud and build this invoice approval workflow in minutes, not hours.