Skip to main content

Loan lifecycle

A loan moves through a small set of states, and everything that happens to it is recorded as append-only, value-dated facts rather than mutable balances.

States

Draft ──activate──▶ Active ──┬─ natural run-off ─▶ PaidUp
├─ pay up (early) ─▶ Settled
├─ default ─▶ Defaulted
└─ write off ─▶ WrittenOff
  • Draft — priced and persisted, but no money has moved.
  • Active — funded and open for servicing; the schedule's due dates drive collection.
  • PaidUp / Settled / Defaulted / WrittenOff — terminal outcomes.

Calculations are versioned

A loan's terms live in a calculation — the priced schedule, rate, APR, and totals. Re-terms and prepayments don't mutate the loan; they append a new calculation and bump the loan's sequence. The current calculation is the one whose sequence matches the loan — there's no stored "current" pointer, it's derived. Superseded schedules stay on record (the console's History tab).

The value-dated ledger

Money is tracked as a signed, append-only sub-ledger: disbursement adds principal, repayments reduce it, and the outstanding balance is the running sum of the deltas — derived, never stored. Because entries are append-only and value-dated, the balance is reproducible as of any date, and a late payment that arrives after a re-term still lands correctly on the current schedule.

Servicing runs in the background

The admin host runs scheduled sweeps (in the worker, once per cluster): charging due installments, aging into delinquency buckets, applying late fees, computing IFRS-9 provisions, posting the sub-ledger to the general ledger, and dispatching disbursements. The partner host runs none of these — it only serves requests against the same database.