>
Module 05
Fintech AI

Payments and Reconciliation

Proving where the money actually is. This module covers the matching cascade, tolerance design, one-to-many settlement matching, the exception taxonomy, India’s URCS and TAT dispute rails, double-entry ledger design, and the ways a reconciliation silently goes wrong while reporting a perfect match rate.

Verified May 2026Free · No signupOfficial sources only
BeginnerStart here. No prior knowledge assumed.
Product guide

Collecting on a standing instruction? The eight steps and the 2026 e-mandate limits: Recurring Payments: How to Build It →

Product guide

Moving money across the border? The eight steps, the PA-CB rules, and where the FX cost hides: Cross-Border Payments: How to Build It →

Product guide

Selling cover inside your own checkout? The eight steps, who is allowed to sell, and what changes on 1 January 2027: Embedded Insurance: How to Build It →

Product guide

Building on the UPI rails themselves? PSP banks, NPCI certification, the deemed state, and what the September 2026 MDR notification changes: UPI Switch Infrastructure: How to Build It →

Product guide

Turning paper cheques into data? The eight steps, the CTS image specification, and what the October 2025 clearing change means: Cheque Reading: How to Build It →

Product guide

Deciding whether to let a business accept payments through you? The eight steps, the 2025 PA Directions, and the marketplace rule that makes you answerable for sellers you never onboarded: Merchant Onboarding: How to Build It →

Draft guide

Wondering what happens when an AI agent initiates the payment? What is known about NPCI’s Unified Agent Protocol, the liability question nobody has answered, and what to do now: Agentic Payments: How to Prepare →

Build sheet

Every tool for this module, how to use each one, what it costs, the best combinations and three recommended builds: Payments Build Sheet →

What reconciliation actually is

You think you moved money. The bank thinks something happened. Your ledger says one thing, the settlement file says another. Reconciliation is the process of proving those views agree ’ and finding the ones that do not.

Every payment touches at least three records:

  1. Your ledger — what your system believes happened
  2. The processor or scheme — what the payment rail reports
  3. The bank account — what money actually arrived or left

When all three agree, nothing needs doing. When they disagree, someone’s money is in the wrong place and it is your problem to find out whose.

Note

This is the least glamorous module in the section and the one that most often decides whether a fintech survives its first audit. A company can have excellent fraud detection and a beautiful app, and still fail because it cannot prove where its customers’ money is.

Why it is harder than matching two lists

If every payment had a unique reference that appeared identically in all three systems, this would be a database join. It does not.

What goes wrongWhy
Timing differencesA transaction happens on Tuesday and settles on Thursday. Both records are correct; they disagree on Wednesday.
NettingThe processor settles 400 transactions as one bank credit, minus fees. One deposit, four hundred records.
Fees deducted at sourceYou expected ₹10,000; ₹9,823 arrived. The difference is a fee you have to derive.
Partial paymentsOne invoice paid across three transfers. Or three invoices paid with one lump sum.
Missing referencesThe customer paid with no reference at all, which is extremely common.
Reversals and refundsWhich original does this refund belong to? Often not stated.
Currency and roundingConversion applied at a rate you did not choose, rounded in a direction you did not pick.

The last one on that list ’ a payment with no reference ’ is where most reconciliation software quietly fails.

What AI can and cannot do here

TaskHow well AI does it
Match transactions with clean referencesTrivially. Deterministic rules handle this; no AI needed.
Match with tolerance for timing and roundingVery well. Still rules, just better rules.
Match a payment with no reference to an open invoiceWell. Amount, timing, payer history, partial-text similarity. This is where AI earns its place.
Many-to-many matchingReasonably. Combinatorially hard and the main source of residual exceptions.
Explain why two records did not matchWell. Genuinely useful for the exception queue.
Decide that a difference is acceptableIt should not. That is a write-off decision with accounting consequences.
Sign off a reconciliationNo. Someone is accountable for that signature.
Watch out

Mature platforms report auto-match rates between 90% and 99%, and published benchmarks show reconciling 1,000 records dropping from around 7.2 hours manually to about 1.6. Those gains are real. They are also gains on matching, not on accountability — do not automate matching and remove human sign-off in the same project.

The structural fact: money moves on a different clock

The single idea that makes reconciliation make sense: the transaction clock and the settlement clock are different clocks.

A card payment is authorised instantly, captured later, settled in batch, and the money lands in your account a day or more after the customer saw "payment successful". A UPI payment appears instant to the user and still settles between banks in defined cycles.

Everything in this module follows from that gap. Exceptions are usually not errors — they are records observed at different points on two different clocks. The skill is telling apart a timing difference that will resolve itself from a genuine break that will not.

In India this is codified. The RBI harmonised turn-around-time framework sets the expected auto-reversal window for failed transactions, and the NPCI dispute rails are built around settlement cycles rather than wall-clock time. Designing your reconciliation on wall-clock assumptions guarantees false exceptions.

IntermediateBuild it. Pipelines, tools and working code.

The matching cascade

Run the most certain rules first. Every stage you clear cheaply is a stage the expensive ones never have to consider.

The matching cascade — cheapest and most certain first
RECORDS FROM BOTH SIDES
  |
  1. EXACT REFERENCE      unique id present and identical on both sides
  |                       -> should clear 60-80% instantly. If it does not,
  |                          fix your reference propagation before anything else.
  |
  2. EXACT AMOUNT + DATE  same amount, same value date, one candidate only
  |
  3. TOLERANCE MATCH      amount within fee/rounding tolerance,
  |                       date within settlement window
  |                       -> tolerances are POLICY, versioned and approved
  |
  4. DERIVED-FEE MATCH    expected_gross - known_fee_schedule = observed_net
  |
  5. ONE-TO-MANY          one settlement credit vs N transactions
  |                       -> subset-sum within tolerance
  |
  6. MANY-TO-MANY         N payments against M invoices
  |                       -> the genuinely hard case
  |
  7. PROBABILISTIC        no reference: score on amount, timing, payer
  |                       history, narration similarity
  |                       -> SUGGESTS a match, a human confirms
  |
  8. EXCEPTION            categorise, age, assign an owner
  |
NEVER let stages 5-7 auto-post without review until you have measured
their precision on your own data for at least one full close cycle.
Python — deterministic core with tolerance bands
from dataclasses import dataclass
from datetime import date, timedelta
from decimal import Decimal

@dataclass(frozen=True)
class Tolerance:
    """Tolerances are POLICY. Versioned, approved, logged with every match.
    Widening a tolerance to clear a backlog is a control change, not a
    configuration tweak."""
    version: str
    amount_abs: Decimal        # absolute paise/cents tolerance
    amount_pct: Decimal        # proportional tolerance for fee variance
    date_days: int             # settlement window

def amount_within(a: Decimal, b: Decimal, tol: Tolerance) -> bool:
    diff = abs(a - b)
    return diff <= tol.amount_abs or (b and diff / abs(b) <= tol.amount_pct)

def match_pass(ledger, bank, tol: Tolerance):
    """Returns (matches, unmatched_ledger, unmatched_bank).
    Each match records WHICH RULE fired - that is what makes the
    reconciliation auditable rather than merely complete."""
    matches, used_bank = [], set()

    # Stage 1: exact reference. Highest certainty, do it first.
    bank_by_ref = {}
    for b in bank:
        if b.get("reference"):
            bank_by_ref.setdefault(b["reference"].strip().upper(), []).append(b)

    for l in ledger:
        ref = (l.get("reference") or "").strip().upper()
        cands = [b for b in bank_by_ref.get(ref, []) if id(b) not in used_bank]
        if ref and len(cands) == 1:
            b = cands[0]
            used_bank.add(id(b))
            matches.append({"ledger": l, "bank": b, "rule": "exact_reference",
                            "confidence": 1.0, "tolerance_version": tol.version})

    # Stage 2-3: amount + date within tolerance, ONE candidate only.
    # Ambiguity is an exception, never a guess.
    rem_l = [l for l in ledger if not any(m["ledger"] is l for m in matches)]
    for l in rem_l:
        cands = [b for b in bank
                 if id(b) not in used_bank
                 and amount_within(Decimal(str(l["amount"])),
                                   Decimal(str(b["amount"])), tol)
                 and abs((l["value_date"] - b["value_date"]).days) <= tol.date_days]
        if len(cands) == 1:
            b = cands[0]
            used_bank.add(id(b))
            exact = Decimal(str(l["amount"])) == Decimal(str(b["amount"]))
            matches.append({"ledger": l, "bank": b,
                            "rule": "amount_date_exact" if exact else "amount_date_tolerance",
                            "difference": Decimal(str(b["amount"])) - Decimal(str(l["amount"])),
                            "confidence": 0.95 if exact else 0.85,
                            "tolerance_version": tol.version})

    matched_l = {id(m["ledger"]) for m in matches}
    return (matches,
            [l for l in ledger if id(l) not in matched_l],
            [b for b in bank if id(b) not in used_bank])

Record which rule fired

The field that makes a reconciliation auditable is not whether it matched ’ it is why. "Matched by exact reference" and "matched by amount within a 2% tolerance" carry very different confidence, and an auditor will ask you to separate them.

Note

If your exact-reference stage is not clearing the majority of volume, stop building matching logic and go fix reference propagation instead. A reference that survives from your ledger through the processor to the bank statement is worth more than any amount of fuzzy matching downstream.

One-to-many and the ambiguity rule

A processor settles four hundred transactions as a single bank credit, net of fees. Matching that credit back to its components is a subset-sum problem, and subset-sum is exponential.

Python — one-to-many subset matching (bounded, not brute force)
from itertools import combinations
from decimal import Decimal

def subset_match(settlement_amount, candidates, tol, max_items=6, max_combos=50000):
    """A single settlement credit against N underlying transactions.
    Brute force is 2^N - bound it hard and fall through to exception.

    Returns the UNIQUE subset if exactly one fits. Multiple fits means
    ambiguity, and ambiguity goes to a human. Picking one arbitrarily
    is how reconciliations become confidently wrong."""
    target = Decimal(str(settlement_amount))
    hits, tried = [], 0

    # Prune first: nothing larger than the target can be in the subset
    pool = sorted((c for c in candidates
                   if Decimal(str(c["amount"])) <= target + tol.amount_abs),
                  key=lambda c: -Decimal(str(c["amount"])))

    for size in range(1, min(max_items, len(pool)) + 1):
        for combo in combinations(pool, size):
            tried += 1
            if tried > max_combos:
                return {"status": "search_exhausted", "tried": tried}
            total = sum(Decimal(str(c["amount"])) for c in combo)
            if abs(total - target) <= tol.amount_abs:
                hits.append(combo)
                if len(hits) > 1:
                    return {"status": "ambiguous", "candidate_count": len(hits)}

    if len(hits) == 1:
        return {"status": "matched", "items": hits[0], "rule": "one_to_many_subset"}
    return {"status": "no_match"}

Two design decisions in that code matter more than the algorithm. Bound the search, because an unbounded combinatorial match will hang your close. And treat ambiguity as an exception — if more than one subset fits, a human decides. Picking the first fit is how a reconciliation becomes confidently and invisibly wrong.

In practice, most settlement files include a batch or settlement identifier that makes this unnecessary. Use it if it exists. Subset matching is the fallback for processors whose files do not.

The exception taxonomy

Unmatched items are not one thing. Categorising them is what turns a queue into a workflow.

CategoryMeaningTypical actionAges to
TimingIn one system, not yet the otherWait for the next cycleAuto-resolves, or becomes a real break at T+n
Fee varianceNet differs from expected by an undocumented amountReconcile against the fee schedule; if persistent, raise with the processorRevenue leakage
Missing in bankYour ledger says paid; no money arrivedInvestigate urgentlyReal loss or a failed payment not handled
Missing in ledgerMoney arrived you did not expectIdentify the payer; do not spend itUnapplied cash, and a liability
Amount mismatchBoth exist, amounts differ beyond toleranceDetermine which is correctAdjustment or dispute
DuplicateSame payment appears twiceCheck idempotency; refund if genuinely double-chargedCustomer complaint
AmbiguousMultiple plausible matchesHuman decisionStays until someone decides

The one people underestimate

"Missing in ledger" — money that arrived and you cannot identify — is the most dangerous category. It looks like a windfall and it is a liability. Somebody paid you for something, and until you know who and why, that money is not yours.

Unapplied cash sitting in a suspense account for months is a standard audit finding and a standard route to customer complaints, because the customer who paid believes they have paid and your system believes they have not.

India: the dispute and settlement rails

Indian payment disputes run on specific infrastructure with specific timing, and the rules changed materially.

Turn-around time

The RBI harmonised turn-around-time framework (circular RBI/2019-20/67, 20 September 2019, as subsequently amended) sets expected auto-reversal windows for failed transactions across payment systems. For failed UPI transactions the auto-reversal expectation is T+1 working day, with compensation payable for delay beyond it.

Design consequence: a debit with no corresponding credit is not immediately a break. It is an item inside a defined reversal window. Alerting on it at T+0 generates noise; failing to alert at T+2 is a compliance problem.

URCS, UDIR and the TCC/RET mechanism

UPI disputes are handled through the UPI Dispute Resolution System, with the Unified Dispute and Issue Resolution interface for customer-initiated queries. Two codes do most of the work:

  • TCC — Transaction Credit Confirmation. The beneficiary bank confirms the credit was applied.
  • RET — Return. The beneficiary bank returns the funds because the credit could not be applied.

The deemed-acceptance trap that was fixed

Worth understanding because it shows how settlement timing becomes a financial risk.

Previously, a remitting bank could file a chargeback in URCS from T+0 — often before the beneficiary bank had finished reconciling. The beneficiary bank would raise a return, find the chargeback already filed, and the chargeback would close with deemed acceptance. Banks were penalised by the RBI for the resulting mess.

NPCI revised the framework so that from 15 February 2025, URCS automatically accepts or rejects a chargeback based on the TCC or RET raised by the beneficiary bank in the subsequent settlement cycle — giving the beneficiary bank a cycle to reconcile first. The change applies to bulk upload and UDIR paths rather than front-end dispute options.

Watch out

The lesson generalises beyond UPI: if your dispute process can conclude before your reconciliation process has run, you will lose money to deemed outcomes. Sequence disputes behind settlement, not in parallel with it.

Customer-facing timelines

RBI data indicates UPI complaints exceeded 1.2 million per month in early 2026, with around 60% being "amount debited but not credited". Customers who are unsatisfied can escalate to the RBI Ombudsman through the CMS portal after the bank window elapses. The Reserve Bank - Integrated Ombudsman Scheme 2026 replaced the 2021 scheme from 1 July 2026, with award limits of up to ₹30 lakh for consequential loss and up to ₹3 lakh for time, expense and harassment.

Build your internal service levels inside those windows, not up against them.

The registry

Reconciliation platforms

Verified May 2026
HighRadiusdirect
Enterprise record-to-report and invoice-to-cash. Published auto-match rates in the mid-90s and above. Three-to-six-month implementations; suits large finance operations.
BlackLinedirect
The incumbent default for Fortune 500 close operations. Mature, heavy, audit-oriented.
Trintechdirect
Financial close and reconciliation, strong in regulated and SOX environments.
Ledgedirect
Built for high-volume real-time fintech and marketplace matching. Faster to first production matching than enterprise suites.
Numericdirect
AI-native, oriented to modern-stack ERPs and mid-market finance teams.
Kognitosdirect
Agentic automation across bank matching, AP and AR with a deterministic audit trail.
Nanonets / Osfin / Cointabdirect
Reconciliation automation with India market presence.

Payment rails and data sources — India

Verified May 2026
NPCI URCS / UDIRindirect
UPI dispute resolution. Accessed through your sponsor bank or PSP, not directly.
NPCI settlement filesindirect
Settlement and raw data files reach you via the sponsor bank or payment aggregator.
RBI CMS portaldirect
Ombudsman complaint system — customer-facing, and you should monitor what reaches it.
Razorpay / Cashfree / PayU / Paytm PGdirect
Payment aggregators. Settlement reports and reconciliation APIs; the practical source of truth for most fintechs.
Account Aggregatorindirect
Consented bank statement data — usable for reconciliation as well as underwriting.

Build-your-own components

Verified May 2026
pandas / Polarsoss
Dataframe matching for moderate volumes. Polars handles larger files materially faster.
DuckDBoss
In-process analytical SQL. Excellent for reconciliation joins over large files without a warehouse.
RapidFuzzoss
String similarity for narration-based probabilistic matching.
Apache Arrow / Parquetoss
Columnar storage for settlement file archives you must retain and re-query.
dbtoss
Versioned, tested transformation logic — useful because reconciliation rules are exactly the kind of logic that must be testable.
Watch out

Registry reflects what was publicly visible in May 2026. NPCI dispute rules in particular have changed more than once; verify the current circular set with your sponsor bank before designing around any timing assumption.

A prompt for designing your reconciliation

Prompt — paste into any AI
You are a payments operations lead who has built reconciliation
for high-volume fintechs.

My situation:
- Rails I use: [e.g. UPI via Razorpay, cards via a PG, NACH mandates]
- Daily transaction volume: [number]
- Settlement pattern: [e.g. T+1 net settlement, gross, multiple]
- Systems holding records: [ledger, PG dashboard, bank statement, ERP]
- Current process: [describe, or "spreadsheets"]
- Team size on reconciliation: [number]

Give me:

1. A matching cascade for MY rails - the specific stages in order,
   and what share of volume each should clear.
2. The tolerance bands I should start with for amount and date,
   with the reasoning for each rail.
3. My exception categories, and the action and owner for each.
4. Which exceptions are timing artefacts that will self-resolve, and
   at what point each becomes a genuine break I must investigate.
5. The India-specific timing rules I must respect - TAT windows,
   settlement cycles, dispute sequencing.
6. What to log so that a year from now I can prove where a specific
   customer's money went.
7. The three ways this reconciliation will silently go wrong.

Be concrete about numbers and say where they must be calibrated on
my own settlement data.
AdvancedShip it. Failure modes, thresholds and evidence.

Design the ledger so breaks cannot happen

Most reconciliation problems are ledger design problems that became visible at settlement. Three decisions prevent the majority of them.

Double-entry ledger — the design that prevents most breaks
-- Money is never a column you UPDATE. It is a sequence of entries
-- you APPEND. Balances are derived, never stored as mutable truth.

CREATE TABLE ledger_entry (
  entry_id        BIGSERIAL PRIMARY KEY,
  transaction_id  UUID        NOT NULL,   -- groups the legs together
  account_id      TEXT        NOT NULL,
  direction       CHAR(2)     NOT NULL CHECK (direction IN ('DR','CR')),
  amount_minor    BIGINT      NOT NULL CHECK (amount_minor > 0),
  currency        CHAR(3)     NOT NULL,
  effective_at    TIMESTAMPTZ NOT NULL,   -- when it counts (value date)
  recorded_at     TIMESTAMPTZ NOT NULL DEFAULT now(),  -- when we learned
  idempotency_key TEXT        NOT NULL,
  external_ref    TEXT,                   -- the reference that must survive
  metadata        JSONB       NOT NULL DEFAULT '{}',
  UNIQUE (idempotency_key)
);

-- Every transaction must balance. Enforce it, do not assume it.
CREATE OR REPLACE FUNCTION assert_balanced(txn UUID) RETURNS VOID AS $$
DECLARE d BIGINT; c BIGINT;
BEGIN
  SELECT COALESCE(SUM(amount_minor) FILTER (WHERE direction='DR'),0),
         COALESCE(SUM(amount_minor) FILTER (WHERE direction='CR'),0)
    INTO d, c FROM ledger_entry WHERE transaction_id = txn;
  IF d <> c THEN
    RAISE EXCEPTION 'Unbalanced transaction %: DR=% CR=%', txn, d, c;
  END IF;
END; $$ LANGUAGE plpgsql;

-- THREE THINGS THAT PREVENT MOST RECONCILIATION PAIN:
--
-- 1. amount_minor as BIGINT in the smallest unit (paise, cents).
--    Never floating point. Never. 0.1 + 0.2 != 0.3 and you will
--    discover this during a month-end close.
--
-- 2. effective_at separate from recorded_at. The two clocks from the
--    Beginner lane, made explicit in the schema. This is what lets you
--    answer "what did we believe on Tuesday" versus "what was true".
--
-- 3. idempotency_key UNIQUE. A retried webhook, a double-clicked
--    button, a replayed settlement file - all become no-ops instead
--    of duplicate money.

Idempotency is the one to get right first

Payment systems retry. Webhooks arrive twice. Settlement files get reprocessed after a partial failure. Networks time out after the money moved but before you heard about it.

Without an idempotency key on the write path, every one of those becomes a duplicate ledger entry, and duplicates are the hardest reconciliation break to unwind because both entries look legitimate.

Note

The rule worth adopting: a payment is identified by a key the caller supplies, not by one you generate. If the caller retries with the same key you return the original result rather than creating a second transaction. This single pattern removes an entire category of exception.

Measuring it honestly

Auto-match rate is the number everyone quotes and it is easy to make look good.

Reconciliation metrics that mean something
1. AUTO-MATCH RATE, by rail and by rule
   matched automatically / total items
   Mature platforms report 90-99%. Track it MONTHLY by rail - a drop
   in one rail is a signal, a blended figure hides it.
   Break it down by RULE: a high rate achieved mostly through wide
   tolerances is not the same as one achieved by exact reference.

2. EXCEPTION AGING
   The single most important number. No unmatched item older than
   30 days without a documented owner and a plan.
   Report the distribution, not the average: 0-3d / 4-7d / 8-30d / 30d+

3. VALUE AT RISK IN EXCEPTIONS
   Sum of unmatched amounts, by category and age.
   500 exceptions worth Rs 200 each is an annoyance.
   3 exceptions worth Rs 40 lakh each is an incident.

4. REVIEWER HOURS: PREPARING vs APPROVING
   Before automation the ratio is heavily weighted to preparing.
   It should invert within two close cycles. If it has not, the
   automation moved work rather than removing it.

5. DAYS TO RECONCILE
   Target a 50% reduction within two close cycles.

WHAT NOT TO REPORT AS SUCCESS
  - auto-match rate alone, with no rule breakdown
  - exceptions CLOSED, with no distinction between resolved
    and written off
  - a blended rate across rails with very different profiles

The way auto-match rate gets gamed

Widen the tolerances and the auto-match rate rises immediately. The exceptions do not disappear ’ they get absorbed into matches that are slightly wrong, and the error accumulates somewhere invisible.

This is why tolerance changes should be treated as control changes: versioned, approved, dated, with the auto-match rate reported before and after so the improvement is attributable to the right cause.

Watch out

If auto-match rate improves sharply and exception value does not fall proportionately, someone widened a tolerance. Ask.

Disputes and chargebacks as a system

A dispute is a reconciliation problem with a deadline and a counterparty.

The lifecycle

  1. Trigger — customer complaint, failed credit, or a scheme-initiated claim
  2. Evidence assembly — the transaction record, the authorisation, delivery proof, prior history
  3. Representment or acceptance — contest with evidence, or accept the loss
  4. Resolution — funds move, or do not
  5. Post-mortem — feed the outcome back into fraud and product

Where AI genuinely helps

Evidence assembly is the slow part and it is mechanical. Pulling the transaction, the device fingerprint, the delivery confirmation, the customer contact history and prior dispute record into a single pack is exactly the kind of retrieval task that should be automated.

Deciding whether to contest is not. That is a commercial judgement about the cost of representment against the probability of winning against the customer relationship.

The economics people get wrong

Contesting a dispute has a cost ’ staff time, sometimes a scheme fee, sometimes a penalty if you lose. For small amounts, accepting is frequently the rational answer and teams contest anyway out of principle.

Model it: expected value of contesting is (probability of winning × amount) minus cost of representment. Set a threshold below which you accept automatically, and review it quarterly against actual win rates rather than assumed ones.

Note

Watch the dispute ratio as a separate metric from the dispute count. Schemes and regulators care about the ratio, and a rising ratio with flat volume signals a product problem — not a fraud problem — far earlier than revenue figures will.

Where reconciliation silently fails

FailureHow it looksHow to catch it
Silent file truncationSettlement file arrives short; everything in it reconciles perfectlyReconcile the control totals in the file header, not just the rows
Timezone driftItems land in the wrong day; month-end is persistently wrong by a few itemsStore everything in UTC with an explicit value date; never infer
Float arithmeticTiny unexplained differences that accumulateInteger minor units everywhere. This is not negotiable.
Tolerance creepAuto-match rate quietly improving over quartersVersion tolerances; alert on changes
Suspense account growthUnapplied cash accumulating unnoticedAge and value the suspense balance in the same report as exceptions
Reprocessed filesA file re-ingested after a failure creates duplicatesIdempotency on file ingestion as well as on transactions
Fee schedule driftProcessor changes fees; your derived-fee matching degradesTrack fee variance as a monitored metric, not an exception category
Watch out

Control totals deserve particular attention. A settlement file that is missing its last 200 rows will reconcile with a perfect match rate on the rows present. Nothing looks wrong. The only defence is reconciling against the count and total the file itself declares.

The audit trail, and where this module ends

JSON — reconciliation run record
{
  "run_id": "rec_2026-05-23_upi_01",
  "rail": "upi",
  "period": {"from": "2026-05-22T00:00:00Z", "to": "2026-05-22T23:59:59Z"},
  "executed_at": "2026-05-23T04:15:00Z",

  "sources": [
    {"name": "internal_ledger", "records": 41288, "control_total_minor": 918442100},
    {"name": "psp_settlement_file", "file": "stl_20260522.csv",
     "records": 41290, "declared_records": 41290,
     "control_total_minor": 916118400, "checksum": "sha256:9a1c..."},
    {"name": "bank_statement", "records": 37, "control_total_minor": 916118400}
  ],
  "control_total_check": "passed",

  "configuration": {
    "cascade_version": "recon-cascade-v9",
    "tolerance_version": "tol-v4",
    "tolerances": {"amount_abs_minor": 100, "amount_pct": 0.002, "date_days": 2},
    "tolerance_approved_by": "finance_controller_2026-04-02"
  },

  "results": {
    "matched": 41205,
    "by_rule": {"exact_reference": 39880, "amount_date_exact": 1102,
                "amount_date_tolerance": 198, "one_to_many_subset": 25},
    "auto_match_rate": 0.998,
    "exceptions": 85,
    "exceptions_by_category": {"timing": 61, "fee_variance": 14,
                               "missing_in_ledger": 7, "ambiguous": 3},
    "exception_value_minor": 2323700
  },

  "aging": {"0_3d": 61, "4_7d": 17, "8_30d": 6, "over_30d": 1,
            "over_30d_owner": "ops_lead_04", "over_30d_plan_ref": "JIRA-8812"},

  "signoff": {"prepared_by": "system", "reviewed_by": "analyst_12",
              "approved_by": "finance_controller", "approved_at": "..."}
}

Three fields carry disproportionate weight. control_total_check, because it catches truncated files that otherwise reconcile perfectly. by_rule, because it distinguishes a genuinely clean reconciliation from one achieved through wide tolerances. And over_30d_owner, because an aged exception without a named owner is an aged exception nobody is working.

Where this module ends

  • Fraud detection is Module 03. A dispute may originate in fraud, but reconciliation is about where the money is, not who took it.
  • AML transaction monitoring is Module 04 and runs on the same transaction data for a different purpose. Share the pipeline, not the logic.
  • Customer communication during a dispute is Customer Operations, and what you may say is constrained by the tipping-off rules in Module 04.
  • Core banking and ledger infrastructure choices are Infrastructure.
  • Settlement obligations and TAT compliance sit in the India regulatory spine page.
Watch out

Illustrative throughout. A production ledger and reconciliation system handling customer money needs an independent financial controls review, and the ledger schema in particular should be reviewed by someone accountable for the accounting treatment before it holds real balances.

Sources

Every figure, rule and date on this page, and where to check it. Entries are typed so you can see which are primary-sourced and which are industry reporting.

  1. officialRBI (Regulation of Payment Aggregators) Directions, 2025 — PA-O / PA-P / PA-CB, escrow and settlement. www.rbi.org.in
  2. officialPayment and Settlement Systems Act, 2007 — the statutory basis for payment system regulation in India. www.rbi.org.in
  3. officialNPCI — UPI volumes, dispute handling and settlement mechanics. www.npci.org.in
  4. officialRB-IOS and the RBI CMS portal — the complaint route and award limits. cms.rbi.org.in

Tooling, pricing and cost sources for this module are on its build sheet: sources →

Checked September 2026. Regulation in this area is actively developing; the date is part of the claim.

Ask an AI about this page

Opens your assistant with this page as the source, and a question rather than a summary. It will ask what you are building before it answers.

Nothing is sent from here. The link carries only this page’s title and address.