AutomationFlowsAI & RAG › Reconcile Financial Data from Bank, Invoice, ERP & CSV

Reconcile Financial Data from Bank, Invoice, ERP & CSV

Original n8n title: Ai-powered Fuzzy Matching, and Assigns Confidence Scores.

ByResilNext @rnair1996 on n8n.io

This workflow automates financial reconciliation across multiple data sources such as bank statements, invoices, ERP systems, and CSV uploads.

Webhook trigger★★★★★ complexityAI-powered34 nodesAgentOpenAI ChatOutput Parser StructuredGoogle SheetsSlack
AI & RAG Trigger: Webhook Nodes: 34 Complexity: ★★★★★ AI nodes: yes Added:

This workflow corresponds to n8n.io template #14276 — we link there as the canonical source.

This workflow follows the Agent → Google Sheets 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 →

Download .json
{
  "nodes": [
    {
      "id": "42217665-1184-40a8-bddf-b40f9e84fce1",
      "name": "Webhook - Receive Financial Data",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -768,
        288
      ],
      "parameters": {
        "path": "financial-data-reconciliation",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "lastNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "de790021-1521-4926-bd1a-b40ae43e2794",
      "name": "Workflow Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        -592,
        288
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "confidenceThreshold",
              "type": "number",
              "value": 0.85
            },
            {
              "id": "id-2",
              "name": "matchingKeys",
              "type": "array",
              "value": "[\"transactionId\", \"invoiceNumber\", \"amount\", \"date\"]"
            },
            {
              "id": "id-3",
              "name": "fuzzyMatchThreshold",
              "type": "number",
              "value": 0.7
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "9f455d80-262a-4af3-85ed-143db95c4696",
      "name": "Check Data Source Type",
      "type": "n8n-nodes-base.if",
      "position": [
        -368,
        288
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "id-1",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.sourceType }}",
              "rightValue": "bank_statement"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "3beba561-8803-4b92-b81e-d5aea0832d57",
      "name": "Extract CSV Data",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        16,
        48
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1.1
    },
    {
      "id": "5fd65063-4ddc-441b-97b3-9f75d88c32a2",
      "name": "Normalize Bank Statement Schema",
      "type": "n8n-nodes-base.set",
      "position": [
        16,
        224
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "recordId",
              "type": "string",
              "value": "={{ $json.transaction_id }}"
            },
            {
              "id": "id-2",
              "name": "amount",
              "type": "number",
              "value": "={{ $json.transaction_amount }}"
            },
            {
              "id": "id-3",
              "name": "date",
              "type": "string",
              "value": "={{ $json.transaction_date }}"
            },
            {
              "id": "id-4",
              "name": "description",
              "type": "string",
              "value": "={{ $json.transaction_description }}"
            },
            {
              "id": "id-5",
              "name": "sourceType",
              "type": "string",
              "value": "bank_statement"
            },
            {
              "id": "id-6",
              "name": "normalizedAt",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "cd62e763-8cd1-46bb-82b6-f18078412dfb",
      "name": "Normalize Invoice Schema",
      "type": "n8n-nodes-base.set",
      "position": [
        16,
        400
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "recordId",
              "type": "string",
              "value": "={{ $json.invoice_number }}"
            },
            {
              "id": "id-2",
              "name": "amount",
              "type": "number",
              "value": "={{ $json.invoice_total }}"
            },
            {
              "id": "id-3",
              "name": "date",
              "type": "string",
              "value": "={{ $json.invoice_date }}"
            },
            {
              "id": "id-4",
              "name": "description",
              "type": "string",
              "value": "={{ $json.invoice_description }}"
            },
            {
              "id": "id-5",
              "name": "sourceType",
              "type": "string",
              "value": "invoice"
            },
            {
              "id": "id-6",
              "name": "normalizedAt",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "0efc2451-3c62-4988-85d6-e06312afa34c",
      "name": "Normalize ERP Schema",
      "type": "n8n-nodes-base.set",
      "position": [
        16,
        608
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "recordId",
              "type": "string",
              "value": "={{ $json.erp_transaction_id }}"
            },
            {
              "id": "id-2",
              "name": "amount",
              "type": "number",
              "value": "={{ $json.erp_amount }}"
            },
            {
              "id": "id-3",
              "name": "date",
              "type": "string",
              "value": "={{ $json.erp_date }}"
            },
            {
              "id": "id-4",
              "name": "description",
              "type": "string",
              "value": "={{ $json.erp_description }}"
            },
            {
              "id": "id-5",
              "name": "sourceType",
              "type": "string",
              "value": "erp"
            },
            {
              "id": "id-6",
              "name": "normalizedAt",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "0c4e99b7-8eda-4216-aba0-c14e076285c5",
      "name": "Normalize CSV Schema",
      "type": "n8n-nodes-base.set",
      "position": [
        224,
        48
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "recordId",
              "type": "string",
              "value": "={{ $json.id }}"
            },
            {
              "id": "id-2",
              "name": "amount",
              "type": "number",
              "value": "={{ $json.amount }}"
            },
            {
              "id": "id-3",
              "name": "date",
              "type": "string",
              "value": "={{ $json.date }}"
            },
            {
              "id": "id-4",
              "name": "description",
              "type": "string",
              "value": "={{ $json.description }}"
            },
            {
              "id": "id-5",
              "name": "sourceType",
              "type": "string",
              "value": "csv_upload"
            },
            {
              "id": "id-6",
              "name": "normalizedAt",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "a8f3c054-a206-4a16-99d0-2957eb87caa6",
      "name": "Merge All Normalized Data",
      "type": "n8n-nodes-base.merge",
      "position": [
        448,
        208
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineAll"
      },
      "typeVersion": 3.2
    },
    {
      "id": "b54e7970-4c2b-478c-9568-57a10757895d",
      "name": "Deterministic Matching Logic",
      "type": "n8n-nodes-base.code",
      "position": [
        832,
        304
      ],
      "parameters": {
        "jsCode": "// Deterministic Matching Logic\n// Groups records by exact matches on recordId, amount, and date fields\n// Calculates match confidence based on number of matching fields\n\nconst items = $input.all();\nconst matchedGroups = {};\nconst results = [];\n\n// Helper function to normalize values for comparison\nfunction normalizeValue(value) {\n  if (value === null || value === undefined) return '';\n  return String(value).trim().toLowerCase();\n}\n\n// Helper function to normalize date for comparison\nfunction normalizeDate(date) {\n  if (!date) return '';\n  try {\n    const d = new Date(date);\n    return d.toISOString().split('T')[0]; // YYYY-MM-DD format\n  } catch (e) {\n    return String(date).trim();\n  }\n}\n\n// Helper function to normalize amount for comparison\nfunction normalizeAmount(amount) {\n  if (amount === null || amount === undefined) return '';\n  const num = parseFloat(String(amount).replace(/[^0-9.-]/g, ''));\n  return isNaN(num) ? '' : num.toFixed(2);\n}\n\n// Process each item and create matching keys\nfor (const item of items) {\n  const json = item.json;\n  \n  const recordId = normalizeValue(json.recordId || json.id || json.transactionId);\n  const amount = normalizeAmount(json.amount || json.total || json.value);\n  const date = normalizeDate(json.date || json.transactionDate || json.timestamp);\n  \n  // Create multiple matching keys with different combinations\n  const matchingKeys = [];\n  \n  // Exact match on all three fields (highest confidence)\n  if (recordId && amount && date) {\n    matchingKeys.push({ key: `${recordId}|${amount}|${date}`, matchedFields: ['recordId', 'amount', 'date'], confidence: 1.0 });\n  }\n  \n  // Match on recordId and amount (high confidence)\n  if (recordId && amount) {\n    matchingKeys.push({ key: `${recordId}|${amount}`, matchedFields: ['recordId', 'amount'], confidence: 0.85 });\n  }\n  \n  // Match on recordId and date (high confidence)\n  if (recordId && date) {\n    matchingKeys.push({ key: `${recordId}|${date}`, matchedFields: ['recordId', 'date'], confidence: 0.85 });\n  }\n  \n  // Match on amount and date (medium confidence)\n  if (amount && date) {\n    matchingKeys.push({ key: `${amount}|${date}`, matchedFields: ['amount', 'date'], confidence: 0.7 });\n  }\n  \n  // Match on recordId only (lower confidence)\n  if (recordId) {\n    matchingKeys.push({ key: `${recordId}`, matchedFields: ['recordId'], confidence: 0.6 });\n  }\n  \n  // Store item with its matching keys\n  for (const matchKey of matchingKeys) {\n    if (!matchedGroups[matchKey.key]) {\n      matchedGroups[matchKey.key] = {\n        items: [],\n        matchedFields: matchKey.matchedFields,\n        baseConfidence: matchKey.confidence\n      };\n    }\n    matchedGroups[matchKey.key].items.push(json);\n  }\n}\n\n// Process matched groups and create output\nfor (const [key, group] of Object.entries(matchedGroups)) {\n  if (group.items.length > 1) {\n    // Multiple items matched - potential reconciliation\n    const matchCount = group.items.length;\n    const confidence = group.baseConfidence * Math.min(1.0, matchCount / 2); // Adjust confidence based on match count\n    \n    results.push({\n      matchKey: key,\n      matchedFields: group.matchedFields,\n      matchCount: matchCount,\n      confidence: parseFloat(confidence.toFixed(2)),\n      matchType: 'deterministic',\n      records: group.items,\n      status: confidence >= 0.8 ? 'high_confidence' : 'medium_confidence'\n    });\n  } else if (group.items.length === 1) {\n    // Single item - no match found\n    results.push({\n      matchKey: key,\n      matchedFields: group.matchedFields,\n      matchCount: 1,\n      confidence: 0,\n      matchType: 'no_match',\n      records: group.items,\n      status: 'unmatched'\n    });\n  }\n}\n\n// Return results\nreturn results.map(result => ({ json: result }));"
      },
      "typeVersion": 2
    },
    {
      "id": "895fed31-1878-4834-bdff-834c29948de5",
      "name": "Check Match Quality",
      "type": "n8n-nodes-base.if",
      "position": [
        1008,
        304
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "id-1",
              "operator": {
                "type": "number",
                "operation": "lt"
              },
              "leftValue": "={{ $('Deterministic Matching Logic').item.json.matchConfidence }}",
              "rightValue": "={{ $('Workflow Configuration').first().json.fuzzyMatchThreshold }}"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "73cfb80a-0342-4542-a099-9ca83d8b045e",
      "name": "AI Fuzzy Matching Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1248,
        528
      ],
      "parameters": {
        "text": "=Records to match: {{ $json.unmatchedRecords }}",
        "options": {
          "systemMessage": "You are a financial data reconciliation specialist. Your task is to analyze unmatched financial records and identify potential matches using fuzzy matching techniques.\n\nFor each unmatched record, you should:\n1. Compare transaction descriptions using semantic similarity\n2. Check for near-matches in amounts (within 1% tolerance)\n3. Look for date matches within a 3-day window\n4. Consider vendor name variations and abbreviations\n5. Return matched pairs with confidence scores\n\nReturn your analysis in the structured format defined by the output parser."
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 3
    },
    {
      "id": "7abbe9b8-2a0b-48d9-81c0-39b0a80df771",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        1248,
        864
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini"
        },
        "options": {},
        "builtInTools": {}
      },
      "typeVersion": 1.3
    },
    {
      "id": "2a37407e-1fd9-46d5-a942-de40e246f47d",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        1408,
        848
      ],
      "parameters": {
        "jsonSchemaExample": "{\n\t\"matchedPairs\": [\n\t\t{\n\t\t\t\"sourceRecord\": {},\n\t\t\t\"targetRecord\": {},\n\t\t\t\"confidenceScore\": 0.95,\n\t\t\t\"matchReason\": \"Exact match on transaction ID and amount\"\n\t\t}\n\t],\n\t\"unmatchedRecords\": []\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "295397ed-0102-4df9-a2cf-446a85439e49",
      "name": "Merge Matched Results",
      "type": "n8n-nodes-base.merge",
      "position": [
        1680,
        304
      ],
      "parameters": {
        "mode": "combineAll"
      },
      "typeVersion": 3.2
    },
    {
      "id": "54f8e770-f6f8-4b49-bb2c-78962732ed6f",
      "name": "Calculate Confidence Scores",
      "type": "n8n-nodes-base.code",
      "position": [
        1872,
        304
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Calculate final confidence scores by combining deterministic and AI fuzzy match scores\n// Normalize to 0-1 range and add audit trail metadata\n\nconst item = $input.item.json;\n\n// Extract scores from different matching stages\nconst deterministicScore = item.deterministicMatchScore || 0;\nconst aiScore = item.aiMatchScore || 0;\nconst hasAiMatch = item.aiMatchScore !== undefined;\n\n// Weight configuration\nconst DETERMINISTIC_WEIGHT = 0.6;\nconst AI_WEIGHT = 0.4;\n\n// Calculate combined confidence score\nlet finalConfidence;\nif (hasAiMatch) {\n  // Combine both scores with weights\n  finalConfidence = (deterministicScore * DETERMINISTIC_WEIGHT) + (aiScore * AI_WEIGHT);\n} else {\n  // Only deterministic match available\n  finalConfidence = deterministicScore;\n}\n\n// Normalize to 0-1 range\nfinalConfidence = Math.max(0, Math.min(1, finalConfidence));\n\n// Determine confidence level\nlet confidenceLevel;\nif (finalConfidence >= 0.9) {\n  confidenceLevel = 'HIGH';\n} else if (finalConfidence >= 0.7) {\n  confidenceLevel = 'MEDIUM';\n} else if (finalConfidence >= 0.5) {\n  confidenceLevel = 'LOW';\n} else {\n  confidenceLevel = 'VERY_LOW';\n}\n\n// Build audit trail\nconst auditTrail = {\n  timestamp: new Date().toISOString(),\n  deterministicScore: deterministicScore,\n  aiScore: hasAiMatch ? aiScore : null,\n  finalConfidence: finalConfidence,\n  confidenceLevel: confidenceLevel,\n  matchingMethod: hasAiMatch ? 'HYBRID' : 'DETERMINISTIC',\n  weights: {\n    deterministic: DETERMINISTIC_WEIGHT,\n    ai: AI_WEIGHT\n  }\n};\n\n// Return enriched item with confidence scores and audit trail\nreturn {\n  json: {\n    ...item,\n    finalConfidence: finalConfidence,\n    confidenceLevel: confidenceLevel,\n    confidencePercentage: Math.round(finalConfidence * 100),\n    auditTrail: auditTrail\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "e96842c2-5bc0-44e0-b3e9-bfcbb62a80e6",
      "name": "Route by Confidence Threshold",
      "type": "n8n-nodes-base.if",
      "position": [
        2080,
        320
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "id-1",
              "operator": {
                "type": "number",
                "operation": "gte"
              },
              "leftValue": "={{ $json.finalConfidence }}",
              "rightValue": "={{ $('Workflow Configuration').first().json.confidenceThreshold }}"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "b10d37cf-b377-4397-8b67-f358bfc5da07",
      "name": "Flag for Human Review",
      "type": "n8n-nodes-base.set",
      "position": [
        2320,
        400
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "reviewStatus",
              "type": "string",
              "value": "pending_human_review"
            },
            {
              "id": "id-2",
              "name": "reviewReason",
              "type": "string",
              "value": "Low confidence match - requires manual verification"
            },
            {
              "id": "id-3",
              "name": "flaggedAt",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "52559270-484a-49d0-847e-f004518abb02",
      "name": "Mark as Auto-Reconciled",
      "type": "n8n-nodes-base.set",
      "position": [
        2320,
        256
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "reviewStatus",
              "type": "string",
              "value": "auto_reconciled"
            },
            {
              "id": "id-2",
              "name": "reconciledAt",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "fe27e284-4bc7-446b-85fc-1cd03f8b8f81",
      "name": "Merge All Results",
      "type": "n8n-nodes-base.merge",
      "position": [
        2640,
        352
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "4cdd473a-2795-43ed-a464-a874d458e3ff",
      "name": "Log to Reconciliation Report",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2864,
        352
      ],
      "parameters": {
        "columns": {
          "value": null,
          "mappingMode": "autoMapInputData"
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Reconciliation Report"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "<__PLACEHOLDER_VALUE__Google Sheets Document ID__>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "2682e628-ed05-4fd2-8feb-43d9caff7470",
      "name": "Notify Finance Team",
      "type": "n8n-nodes-base.slack",
      "position": [
        3120,
        352
      ],
      "parameters": {
        "text": "=Reconciliation Complete!\n\nTotal Records Processed: {{ $json.totalRecords }}\nAuto-Reconciled: {{ $json.autoReconciledCount }}\nPending Review: {{ $json.pendingReviewCount }}\n\nView full report: {{ $json.reportUrl }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "<__PLACEHOLDER_VALUE__Slack Channel ID or Name__>"
        },
        "otherOptions": {}
      },
      "typeVersion": 2.4
    },
    {
      "id": "3d42b58b-ab5e-4969-bbd9-96444b55ee6c",
      "name": "Merge All Normalized Data1",
      "type": "n8n-nodes-base.merge",
      "position": [
        448,
        432
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineAll"
      },
      "typeVersion": 3.2
    },
    {
      "id": "e879468a-0a17-4a40-ada7-79e8023272e2",
      "name": "Merge All Normalized Data2",
      "type": "n8n-nodes-base.merge",
      "position": [
        640,
        304
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineAll"
      },
      "typeVersion": 3.2
    },
    {
      "id": "c8f83a37-b017-4749-99da-e51966fabb13",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1664,
        64
      ],
      "parameters": {
        "width": 560,
        "height": 528,
        "content": "\n## How it works\nThis workflow automates reconciliation across bank statements, invoices, ERP systems, and CSV uploads. It standardizes all data into a unified format and performs deterministic matching using fields like ID, amount, and date.\n\nFor unmatched records, AI fuzzy matching identifies potential matches using descriptions, amount tolerance, and date proximity. A final confidence score is calculated, auto-reconciling high-confidence matches while flagging others for review.\n\n## Setup\n\n- Configure webhook to receive financial data  \n- Set matching keys and confidence thresholds  \n- Connect OpenAI for fuzzy matching  \n- Connect Google Sheets for reporting  \n- Connect Slack for notifications  \n- Ensure input data follows expected formats  \n- Test with sample data before activating  \n"
      },
      "typeVersion": 1
    },
    {
      "id": "d97902db-baa8-41af-8e92-60ed7b090c42",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -864,
        144
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 352,
        "content": "## Data Input\nReceives financial data via webhook from multiple sources and Defines matching keys, thresholds, and reconciliation rules."
      },
      "typeVersion": 1
    },
    {
      "id": "1f5d7acb-20d4-4968-ae11-15a950fb1d82",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -432,
        144
      ],
      "parameters": {
        "color": 7,
        "width": 256,
        "height": 352,
        "content": "## Source Routing\nDetects data type and routes to correct normalization flow.)"
      },
      "typeVersion": 1
    },
    {
      "id": "d5f1e7f2-2466-407a-bf7e-863fefa466f5",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -96,
        -80
      ],
      "parameters": {
        "color": 7,
        "width": 272,
        "height": 848,
        "content": "## Data Normalization\nConverts bank, invoice, ERP, and CSV data into a unified schema."
      },
      "typeVersion": 1
    },
    {
      "id": "57d64771-fda3-4bd2-9b0a-d5e60537f5ae",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        384,
        80
      ],
      "parameters": {
        "color": 7,
        "width": 384,
        "height": 528,
        "content": "## Data Merge\nCombines all normalized records for comparison."
      },
      "typeVersion": 1
    },
    {
      "id": "f93866d8-35f9-4065-8a18-d16ce408a60a",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        800,
        128
      ],
      "parameters": {
        "color": 7,
        "width": 352,
        "height": 384,
        "content": "## Deterministic Matching\nMatches records using exact fields like ID, amount, and date."
      },
      "typeVersion": 1
    },
    {
      "id": "1d8eb26d-23ee-4ca0-9620-3bd8775e7559",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1200,
        416
      ],
      "parameters": {
        "color": 7,
        "width": 336,
        "height": 352,
        "content": "## AI Fuzzy Matching\nUses AI to find near matches based on text, amount, and date."
      },
      "typeVersion": 1
    },
    {
      "id": "10ca8421-c5f2-4033-a2e5-bf8f845322e1",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1584,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 272,
        "content": "## Confidence Scoring\nCombines matching results into final confidence score with audit trail."
      },
      "typeVersion": 1
    },
    {
      "id": "89ed1034-6845-4de2-8a5c-5a235c58bf7e",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2048,
        176
      ],
      "parameters": {
        "color": 7,
        "width": 464,
        "height": 416,
        "content": "## Decision Routing\nRoutes high-confidence matches or flags low-confidence for review."
      },
      "typeVersion": 1
    },
    {
      "id": "ef0d6bea-dbd9-42cb-b9e6-6fdac191bbd4",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2832,
        176
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 416,
        "content": "## Reporting\nLogs reconciliation results into Google Sheets and Sends summary of reconciliation results via Slack."
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Extract CSV Data": {
      "main": [
        [
          {
            "node": "Normalize CSV Schema",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge All Results": {
      "main": [
        [
          {
            "node": "Log to Reconciliation Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Fuzzy Matching Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Check Match Quality": {
      "main": [
        [
          {
            "node": "Merge Matched Results",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "AI Fuzzy Matching Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize CSV Schema": {
      "main": [
        [
          {
            "node": "Merge All Normalized Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize ERP Schema": {
      "main": [
        [
          {
            "node": "Merge All Normalized Data1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Flag for Human Review": {
      "main": [
        [
          {
            "node": "Merge All Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Matched Results": {
      "main": [
        [
          {
            "node": "Calculate Confidence Scores",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Data Source Type": {
      "main": [
        [
          {
            "node": "Normalize Bank Statement Schema",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Normalize Invoice Schema",
            "type": "main",
            "index": 0
          },
          {
            "node": "Normalize ERP Schema",
            "type": "main",
            "index": 0
          },
          {
            "node": "Extract CSV Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Workflow Configuration": {
      "main": [
        [
          {
            "node": "Check Data Source Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Fuzzy Matching Agent": {
      "main": [
        [
          {
            "node": "Merge Matched Results",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Mark as Auto-Reconciled": {
      "main": [
        [
          {
            "node": "Merge All Results",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Normalize Invoice Schema": {
      "main": [
        [
          {
            "node": "Merge All Normalized Data1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "AI Fuzzy Matching Agent",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Merge All Normalized Data": {
      "main": [
        [
          {
            "node": "Merge All Normalized Data2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge All Normalized Data1": {
      "main": [
        [
          {
            "node": "Merge All Normalized Data2",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge All Normalized Data2": {
      "main": [
        [
          {
            "node": "Deterministic Matching Logic",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Confidence Scores": {
      "main": [
        [
          {
            "node": "Route by Confidence Threshold",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Deterministic Matching Logic": {
      "main": [
        [
          {
            "node": "Check Match Quality",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log to Reconciliation Report": {
      "main": [
        [
          {
            "node": "Notify Finance Team",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Confidence Threshold": {
      "main": [
        [
          {
            "node": "Mark as Auto-Reconciled",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Flag for Human Review",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Bank Statement Schema": {
      "main": [
        [
          {
            "node": "Merge All Normalized Data",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Webhook - Receive Financial Data": {
      "main": [
        [
          {
            "node": "Workflow Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow automates financial reconciliation across multiple data sources such as bank statements, invoices, ERP systems, and CSV uploads.

Source: https://n8n.io/workflows/14276/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Enhance your support, onboarding, and internal knowledge workflows with an intelligent RAG-powered chatbot that responds using live data stored in Google Sheets. 🤖📚 Built for teams that rely on struct

Chat Trigger, Output Parser Structured, Memory Buffer Window +6
AI & RAG

This workflow automates customer feedback processing by analyzing sentiment, identifying key issues, generating personalized responses, and escalating critical cases to support teams when required. De

Redis, Postgres, Agent +7
AI & RAG

This workflow automates the creation and publishing of LinkedIn posts with AI-generated content and human approval via Slack, using Google Sheets, OpenAI (GPT-4), Slack Interactive Messages, and the L

Output Parser Structured, OpenAI Chat, Google Sheets Trigger +4
AI & RAG

🧾 Short Description

Agent, OpenAI Chat, Output Parser Structured +4
AI & RAG

This workflow automates legislative compliance analysis by coordinating multiple specialized OpenAI agents to interpret regulatory documents, evaluate organizational impact, and manage stakeholder com

HTTP Request, OpenAI Chat, Output Parser Structured +4