Vendor Payment Reconciliation

Bank statement to AP ledger matching. Distinct from AP Invoice Reconciliation because the data is messier: bank descriptions rarely match vendor names cleanly, payments are often consolidated, and timing gaps are wider. Fuzzy matching and date windows do the heavy lifting.

What Gets Automated

Matching bank transactions to AP ledger entries despite format mismatches. Investigating consolidated payments, timing differences, partial payments, and unknown transactions. The agent prepares an evidence-backed recommendation for review.

What Humans Still Own

Unidentified transactions after AI investigation. Month-end reconciliation sign-off. Bank fee disputes and error corrections.

Pipeline

flowchart TD A[Input: Bank Statement + AP Ledger] --> B[Matcher: Fuzzy Match] B -->|Exact match ~50%| C[Auto-Clear] B -->|Fuzzy match ~25%| D{Within Tolerance?} B -->|No match ~25%| E[Exception Queue] D -->|Yes| F[Auto-Clear with Note] D -->|No| E E --> G[ReAct Agent: Investigate] G --> G1[Check consolidated payments] G1 --> G2[Search for timing delays] G2 --> G3[Fuzzy vendor name match] G3 -->|Identified| H[Auto-Resolve] G3 -->|Partial match| I[Human Review] G3 -->|Unidentified| I I -->|Matched| J[Manual Clear + Log] I -->|Unknown| K[Flag for Bank Inquiry] C --> L[Custom Table: Bank Recon Log] F --> L H --> L J --> L K --> L style B fill:#e8a84c,color:#09090b,stroke:none style G fill:#4ade80,color:#09090b,stroke:none style I fill:#60a5fa,color:#09090b,stroke:none

This template includes a schedule block: it runs automatically every day at 6 AM Eastern. Remove or adjust the schedule for manual execution.

## Workflow Definition
json
{
  "name": "vendor_payment_reconciliation",
  "definition": {
    "schedule": {
      "every": "1d",
      "at": "06:00",
      "timezone": "America/New_York"
    },
    "actions": [
      {
        "type": "matcher",
        "properties": {
          "left": "@input.bank_transactions",
          "right": "@input.ap_ledger",
          "matchOn": ["reference_number"],
          "tolerance": 25,
          "dateWindowDays": 7,
          "fuzzyThreshold": 75,
          "descriptionKey": "description",
          "outputMatched": "cleared",
          "outputUnmatchedLeft": "unmatched_bank",
          "outputUnmatchedRight": "outstanding_payments"
        }
      },
      {
        "type": "loop",
        "filter": {
          "condition": {
            "greaterThan": [{ "length": "@unmatched_bank" }, 0]
          }
        },
        "properties": {
          "mode": "foreach",
          "items_path": "@unmatched_bank",
          "item_variable_name": "txn",
          "actions_to_execute": [
            {
              "type": "custom-table",
              "properties": {
                "table": "payment_reconciliation_cases",
                "operation": "upsert",
                "key_fields": ["run_id", "transaction_id"],
                "keys": ["run_id", "transaction_id", "description", "amount", "transaction_date", "status"],
                "values": ["@__run_id", "@txn.transaction_id", "@txn.description", "@txn.amount", "@txn.date", "pending_review"]
              }
            }
          ],
          "max_concurrency": 5,
          "failure_strategy": "fail_fast",
          "collect_results": false,
          "result_key": "payment_case_writes"
        }
      },
      {
        "type": "loop",
        "filter": {
          "condition": {
            "greaterThan": [{ "length": "@unmatched_bank" }, 0]
          }
        },
        "properties": {
          "mode": "react",
          "objective": "Read the current run's unmatched payment cases. Investigate whether each transaction is a consolidated payment, a timing difference, a vendor-name mismatch, or a bank fee. Cite the records returned by the declared tools and recommend the next action. Do not post an accounting entry.",
          "tools": [
            { "type": "action", "name": "read_payment_reconciliation_cases" },
            { "type": "action", "name": "search_ap_by_amount" },
            { "type": "action", "name": "search_ap_by_date_range" },
            { "type": "action", "name": "search_ap_by_vendor" },
            { "type": "action", "name": "get_known_bank_fees" }
          ],
          "max_iterations": 12,
          "on_stuck": {
            "iterations": 3,
            "action": "retry_with_hint",
            "hint": "If the evidence is insufficient, complete with a recommendation for manual review."
          },
          "result_key": "bank_investigations"
        }
      },
      {
        "type": "PbotApproval",
        "filter": {
          "condition": {
            "greaterThan": [{ "length": "@unmatched_bank" }, 0]
          }
        },
        "properties": {
          "comment": "Bank reconciliation: {{cleared.length}} auto-cleared, {{unmatched_bank.length}} investigated. Review findings and approve close entries.",
          "request_payload": {
            "cleared_count": "@cleared.length",
            "outstanding_count": "@outstanding_payments.length",
            "investigations": "@bank_investigations"
          }
        }
      },
      {
        "type": "custom-table",
        "properties": {
          "table": "bank_reconciliation_log",
          "operation": "write",
          "keys": ["run_id", "reconciliation_date"],
          "values": ["@__run_id", "@now"],
          "fields": {
            "bank_transactions": "@input.bank_transactions.length",
            "auto_cleared": "@cleared.length",
            "investigated": "@unmatched_bank.length",
            "outstanding_payments": "@outstanding_payments.length",
            "status": "completed"
          }
        }
      }
    ]
  }
}

Required Registered Actions

Action Kind Purpose
search_ap_by_amount db Find AP entries matching an amount or sum of amounts
search_ap_by_date_range db Find AP entries within a date window
search_ap_by_vendor db Fuzzy search AP entries by vendor/payee name
get_known_bank_fees db Retrieve known fee patterns for the bank account
read_payment_reconciliation_cases custom-table Read the current run's persisted exception rows

The workflow writes unmatched bank transactions to payment_reconciliation_cases, keyed by run_id and transaction_id. Bind read_payment_reconciliation_cases to that table with operation: "read" and where: { "run_id": "@__run_id" }. This gives the agent a fixed, read-only batch view before approval.

Customization Notes

Fuzzy threshold. 75 is lower than other templates because bank descriptions are notoriously messy ("WIRE TRF ACME" vs. "Acme Corporation"). Lower to 65 if your bank formats are especially terse; raise to 85 if descriptions are reasonably clean.

Date window. 7 days covers typical bank clearing delays. Extend to 10-14 days for international wires or ACH batches that clear slowly.

Consolidated payment detection. The search_ap_by_amount action should support sum-matching: finding multiple AP entries whose amounts sum to the bank transaction amount. This is the most common exception type in bank reconciliation.

Bank fee patterns. The get_known_bank_fees action returns known fee types and amounts for the bank account. Preloading common patterns (monthly maintenance fees, wire fees, etc.) lets the agent quickly classify these without investigation.