# Financial reconciliation: when matching amounts are not enough

A colleague asks, “The money is back. Can I finish this case?” The case expects temporary credit, but a credit record says final. Both say USD 125.00. Before you build an agent, learn to explain that difference.

Reconciliation means comparing related records, explaining differences and getting those differences resolved by the responsible owners. This original workshop gives you a visual explanation, a runnable comparison and a review exercise. Start with [Financial-services business 101](/training/fde-101?topic=finance-business#concept) if the terms are new.

Allow 50–70 minutes. You need Python 3.11 or newer and a terminal. Every record is invented; there are no accounts, keys, packages, network calls or financial writes. The advanced [Academy reconciliation lesson](/app/academy?course=financial-services-operations&lesson=reconciliation&view=focus) adds architecture decisions, worked examples and knowledge checks under the existing training access controls.

## 1. Understand the two stories

Think about comparing an order and a shipment. Equal quantities do not establish the same order, and a shipping label does not prove delivery. In this exercise, the case tracks an investigation and an expected credit. The ledger extract describes the credit entries actually observed. You need both, with their meanings intact.

| Term | What it means here | What it does not prove |
| --- | --- | --- |
| Case | The investigation and expected action | That money was posted |
| Original transaction | The debit being disputed | The identity of a later credit |
| Ledger entry | One identified accounting record | The final investigation outcome |
| Posting state | Whether this credit is pending, posted or reversed | That all case work is complete |
| Provisional credit | Temporary credit during the applicable investigation process | A final case decision |
| Reconciliation exception | A specific unresolved difference or missing record | Permission to edit either source |
| Duplicate delivery | An identical repeat of the same stable entry ID | That two different entries are the same financial effect |

The lab's case expects one positive USD credit. Its extract contains only credit records for that case, not a complete general ledger. This is a deliberately small business contract, not a universal bank matching algorithm.

## 2. Follow one disagreement

Read these two synthetic records before stepping through the diagram:

| Field | Case expectation | Observed credit |
| --- | --- | --- |
| Case | SYNTHETIC-CASE-104 | SYNTHETIC-CASE-104 |
| Account | SYNTHETIC-ACCOUNT-7 | SYNTHETIC-ACCOUNT-7 |
| Original transaction | SYNTHETIC-DEBIT-81 | SYNTHETIC-DEBIT-81 |
| Amount and currency | 12500 minor units, USD | 12500 minor units, USD |
| Credit kind | provisional | final |
| Separate state | investigation open | credit posted |

The amount is USD 125.00, represented by an integer to avoid binary floating-point equality surprises. Other currencies may use different scales. Keep currency explicit; this lab never converts or nets amounts.

The references and money agree. The credit kind does not. A useful handoff names that disagreement and asks the case and ledger owners to resolve it. It does not rewrite final as provisional to make the comparison pass.

## 3. Run the reconciliation lab

In the online workbook, explore eight recorded cases below. Compare the case expectation with each delivered row, predict the next step and inspect the exact rule trace. The credit-kind comparison uses unchanged source records to expose a false match. These are saved Python observations, not code running in your browser.

Download **fde-financial-reconciliation.py** from the lab panel into a local folder. Predict the four highlighted distinctions: wrong credit kind, repeated delivery, two distinct entries and incomplete extract. Then run:

```bash
python3 fde-financial-reconciliation.py
python3 fde-financial-reconciliation.py --json > reconciliation-guarded.json
python3 fde-financial-reconciliation.py --omit-credit-kind --json > reconciliation-broken.json
```

The first command prints a compact report. The JSON preserves each case's full input and result. Inspect `state`, `reason`, `matched_entry_ids`, `duplicate_deliveries`, `owner`, `next_action` and `trace` together. The records are read-only; running the lab changes no case or ledger.

| Scenario | Guarded reason | What you should explain |
| --- | --- | --- |
| `matched-provisional` | `matched_snapshot` | Matching temporary credit leaves the investigation open |
| `wrong-transaction` | `reference_mismatch` | An equal amount can belong to another transaction |
| `amount-mismatch` | `amount_mismatch` | A one-cent difference stays visible |
| `currency-mismatch` | `currency_mismatch` | EUR and USD are not interchangeable |
| `pending-credit` | `credit_not_posted` | Pending is not posted |
| `wrong-credit-kind` | `credit_kind_mismatch` | Provisional and final carry different meanings |
| `missing-credit` | `missing_credit` | The complete scoped extract has no expected entry |
| `repeated-delivery` | `matched_snapshot` | One entry ID, identical content, one repeated delivery |
| `conflicting-entry` | `conflicting_entry` | One ID carries different payloads; no last-arrival guess |
| `two-credit-entries` | `multiple_credit_entries` | Two different IDs need an explanation, not deduplication by amount |
| `partial-extract` | `incomplete_extract` | Missing coverage means unknown, not absent |
| `reversed-credit` | `unsupported_posting_state` | A reversed record cannot support a posted-credit statement |
| `matched-final` | `matched_snapshot` | Matching final evidence still does not certify notices or close a case |

In the deliberately broken report, only **wrong-credit-kind** changes its decision: the code falsely calls the snapshot matched. Compare `expected_kind` and `kind` in its inputs. Both processes can exit successfully; only the guarded comparison preserves this business rule. The other twelve scenarios retain their guarded business decisions; the trace records the omitted check wherever it was reached.

## 4. Change the code and prove the boundary

In a copy of the script, add these scenarios to `scenarios()`. Predict the result before running:

```python
("wrong-account", case,
 replace(extract, entries=(replace(entry, account_id="SYNTHETIC-ACCOUNT-OTHER"),))),
("debit-direction", case,
 replace(extract, entries=(replace(entry, direction="debit"),))),
("unknown-posting", case,
 replace(extract, entries=(replace(entry, status="unrecognized"),))),
```

Expect `reference_mismatch`, `invalid_credit` and `unsupported_posting_state`. Each should leave `matched_entry_ids` empty. Keep the original input, your prediction, actual trace and next owner. Repeat the run and explain why the same snapshot should produce the same result.

Then challenge the contract: what if a valid credit is split into two entries? The current one-credit rule deliberately requests reconciliation. Do not weaken it to “sum everything.” First agree which entries belong together, how reversals and versions work, and what the business considers a complete match. Write a counterexample that would fool a simple sum before implementing your extension.

## 5. Design the adapter an FDE would have to ship

The `complete` flag and record fields are trusted fixtures. In a deployed system, a server-side adapter must establish the permitted workspace and account, source identity, pagination coverage, version and freshness. A model or caller cannot grant itself that authority. HTTP 200 for one page is not proof that you saw every relevant record.

The duplicate rule assumes an immutable snapshot. If the real source revises entries under the same ID, represent and order those revisions explicitly; taking the latest network arrival is not a version strategy. Source changes between reads also need an agreed snapshot or reconciliation policy. This local script does not prove concurrent adapters, production authorization or eventual consistency.

An agent can help explain a verified difference or draft a handoff. Keep record comparison deterministic, preserve source evidence and give unresolved work an accountable owner. Choosing the reconciliation interval, case closure rules and customer communication process belongs with the institution's authorized owners.

## 6. Rehearse the receiving-owner review

Use a peer, facilitator or recorded self-rehearsal. A worksheet does not book a mentor. Allow 15 minutes:

1. Explain case, transaction, entry and provisional credit without field names.
2. Show the wrong-credit-kind inputs and reproduce the deliberately broken match.
3. Compare repeated-delivery with two-credit-entries. Explain why you keep both distinct IDs.
4. Have the reviewer change the account or posting state and predict the result.
5. Hand over one exception and identify the adapter evidence still needed.

Use this sample as a structure, then write your own from an unseen case:

```text
Case: SYNTHETIC-CASE-104
Source: SYNTHETIC-EXTRACT-4, complete scoped teaching fixture
Expected: posted provisional credit, USD 125.00
Observed: posted final credit, USD 125.00, SYNTHETIC-CREDIT-9
Difference: credit_kind_mismatch
Owner: case and ledger operations
Next action: resolve the credit-kind mapping against authoritative records
Remaining case work: investigation open; communications checked separately
```

Keep the dictionary, diagram explanation, guarded and broken reports, changed code, unseen results and review notes. A receiving engineer should be able to reproduce the exception without your narration.

## 7. Sources and next practice

Sources checked 22 October 2026. The comparison rules, records and review exercise are original teaching material.

- [CFPB Regulation E, error-resolution procedures](https://www.consumerfinance.gov/rules-policy/regulations/1005/11/) distinguishes provisional credit, investigation and final resolution for its scope. This lab does not implement legal deadlines or determine applicability to another financial product.
- [Python decimal documentation](https://docs.python.org/3/library/decimal.html) explains exact decimal arithmetic and why binary floating-point equality matters in accounting. Our bounded USD exercise uses integer minor units.

Continue with the [paginated-extract workshop](/training/guides/financial-extract) to build the complete scoped read this comparison requires, or open the protected [financial reconciliation lesson](/app/academy?course=financial-services-operations&lesson=reconciliation&view=focus) for architecture challenges and knowledge checks.
