Skip to content
FlowScript templates

Finance — Payment Reconciliation Flow

A month-end payment reconciliation, walked from the gateway settlement file down to the lines posted to the ledger. Typed FlowScript for startup finance. Keywords: cap table, captable, equity, dilution, funding round.

Template previewFlowScript
Settlements Receivedn = 48620Source: Gateway settlement file, 2026-03Ingestedn = 48602Excluded (n = 18)• Malformed row: 11• Duplicate settlement id: 7Matched Exactn = 46318Excluded (n = 2284)• Amount mismatch: 1204• Missing ledger entry: 806• Currency rounding: 274Breaks Investigatedn = 2284Source: Reconciliation break queue, 2026-03Breaks Clearedn = 2091Excluded (n = 193)• Awaiting bank confirmation: 138• Escalated to dispute: 55Reconciledn = 48409Posted To Ledgern = 48387Excluded (n = 22)• Held for manual review: 15• Fx revaluation pending: 7

Make it your own.

// A month-end payment reconciliation, walked from the gateway
// settlement file down to the lines posted to the ledger. Both passes —
// the automatic match and the manual break queue — have to add up, and
// the reconciled total is derived from the two rather than asserted.

cohort settlements_received {
  n: 48620 source "Gateway settlement file, 2026-03"
}

cohort ingested {
  n: 48602
  from: settlements_received
  excluded: {
    malformed_row: 11
    duplicate_settlement_id: 7
  }
}

// Pass one: everything the matcher could clear without a human.
cohort matched_exact {
  n: 46318
  from: ingested
  excluded: {
    amount_mismatch: 1204
    missing_ledger_entry: 806
    currency_rounding: 274
  }
}

// Pass two: the 2 284 breaks pass one could not clear.
cohort breaks_investigated {
  n: 2284 source "Reconciliation break queue, 2026-03"
}

cohort breaks_cleared {
  n: 2091
  from: breaks_investigated
  excluded: {
    awaiting_bank_confirmation: 138
    escalated_to_dispute: 55
  }
}

// Two passes merge, so the total is derived: 46 318 matched plus 2 091
// cleared by hand.
cohort reconciled {
  n: = matched_exact.n + breaks_cleared.n
}

cohort posted_to_ledger {
  n: 48387
  from: reconciled
  excluded: {
    held_for_manual_review: 15
    fx_revaluation_pending: 7
  }
}

kpi auto_match_rate {
  title: "Lines matched without a human touching them"
  baseline: = pct(matched_exact.n, ingested.n)
  target: 98
  unit: "%"
}

kpi break_ageing {
  title: "Median age of an open break"
  baseline: 6
  target: 2
  unit: "days"
}

kpi close_day {
  title: "Business day the ledger closes"
  baseline: 7
  target: 3
  unit: "days"
}

// A funnel is a strictly narrowing cascade: every stage is a subset of
// the one above it, and the renderer sizes each band as n / base with no
// clamp. The automatic match is NOT a stage of that cascade — it is one
// of two streams, and the 2 091 breaks cleared by hand rejoin below it.
// This chain used to read received -> clean -> matched (46 318) ->
// posted (48 387), which drew a final band WIDER than the one above it
// and printed "104.5% step conv" in the success colour. Nothing caught
// it: funnel_step chains carry no arithmetic check, so unlike the cohort
// cascade above, a wrong n here is silent.
//
// So the stages are the lines still in the run, each one derived from
// the cohort it restates rather than retyped beside it. Retyping is what
// let the two halves of this document disagree in the first place.
funnel_step received   { n: = settlements_received.n, title: "Settlement lines received" }
funnel_step clean      { n: = ingested.n, from: received, title: "Ingested" }
funnel_step reconciled_lines { n: = reconciled.n, from: clean, title: "Reconciled — matcher plus cleared breaks" }
funnel_step posted     { n: = posted_to_ledger.n, from: reconciled_lines, title: "Posted to ledger" }

note funnel_is_not_the_cascade {
  text: "The funnel view shows the lines that stayed in the run: received, ingested, reconciled, posted. It cannot show the automatic match as a stage, because 2 091 lines the matcher could not clear were cleared by hand and rejoin the flow below it — a funnel band drawn for the 46 318 auto-matched lines would be followed by a wider one and a conversion over 100%. The auto-match share is a ratio, not a stage, and is reported as the auto_match_rate KPI."
  anchor: reconciled
}

view reconciliation: consort(settlements_received)
view breaks: funnel(received)