Schedule icon
Webhook icon
Script icon
Docker icon
Query icon
OutputValues icon
If icon
Log icon
ChatCompletion icon
GoogleGemini icon
Write icon
SlackIncomingWebhook icon
Pause icon

Accounts Payable 3-Way Match with DuckDB, AI Triage, and Approval Gate

Reconcile Purchase Orders, Goods Receipts, and Vendor Invoices with DuckDB SQL, isolate AP variances, and optionally run Gemini AI triage and approval gates.

Categories
AIBusinessData

Accounts Payable (AP) teams lose significant working capital to duplicate vendor invoices, unreceived goods billing, quantity overbilling on partial deliveries, and unauthorized unit price hikes. Manually cross-checking Purchase Orders (POs), warehouse Goods Receipt Notes (GRNs), and inbound Vendor Invoices in spreadsheets slows down payment cycles and lets costly variances slip into ERP payment runs.

This blueprint automates deterministic Accounts Payable 3-Way Matching using embedded DuckDB SQL and Python, partitioning inbound invoices into a clean ERP payment batch (auto_approved_erp_batch.csv) and a quarantined exception report (held_exceptions_report.csv). By default, it runs completely self-contained with realistic sample data and zero external credentials required, while providing one-click toggles for Google Gemini AI dispute drafting, Slack alerting, and human-in-the-loop Controller approval gating.

How it works

  1. stage_ap_documents (io.kestra.plugin.scripts.python.Script) normalizes the inbound purchase_orders, goods_receipts, and vendor_invoices JSON inputs into staging CSV files. Toggling inject_discrepancies (true by default) lets you test either the 6-invoice discrepancy scenario or a 100% clean batch.
  2. reconcile_three_way_match (io.kestra.plugin.jdbc.duckdb.Query) joins Purchase Orders, aggregated Goods Receipt quantities (SUM(accepted_qty) per PO and SKU), and deduplicated Vendor Invoices (ROW_NUMBER() OVER (PARTITION BY vendor_id, vendor_invoice_number, item_sku)). It evaluates deterministic control rules against configurable price_tolerance_pct (default 2.0%) and qty_tolerance_pct (default 0.0%):
    • DUPLICATE_INVOICE: Blocks duplicate submissions of the same vendor invoice number (INV-7006 duplicating APEX-88410).
    • MISSING_RECEIPT: Holds invoices billed before any warehouse Goods Receipt Note is recorded (INV-7004 with 0 received units).
    • QTY_OVERBILLED: Flags invoices billing for more units than accepted on the warehouse GRN (INV-7002 billing 50 units against 40 received).
    • INVALID_UNIT_PRICE: Quarantines records where either the contracted PO unit price or the billed unit price is negative (< 0.0).
    • PRICE_VARIANCE_EXCEEDED: Flags unit price overcharges exceeding price_tolerance_pct (INV-7003 at +10.0% variance), as well as any positive charge (billed_unit_price > 0.0) against a zero-price PO line (po_unit_price = 0.0), while auto-approving zero-for-zero lines and minor variances within tolerance (INV-7005 at +1.0% variance).
  3. generate_erp_and_exception_batches (io.kestra.plugin.scripts.python.Script) splits the reconciled dataset into auto_approved_erp_batch.csv and held_exceptions_report.csv, writes reconciliation_metrics.json, renders an executive Markdown audit table (ap_three_way_match_audit_report.md), and emits structured metrics via Kestra's stdout output protocol (::{"outputs": ...}::).
  4. expose_reconciliation_metrics (io.kestra.plugin.core.output.OutputValues) publishes structured counts and dollar variance metrics to the execution outputs.
  5. route_reconciliation_outcome (io.kestra.plugin.core.flow.If) branches on held_exceptions_count > 0. When exceptions exist, it logs the hold summary and evaluates three optional governance gates:
    • check_ai_triage_enabled (enable_ai_triage: true): Calls io.kestra.plugin.ai.completion.ChatCompletion with Google Gemini to draft vendor debit memos and ERP remediation steps (save_ai_triage_memo).
    • check_slack_alert_enabled (send_slack_alert: true): Posts an exception summary card via io.kestra.plugin.slack.notifications.SlackIncomingWebhook.
    • check_human_approval_required (require_human_approval: true): Pauses the execution via io.kestra.plugin.core.flow.Pause (await_controller_signoff) to record the Controller's disposition and audit notes.

What you get

  • Ready-to-post ERP Batch ({{ outputs.generate_erp_and_exception_batches.outputFiles['auto_approved_erp_batch.csv'] }}): Clean invoices verified against PO prices and warehouse receipts.
  • Quarantined Exceptions Report ({{ outputs.generate_erp_and_exception_batches.outputFiles['held_exceptions_report.csv'] }}): Held invoices with exact exception_code, line_variance_usd, price_variance_pct, overbilled_qty, and human-readable hold_explanation.
  • Executive Markdown Audit Report ({{ outputs.generate_erp_and_exception_batches.outputFiles['ap_three_way_match_audit_report.md'] }}): Formatted summary tables for internal audit and SOX control evidence.
  • Structured Execution Metrics ({{ outputs.expose_reconciliation_metrics.values }}): total_invoices_evaluated, auto_approved_count, held_exceptions_count, auto_approved_amount_usd, held_exposure_amount_usd, and held_overcharge_variance_usd.
  • Optional AI Dispute Memo ({{ outputs.save_ai_triage_memo.uri }}): Generated when enable_ai_triage is set to true.

Who it's for

  • Finance Automation and FinOps Engineers building automated procurement-to-pay (P2P) controls.
  • Accounts Payable Controllers seeking deterministic SQL guardrails combined with optional LLM dispute drafting.
  • Data Engineers integrating ERP subledgers (NetSuite, SAP, Dynamics, Coupa, or Stripe Billing) with warehouse receiving logs.

Prerequisites

  • Default dry-run mode requires zero external credentials or services. You can click Execute immediately using the built-in PO, GRN, and Invoice datasets.
  • To enable optional integrations, configure the corresponding namespace secrets:

Secrets

  • AP_WEBHOOK_KEY: Authentication key for the erp_invoice_batch_webhook trigger.
  • GEMINI_API_KEY: Google Gemini API key (required only when enable_ai_triage is set to true).
  • SLACK_WEBHOOK_URL: Slack Incoming Webhook URL (required only when send_slack_alert is set to true).

Quick start

  1. Click Execute with the default inputs (inject_discrepancies: true, price_tolerance_pct: 2.0) to run the 6-invoice 3-way match demo without any secrets.
  2. Open the Outputs tab to download auto_approved_erp_batch.csv (2 approved invoices totaling $37,120.00), held_exceptions_report.csv (4 held invoices totaling $49,900.00 in exposure and $36,100.00 in variance), and ap_three_way_match_audit_report.md.
  3. Re-run with inject_discrepancies: false to verify the clean-batch branch (else path) where 100% of invoices pass.
  4. Optionally set enable_ai_triage: true (with GEMINI_API_KEY), send_slack_alert: true (with SLACK_WEBHOOK_URL), or require_human_approval: true to test the full exception governance workflow.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.