Reconciliation · PG-25
Reconciliation by relationship: many sources, one verified number
Billing totals, terminal batches, channel payouts and the bank deposit tie into one ledger — with every match, exception and adjustment on one traversable evidence path.
- 2..N sides per run
- versioned match rules
- typed assertion edges
- review-gated write-back
- sealed evidence bundle
Reconciliation by relationship starts from one question: does every number in the close trace to the bank? A reconciliation run in Ledgerbook AI accepts two to many sides — billing totals, terminal batches, channel reports, PSP settlements, bank statements — matches them pairwise or against a declared control total, and records each hop's counts and deltas. Matches, settlements and adjustments are typed edges on one lineage graph — the **Financial Event Web**: the typed lineage graph plus the reconciliation assertion edges laid over it, in which billing records, terminal batches, channel statements and bank lines are nodes that must tally against the postings they claim to explain. It is a single traversable path from source to bank. Exceptions carry a reason code, an owner and aging; corrections are proposed as provisional entries that post only through review gates, never on their own. Runs, matches, the rule registry — deterministic, tolerance **and scored-rule** passes — N-sided control totals, assertion edges, review-gated write-back, a KPI subset (auto-match rate, exception rate, aged open value, drift status) and the sealed basic evidence bundle ship at PH-1. Fuzzy and model-based matching, which run above the core, and the full KPI/SLA projection surface are Roadmap (PH-2).
Verified 2026-09-19 · source: reconciliation engine reference FR-150–157, lineage FR-525–527
Reconciliation primitives
2..N
sides per run, including a declared control total · FR-152
8
controlled reason codes on every match decision — eight in the shipped vocabulary today, extensible as policy data · FR-151
≤ 4 hops
per scoped graph projection, stamped by its knowledge watermark · API-reconcile
0
findings that post without an approval event — proposals run the same gates as any entry · FR-154
N-sided control totals: billing, terminals, channels and bank — one verified number
A run accepts two to many sides. At least one side may be a declared control total — a source-system total, a PSP settlement batch total or a bank statement total — and the run matches pairwise, against an anchor side, or as a chain of custody across hops. Scope is always (scenario, book): no source system is re-exported to make a number fit.
PH-1
Strategies are explicit, per direction
1:1, 1:many and many:1 are selected per direction and recorded on the run — a single payment matching three ledger lines is a documented strategy, not an accident. Re-runs are idempotent on the rule-registry version, the window and the source hashes, so replaying a period cannot double-count it.
FR-150/152 · PH-1
PH-1
A missing side completes the run — never shortens it
When a declared side does not arrive, the run records MISSING_SIDE and completes explicitly. The
gap stays visible as an exception with an owner, instead of quietly reconciling a chain that was never
complete.
FR-152 · PH-1
Chain of custody — per-hop counts and deltas under tolerances
| Hop | Sides | Recorded as | A break lands as |
|---|---|---|---|
| 1 | Billing total → ledger receivables | trues_up_to against the declared control total |
Exception with residual, reason code and owner |
| 2 | Terminal batches → ledger sales | matches edges under the batch strategy |
DUPLICATE_SUSPECT / AMOUNT_MISMATCH |
| 3 | Channel report → PSP settlement | settles ⋈ withheld_from for the fees |
FEE_DIFF or TIMESTAMP_DRIFT |
| 4 | PSP settlement → payout → bank deposit | clears / deposits_as through clearing accounts |
In-transit aging past the policy window → finding |
Clearing and in-transit accounts carry an expected-zero or expected-balance assertion over a window; items stale past policy raise findings with an owner and escalation · FR-155 · PH-1
The assertion-edge graph — unresolved edges are the exceptions
Every match, exception and adjustment decision is itself a provenance event, so the whole run exports as OCEL 2.1 alongside the entries it touched. Every relationship a reconciliation establishes is a typed edge on the lineage graph. The registry is exactly twelve types: matches, partially_settles, settles, eliminates, trues_up_to, withheld_from, deposits_as, posted_as, aggregates, clears, responds_to and derives_from. Each edge carries its state, the
rule-registry version that produced it, the actor or tool, and an evidence path. Edges supersede; they never
mutate — and evidence that cannot be resolved fails closed.
| State | Means | Leaves the audit trail as |
|---|---|---|
proposed |
Suggested by a rule pass, or proposed by an agent over MCP | A reviewable proposal — never a posting |
confirmed |
Asserted by a human decision or an approved run decision | The traversable hop an auditor follows |
rejected |
Explicitly refused — the sides do not belong together | An unresolved edge set: the exception |
expired · superseded |
Timed out, or replaced by a newer assertion | A superseding chain — both versions stay visible |
Every adjustment links back
A proposed adjustment carries a responds_to edge resolving to exactly one exception case, so the
posting that settled a finding can always be found from the finding — and the finding from the posting.
FR-154, FR-527 · PH-1
Read the graph scoped and as-believed
The graph projection queries by scope, period, edge types within 4 hops and state, and every result is stamped by its knowledge watermark — the same as-of discipline as every other read. Interactive hot-graph projections stay on the roadmap.
FR-525–527 · PH-1 · hot projections FR-524 Roadmap
Write-back is a loop you review: finding → proposal → approval → posting
Reconciliation proposes; people approve. An exception becomes a proposed provisional entry — bound to the reason code and to the finding that produced it — and that proposal posts through the same lifecycle and materiality gates as any other entry.
-
Finding exception + conservation residual
An unresolved edge set with a reason code, an owner and an aging band. What does not tie — the residual — is part of the finding, not a footnote.
-
Proposal provisional entry · content-hash bound
A provisional entry from a reason-code template — unbooked fees, FX drift, chargeback provisions, missing channel sales, suspense clearing — carrying
derived_from→ the finding and the run. A proposal that would not balance is rejected, never guessed. -
Approval T0/T1 gates, evaluated server-side
Materiality tiers decide whether a human approves; the decision is bound to the proposal's content hash, so any edit voids it. Agents can propose; they cannot approve, finalize or correct.
-
Posting responds_to → exactly one exception
The posted adjustment resolves the finding through a typed edge. Finding, proposal, approval and posting are one queryable record — the whole chain, not four systems' logs.
# Turn a finding into a reviewable proposal — never a posting
→ POST /v1/reconcile/findings/{id}/adjustments
{
"reason_code": "FEE_DIFF",
"template": "unbooked_psp_fee",
"derived_from": ["finding_f_9a1", "run_r_204"]
}
← 202 { "proposal_ids": ["prov_7f3c"],
"evidence_hash": "sha256:41b0…",
# posts only after the T0/T1 gate and a hash-bound approval
"state": "proposed" }
Match rules and reason codes are registry data — configuration, not code
Matching runs as an explicit, versioned cascade: a deterministic pass, a tolerance pass, and a scored pass that arrives with PH-2. Rules are business-editable through the API — versioned, prioritized, never hidden in adapter code — and every run pins the registry version it used, so a re-run pinned to the same version reproduces identical decisions.
| Pass | What it matches | Typical knobs | Phase |
|---|---|---|---|
| a · deterministic | Exact amount, reference and counterparty keys | Key composition per rail | PH-1 |
| b · tolerance | Amounts and dates inside declared windows | ±abs / ±% per currency, date window per rail, FX tolerance | PH-1 |
| c · scored | Fuzzy descriptors, set and group rules | Score thresholds and priorities | Roadmap |
The controlled reason-code vocabulary
Every break carries one of these codes, and each code binds the adjustment templates that can answer it — so a finding cannot be closed with an unrelated posting.
AMOUNT_MISMATCH— the amounts differ beyond the applied tolerance.TIMESTAMP_DRIFT— the value date falls outside the rail's date window.MISSING_REFERENCE— no reference or counterparty key to bind on.DUPLICATE_SUSPECT— the same economic event may have arrived twice.FX_DIFF— the conversion leaves a difference beyond the FX tolerance.FEE_DIFF— a withheld fee differs from the expected schedule.UNMAPPED— no active mapping profile covers the row; it lands in suspense.MISSING_SIDE— a declared side never arrived; the run completes explicitly.
Reason codes feed materiality evaluation on every proposal · FR-151/154 · PH-1
Roadmap: what is not shipped yet Roadmap
Two pieces are already in PH-1, so this strip reads as the delta only: clearing and in-transit aging with auto-clear and escalation, and the basic sealed evidence bundle a completed run can produce. Everything below arrives with PH-2.
- Fuzzy descriptor matching and set/group scored passes, with score thresholds as registry data. Roadmap
- Model-based suggesters — scoring models stay outside the core, above the ledger. Roadmap
- Full KPI/SLA projections: auto-match rate, exception rate, open value aged 0–30 / 31–60 / 60+, days-to-reconcile and automation rate — replayable from the event log and driving alerts. Roadmap
- Hot graph projections for interactive traversal; cold per-export materialisation is the default. Roadmap
Traces: FR-151 scored pass, FR-156, FR-524 · PH-2 · PH-1 baseline: FR-155 clearing aging, FR-157 basic bundle
Frequently asked
What is N-sided reconciliation?
N-sided reconciliation ties more than two sources into one verified number: a run accepts two to many sides — billing totals, terminal batches, channel reports, PSP settlements and the bank — matches them pairwise or against a declared control total, and reports each hop's counts and deltas under tolerance. Unresolved matches become exceptions on the evidence graph, never silent adjustments. The hop model →
How do you reconcile sales from billing, terminals and channels against the bank?
The ledger matches the chain, not just the endpoints: channel batch to PSP settlement to payout to bank deposit, each hop scored against versioned match rules with reason codes for every break. Differences route to an exception queue with an owner and aging; each exception links to the evidence path that explains it, and any correction is proposed as a provisional adjustment for review.
Do reconciliation adjustments post themselves?
No. Reconciliation proposes; people approve. Ledgerbook turns each exception into a proposed provisional entry — bound to the reason code and the finding that produced it — and that proposal posts only through the same lifecycle and materiality gates as any other entry. An adjustment that would not balance is rejected, not guessed; nothing posts silently. The review-gated loop →
How do you prove a completed reconciliation to an auditor?
A completed run can produce a sealed evidence bundle: a manifest of file digests, a KPI snapshot and the rule versions that were pinned, signed, with an external chain-checkpoint reference and a seven-year retention floor — byte-reproducible and verifiable offline. Completeness and accuracy, the two assertions auditors test, are what the bundle carries. Basic sealed bundles ship at PH-1; fuller audit evidence packages are Roadmap (PH-2). How verification works →
What Ledgerbook is not
Three boundaries, and the first one is a design decision rather than a limitation.
- No model-based matching in the core (NG-4). The deterministic rule and tolerance engine is core; probabilistic and scored matching is application-layer. That split is deliberate — a match an auditor has to accept must be reproducible from recorded rules, not from a model version nobody kept.
- No exception-queue screens (NG-1). Runs, matches, exception cases, reason codes and reviewer actions are ours as objects and events. The queue your team works, and the routing around it, belong to the application on top.
- No money movement (NG-3). We tell you the bank and the books disagree, and by how much, and raise the adjustment as a proposal. Moving money to resolve it happens in your treasury or payment system, never here.
Put one verified number in the close
Tie billing, terminals, channels and bank into one ledger — and keep the evidence path to defend it. Proposals stay proposals until your policy approves them.