Money Movement
Orchestrating funds across rails, banks, and internal accounts as a reliable, reconciled flow.
Learning outcomes
Almost nothing in fintech is a single clean transfer. When a customer taps “send,” what looks like one motion is in fact a choreography across several systems that do not trust each other, run on different clocks, settle at different times, and fail in different ways. The bank rail moves value on its schedule. Your ledger records intent the instant the customer acts. A correspondent bank holds the actual dollars. A settlement file shows up the next morning to tell you what really happened. Money movement is the discipline of making all of that behave as one coherent, trustworthy flow, where every dollar is accounted for at every instant even though it is constantly in motion and constantly out of your direct control.
Get this right and a payments platform feels boring in the best way: money goes where it should, the numbers always reconcile, and a stuck transfer is a known state with a known recovery, not a frantic 2 a.m. investigation. Get it wrong and you ship the worst bugs in software, the ones that move real money the wrong way and cannot be quietly rolled back.
After studying this page, you can:
- Explain why moving money is an orchestration problem across multiple systems, not a single database write, and name the systems that must agree.
- Trace a payment through its full lifecycle (initiation, authorization, clearing, settlement, posting) and say which system owns each stage and when it is safe to tell the customer the money has moved.
- Describe what a for-benefit-of (FBO) account is, why fintechs pool customer funds in one bank account, and how a sub-ledger keeps each customer’s share straight.
- Ledger money that is in flight using in-transit and clearing accounts, so the books balance at every instant a payment is partway between two rails.
- Apply funds-availability rules, including the shape of Regulation CC, to decide when a customer can actually spend a deposit versus when you have merely recorded it.
- Achieve exactly-once movement over rails that themselves guarantee only at-most-once or at-least-once, using idempotency keys, reconciliation, and a state machine.
- Design a money-movement orchestrator as an explicit state machine with timeouts, retries, and compensations, and name the failure modes (stuck funds, lost funds, double movement, partial failure) and the control that catches each.
Before we dive in
You do not need prior payments experience. We will define each term the first time it appears and build up from there.
A rail is a system that actually moves money between banks: in the United States, ACH (the Automated Clearing House, a batch system run for low-cost bulk transfers), wire transfers (Fedwire and CHIPS, for large, fast, final payments), card networks (Visa, Mastercard), and the newer instant rails (RTP from The Clearing House, and FedNow from the Federal Reserve). A bank partner (or sponsor bank) is the chartered bank that holds the actual money and connects a fintech to these rails, because a fintech is usually not itself a bank. A ledger is the fintech’s own double-entry record of who owns what; it is software, not money. Settlement is the moment value irrevocably changes hands between banks. Clearing is the exchange of the instructions and obligations that precede settlement.
Two roles will recur. The customer is the person or business whose money is moving. The fintech (or program) is the company orchestrating the movement on a bank partner’s rails. Behind them sit the bank partner, the rail operator (the Fed, The Clearing House, a card network), and often a correspondent or receiving bank on the other side. Each of these is a separate system with its own state, and the whole art of money movement is keeping their views consistent.
One convention: amounts are shown in dollars for readability, but as the ledger page in this track insists, a real system stores money as an integer count of minor units (cents), never a float. Hold that in the background. Our focus here is the flow between systems, not the arithmetic inside one ledger.
Mental Model
The wrong model, and the one almost every engineer starts with, is that moving money is like updating a balance: you subtract from sender, add to receiver, commit the transaction, done. It feels like a transfer between two rows in a database, atomic and instantaneous, the way an in-memory swap would be.
It is nothing like that. The sender’s bank and the receiver’s bank are different institutions with different databases that never share a transaction. There is no global commit. The money does not move when you decide it should; it moves when a rail, on its own batch schedule, settles between banks, which can be seconds later (instant rails), hours later (a wire), or one to several business days later (ACH). In between, the money is neither fully here nor fully there. It is in flight, and a real obligation exists that has not yet been discharged.
Here is the model to hold instead. Picture money movement as shipping a physical package between two warehouses through a courier you do not own. The moment the customer clicks send, you have not moved anything; you have created an instruction and handed it to a courier. The package is now in transit: it has left the sender’s shelf but has not arrived on the receiver’s shelf, and your books must show it sitting on the loading dock (an in-transit account) so that nothing is lost. The courier runs on its own schedule and occasionally loses a package, delivers it twice, or returns it days later. Your job is not to move the package yourself; it is to track exactly where every package is at every moment, to know what to do when the courier misbehaves, and to only tell the customer “delivered” when you have proof it truly arrived. Every design choice below, the in-transit accounts, the idempotency keys, the state machine, the reconciliation, falls out of taking that in-flight package seriously instead of pretending the transfer was instant.
Breaking it down
The core teaching runs in eleven steps. The first five establish what money movement is and where the money physically and logically lives. The next four build the engineering that makes it reliable over rails you do not control. The last two cover how it breaks and how it grows from a single program to a large institution.
1. Why money movement is orchestration not a transfer
Start from the constraint that makes everything else necessary: there is no single database that holds all the money, and no single transaction that can move it. When a customer at a neobank sends fifty dollars to a friend at a different bank, at least five independent systems are involved, and each has its own copy of the truth that updates at its own time.
The fintech’s ledger records the customer’s intent immediately. The fintech’s bank partner holds the actual dollars in a real account and submits to a rail. The rail (say ACH) collects the instruction into a batch, exchanges files with the receiving bank, and settles the net obligation between the two banks at a scheduled time. The receiving bank credits its own customer. None of these share a transaction; each acts on the others’ messages and then updates independently. The movement is real only when every link in that chain has done its part, and the links complete at different times.
This is why money movement is an orchestration problem, not a transfer. An orchestrator is a coordinator that drives a long-running process across systems that cannot be committed together, advancing each step, waiting for confirmation, handling the cases where a step times out or fails, and keeping a coherent record of where the whole thing stands. The orchestrator is the one component that holds the truth of “this payment, as a whole, is currently here,” precisely because no single underlying system holds it.
flowchart LR C["Customer<br/>app"] --> F["Fintech<br/>orchestrator + ledger"] F --> B["Bank partner<br/>(holds the dollars)"] B --> R["Rail operator<br/>(ACH, wire, RTP)"] R --> RB["Receiving bank"] RB --> RC["Receiving<br/>customer"]
The deep point is that each arrow in that diagram is a message between systems that do not share state, not a function call inside one program. Treating it like one program, expecting an instant, atomic, reversible move, is the original sin of payments engineering. Treating it like coordinating independent actors, where you confirm each handoff and plan for each to fail, is the discipline that makes a platform trustworthy.
2. The five stages every movement passes through
Almost every money movement, regardless of rail, passes through the same five logical stages. The names and timing differ by rail, but the shape is universal, and knowing it tells you exactly when it is safe to make a promise to a customer.
Initiation is the customer (or a system on their behalf) expressing intent: send fifty dollars to this account. Nothing has moved; an instruction exists. Authorization is the check that the movement is allowed: the funds are available, the account is in good standing, the transaction passes risk and compliance screening, and (for cards) the issuer approves. Clearing is the exchange of the instructions and obligations between the banks, so each side knows what it owes the other. Settlement is the irrevocable exchange of actual value between the banks, usually across accounts at the central bank. Posting is each institution recording the result on its own books and reflecting it in the customer’s available balance.
The single most important operational fact is that these stages are separated in time, and the gap between authorization and settlement is where all the hard problems live. On a card, authorization happens in under a second but settlement lands a day or two later. On ACH, you submit today and settlement is typically one business day later for standard entries (Same Day ACH settles the same business day across defined windows). On a wire over Fedwire, authorization and settlement are effectively the same moment and final. The customer experience and the money reality diverge during that gap, and your ledger has to hold both truths at once.
The reason to internalize these five stages is that they tell you what you are allowed to say and do at each point. After authorization you may have committed to the customer, but the money has not moved and the transfer can still fail in clearing or be returned after settlement. The customer-facing promise (the spend is done) and the money reality (settlement is final) are different events, and conflating them is how platforms either over-promise and then claw back, or under-promise and frustrate customers.
3. Where the money actually sits FBO and pooled accounts
A crucial and often invisible fact: most fintechs are not banks and do not hold their customers’ money in individual bank accounts. Instead, the bank partner holds one (or a few) large bank accounts, and all the customers’ money is pooled inside. This structure is called a for-benefit-of account, written FBO, because the account is held by the fintech (or the bank) for the benefit of the underlying customers, not for the fintech’s own use.
Concretely, imagine a neobank with one hundred thousand customers holding ten million dollars in total. At the bank partner, there is not one hundred thousand separate accounts. There is one FBO account, titled something like “Fintech Inc. FBO its customers,” holding ten million dollars. The bank sees one account with one balance. The breakdown of which customer owns how much lives entirely in the fintech’s own sub-ledger: a double-entry ledger where each customer has an account, and the sum of all customer balances must equal the balance in the FBO account at the bank. The FBO account is the real money; the sub-ledger is the map of who owns each piece of it.
flowchart TB
subgraph bank["Bank partner (the real dollars)"]
FBO["One FBO account<br/>balance: $10,000,000"]
end
subgraph fintech["Fintech sub-ledger (who owns what)"]
A["Customer A: $1,200"]
B["Customer B: $3,500"]
D["...100k customers"]
E["Sum of all = $10,000,000"]
end
FBO -. must always equal .- EThis structure exists for hard practical reasons. Opening, maintaining, and KYC-ing a hundred thousand real bank accounts is operationally heavy and slow; pooling lets the fintech onboard a customer in minutes while the bank manages one account. It also lets the fintech move money internally between customers instantly (a transfer from A to B is just two postings in the sub-ledger; no rail is touched and the FBO total does not change) while only touching a rail when money actually enters or leaves the pool.
The structure also creates the defining obligation of the model: the sub-ledger must always reconcile to the FBO balance. If the fintech’s books say customers own $10,000,000 but the FBO account holds $9,999,000, a thousand dollars of customer money is unaccounted for, which is both an operational failure and, because these are customer funds the fintech holds in trust, a serious regulatory and legal problem. The discipline of keeping the sub-ledger equal to the pooled account, continuously and provably, is the heart of operating an FBO program. For pass-through deposit insurance to reach the individual customers, the bank and fintech must also maintain accurate records of each customer’s share, which makes the sub-ledger not just an engineering convenience but a legal artifact.
The FBO model is the single most important structural fact about how modern fintechs move money, and it reframes most of what follows. An internal transfer is cheap because it never leaves the pool. An external payment is expensive and slow because it must cross the pool boundary onto a rail. And the entire program’s integrity rests on one invariant: the map of who owns what must always sum to the money actually in the account.
4. Ledgering money while it is in flight
Now the central engineering problem. A movement is not instant, so at some moments the money has left one place but not yet arrived at another. If your double-entry ledger must always balance (and it must, as the ledger page in this track establishes), how do you record a payment that is halfway through a rail? The answer is the most important accounting pattern in money movement: in-transit and clearing accounts.
An in-transit account (also called a clearing or suspense account) is an internal ledger account that holds the value of money that has left its source but not yet reached its destination. It is a temporary parking spot on the books. When a customer sends fifty dollars externally, you do not credit the destination directly, because the money is not there yet and may never arrive. Instead, you debit the customer’s account (their balance goes down immediately, fifty dollars) and credit an in-transit account (the system now holds fifty dollars in flight). The books balance: fifty dollars left the customer and is sitting in transit. When the rail confirms settlement, you move the fifty dollars out of in-transit to its final destination. If the payment fails or is returned, you reverse it: move the fifty back out of in-transit and return it to the customer.
stateDiagram-v2
[*] --> CustomerBalance: customer holds $50
CustomerBalance --> InTransit: payment initiated, debit customer, credit in-transit
InTransit --> Settled: rail confirms, credit destination, debit in-transit
InTransit --> Returned: rail returns, credit customer, debit in-transit
Settled --> [*]
Returned --> [*]
note right of InTransit
The in-transit account holds the value
while the money is on the rail. The books
balance at every instant; nothing is lost.
end noteThe in-transit account is what makes the books honest during the gap. At every instant, the money is somewhere on the ledger: in the customer’s account, in transit, or at the destination, and the total never changes. There is never a moment where fifty dollars has vanished from the customer’s account with no corresponding entry elsewhere. This is the ledger equivalent of the package sitting on the loading dock in the mental model: visible, accounted for, owned by no end party yet, but not lost.
The animation shows the full structure of an in-flight external payment at once: the customer’s balance, the in-transit account that holds the value on the rail, the two possible outcomes (settled or returned), and how the money flows across that fixed map as the rail does or does not confirm. Watch that the books are balanced in every frame, even while the money is in flight.
A few practical refinements. Sophisticated systems use separate in-transit accounts per rail and per direction, so that the balance in “ACH outbound in-transit” is, at any moment, exactly the total of all ACH payments currently on the wire. That balance becomes a powerful control: at the end of the day it should equal the sum of payments you submitted but have not yet seen settle, and any drift signals a stuck or lost payment. In-transit accounts also let you handle the awkward truth that some rails are asynchronous and reversible: ACH debits can be returned days later for insufficient funds, so money that “settled” can come back, and the in-transit and reversal machinery is what keeps that honest rather than a surprise.
5. Holds availability and Regulation CC
There is a critical distinction the in-transit pattern forces you to confront on the receiving side: the difference between money you have recorded and money the customer can actually spend. When a deposit arrives, you may know about it before it is truly final, and if you let the customer spend it and the deposit is later reversed, you have lost real money. This is the purpose of holds and availability.
A hold is a restriction that records a balance but does not make it spendable yet. Availability is the rule for when held funds become spendable. The tension is economic and direct: release funds too early and you absorb the loss if the deposit bounces or is fraudulent; release too late and you frustrate the customer and may break the law, because availability is regulated.
In the United States, the relevant regulation is Regulation CC (which implements the Expedited Funds Availability Act), and it sets maximum hold times for deposits into consumer transaction accounts. The first $225 of a day’s deposits must generally be made available by the next business day (a figure raised from $200 by an inflation adjustment), most local check deposits must be available within a small number of business days, and there are specific, longer maximums for exception cases such as new accounts, large deposits, and accounts with repeated overdrafts. Critically, Regulation CC sets the maximum you may hold; you are always free to make funds available faster, and many fintechs do to compete, accepting the fraud risk as a cost of a better experience.
The reason this sits in a money-movement page rather than a compliance footnote is that availability is where the rail’s settlement timing collides with the customer experience and the law. An ACH credit may post to your view before it is truly irrevocable; a check can be returned days after the customer sees the balance. The hold is your defense, and Regulation CC is the legal envelope around how long that defense may last. A correct design records the deposit immediately (so the books are complete) but governs spendability with an explicit hold and availability schedule, so the customer’s spendable balance and the customer’s recorded balance are tracked as two different numbers. Confusing the two, treating recorded money as spendable money, is a direct path to losses on reversed deposits.
6. Idempotency and exactly-once across unreliable rails
Here is the problem that makes money movement genuinely hard as distributed systems go. You want exactly-once movement: the customer’s fifty dollars moves once, never zero times (lost) and never twice (double movement). But the rails and networks you build on guarantee no such thing. A network call can time out with you never learning whether it succeeded. A rail might accept a submission, and your acknowledgement might be lost, so you do not know if you submitted once or not at all. The fundamental result from distributed systems is sobering: over an unreliable channel, you cannot have true exactly-once delivery. You can only have at-most-once (never duplicate, but might lose) or at-least-once (never lose, but might duplicate).
So how does anyone move money exactly once? You do not get it from the channel. You construct it, by combining at-least-once delivery (keep retrying until you get a confirmation, so you never lose the payment) with idempotency (make a repeated submission have no additional effect, so retries never duplicate the movement). At-least-once guarantees the money is not lost; idempotency guarantees it is not doubled; together they yield effectively-once.
An idempotency key is a unique identifier the initiator attaches to a request so the
receiver can recognize a repeat. The first time the orchestrator submits payment
pay-abc-123, the system performs the movement and records the key with its result. If
a timeout makes the orchestrator retry with the same key, the system sees it has already
processed pay-abc-123 and returns the original result without moving money again. The
retry collapses onto the first attempt. Every responsible payments API requires
idempotency keys for exactly this reason, and the orchestrator must generate the key
once per logical payment and reuse it across every retry of that payment, never minting
a fresh key on retry (which would defeat the entire mechanism).
The subtlety to carry away is that idempotency is not a property the rail gives you; it is a property you build, end to end, by choosing a stable key per logical payment and making every layer (your API, your orchestrator, ideally the rail’s API) honor it. Where a rail does not natively support idempotency, you compensate with the next section’s tools, the state machine and reconciliation, to detect and resolve duplicates after the fact. Exactly-once money movement is not a guarantee you receive; it is an invariant you maintain.
7. The orchestration layer as a state machine
If a payment is a long-running process across systems that fail and retry, then the right way to model it is as an explicit state machine: a payment is always in exactly one well-defined state, and only specific transitions are allowed, each triggered by an event (a customer action, a rail confirmation, a timeout). This is the backbone of every serious money-movement platform, and it is worth building deliberately rather than letting the states emerge implicitly from scattered boolean flags.
The states model the lifecycle from section two with the failure paths made explicit. A
payment is Initiated, then Authorized (or Declined), then Submitted to the rail,
then Settled (or Returned, or Failed). The transitions are the only legal ways to
move between them, and the orchestrator’s whole job is to drive each payment along this
graph, reacting to events and never skipping or reversing a step illegally.
stateDiagram-v2
[*] --> Initiated: customer submits
Initiated --> Authorized: checks pass
Initiated --> Declined: checks fail
Authorized --> Submitted: sent to rail
Submitted --> Settled: rail confirms settlement
Submitted --> Returned: rail returns the entry
Submitted --> Failed: rail rejects or times out
Failed --> Submitted: retry with same idempotency key
Settled --> [*]
Returned --> [*]
Declined --> [*]
note right of Submitted
Submitted is the dangerous waiting state.
A timeout here means UNKNOWN, not failed:
the rail may have accepted it. Resolve via
reconciliation, never by blind resubmission.
end noteThree design principles make this state machine trustworthy. First, persist the state
and every transition durably before acting, so a crash mid-flight resumes from a known
state rather than a guess; the orchestrator is itself an event-sourced ledger of payment
states. Second, every wait has a timeout, because rails do not always answer; a
payment cannot sit in Submitted forever, so after a bounded time it transitions to a
state that triggers investigation rather than silent limbo. Third, and most important,
a timeout is not a failure; it is an unknown. If you submit to a rail and time out,
the rail may have accepted the payment. Treating the timeout as a failure and resubmitting
blindly is how you double-move money. The correct response is to move to a state that
says “we do not know,” and resolve the unknown by querying the rail or matching against
the settlement file, not by guessing.
The reason to make the state machine explicit and durable is that it converts chaos into
a finite set of known situations. A stuck payment is not a mystery; it is a payment that
has been in Submitted past its timeout, and there is a defined procedure to resolve it.
A payment cannot be in two states at once, cannot skip authorization, and cannot settle
without having been submitted. The state machine is the single place that holds the
coherent truth of where each payment stands, which is exactly the coordination job no
single underlying rail can do.
8. Reconciliation against rail settlement files
The orchestrator’s view of a payment is a belief. The rail’s settlement file is the truth. Reconciliation is the control that compares the two and resolves every disagreement, and in money movement it is not optional housekeeping; it is the mechanism that turns “we think it settled” into “we have proof it settled,” and the only thing that catches money that was moved, lost, or duplicated outside your system’s knowledge.
Most rails deliver a settlement file (or report) on a schedule: ACH operators and your bank partner provide files listing exactly which entries settled, which returned, and the net amount moved; card networks deliver settlement and reconciliation files daily. Reconciliation is the process of matching every line in that file against a payment in your state machine, and investigating every line that does not match.
sequenceDiagram participant O as Orchestrator state participant F as Rail settlement file participant R as Reconciliation O->>R: my view: payment P is Submitted ($50) F->>R: file says: P settled ($50) R->>O: match: advance P to Settled Note over R: Mismatches drive investigation F->>R: file says: payment Q settled ($50) R->>O: no Q in my states: money moved we did not initiate O->>R: payment S is Submitted ($50) F->>R: S not in file R->>O: we think it moved but the rail has no record: stuck or lost
There are exactly three kinds of disagreement, and each maps to a real failure. A
payment is in your state machine and in the file with matching amounts: a clean match,
advance it. A payment is in the file but not in your states (or with a different amount):
money moved that you did not initiate, or for the wrong amount, which means a duplicate,
an unauthorized movement, or a bug, and it demands immediate investigation. A payment is
in your states (as Submitted) but absent from the file: you believe money moved but the
rail has no record of it, which is a stuck or lost payment, the timeout’s unknown made
concrete. The first case is the happy path; the other two are the entire reason
reconciliation exists.
The economic and regulatory stakes are why reconciliation is run continuously and treated as a first-class control rather than a monthly chore. For an FBO program, daily reconciliation between the sub-ledger, the bank’s view of the FBO account, and the rail settlement files is what proves customer funds are intact, and a break, where the three do not agree, is investigated and resolved before it can compound. Reconciliation is the discipline that makes the difference between a platform that knows where every dollar is and one that finds out it lost money when a customer complains.
9. Multi-step and conditional transfers
Real products rarely move money in one hop. A marketplace payout might pull from a buyer, hold in escrow, take a platform fee, and pay a seller, possibly across different rails and over several days. A currency conversion moves dollars out and euros in through an intermediary. These are multi-step transfers: a single logical movement composed of several underlying movements that must either all complete or be cleanly unwound, even though they cannot be committed together.
This is the same problem distributed systems call a saga: a sequence of local steps, each with a compensating action that undoes it, so that if a later step fails, you run the compensations for the steps already done and leave the system consistent. You cannot hold a database lock across a multi-day rail, so you cannot use a single atomic transaction. Instead you make each step individually durable and reversible, and you define precisely how to back out of a partially completed sequence.
Conditional transfers add a guard: a movement that happens only if a condition holds (release escrow only when goods are confirmed delivered; sweep a balance only above a threshold; execute a payout only after a verification clears). The orchestrator models the condition as a state the payment waits in, with the movement triggered by the event that satisfies the condition, and a timeout or cancellation path if it never does. The key engineering insight across both is that without atomic transactions, correctness comes from making every step reversible and defining the compensations up front, so a failure at any point has a known, money-safe unwind rather than leaving funds stranded in escrow or a fee taken on a payout that never happened.
10. Failure modes stuck funds double movement and partial failure
A senior engineer is paid to know how money movement fails and to have already built the control for each failure before it happens. The failures are not exotic; they are the direct consequences of orchestrating across systems that do not share a transaction, and each has a specific defense.
The pattern across that list is the lesson of the whole page. Every failure mode comes from the gap between systems that cannot be committed together: the time between authorization and settlement, the moment between submission and acknowledgement, the steps of a multi-hop transfer that complete at different times. The defenses are always the same four tools applied in combination: an in-transit account so money is never unaccounted for in flight, an idempotency key so retries do not duplicate, a durable state machine with timeouts so nothing is silently stuck, and reconciliation against the rail’s settlement file so your beliefs are checked against the truth. No single tool is sufficient. A platform that runs all four, continuously, is one where a failure is a known state with a known recovery rather than lost money.
11. Scaling from a startup to a large institution
The principles above are invariant, but their implementation changes dramatically as a program grows, and knowing the trajectory keeps you from either over-building on day one or under-building when volume arrives.
At the startup stage, a single bank partner, a single FBO account, one rail (often just ACH), and a single-database orchestrator carry you a long way. Reconciliation can be a daily job that compares one settlement file against one ledger. Do not distribute the orchestrator, do not add rails you do not need, and do not build a multi-bank abstraction before you have a second bank. The strong consistency of one database is a gift; spend it.
As volume and product scope grow, pressure arrives on several fronts at once: more rails (wires for large payments, instant rails for speed, cards for spend), each with its own settlement timing, file format, return semantics, and idempotency support, which pushes the orchestrator toward a clean abstraction over rails so the state machine does not grow a special case per rail. Higher throughput pushes the ledger and orchestrator toward partitioning, while the per-payment atomicity (a movement’s legs commit together) and the FBO invariant (sub-ledger equals pooled account) remain non-negotiable. And reconciliation scales from a daily batch toward continuous, automated matching with exception queues, because at high volume even a small break rate produces too many cases for a human to chase manually.
At the large institution stage, several structural changes recur. Programs add multiple bank partners for redundancy and capacity, which means the orchestrator must route a payment to the right bank and reconcile several FBO accounts. They often move toward direct rail membership for some rails (becoming a direct participant rather than going through a sponsor), trading operational and regulatory burden for control and cost. And the money-movement orchestrator typically becomes a dedicated internal platform with a narrow API that other product teams call, so that no product team writes money movement directly and the hard-won invariants (idempotency, state machine, reconciliation, FBO integrity) are enforced in one place rather than reinvented, and re-broken, in many.
flowchart TB
subgraph s1["Startup: one of everything"]
A["One bank partner, one FBO account,<br/>one rail, single-DB orchestrator,<br/>daily reconciliation"]
end
subgraph s2["Growth: many rails, higher volume"]
B["Rail abstraction over ACH, wire,<br/>instant, card; partitioned ledger;<br/>continuous reconciliation with exceptions"]
end
subgraph s3["Institution: a money-movement platform"]
C["Multiple bank partners, some direct<br/>rail membership, a dedicated orchestration<br/>service other teams call through a narrow API"]
end
s1 --> s2 --> s3What never changes across that whole arc is the set of invariants. At every scale, money in flight lives in an in-transit account, every movement carries a stable idempotency key, every payment is a durable state machine with timeouts, the sub-ledger reconciles to the pooled account and the rail files, and a movement is final only when settlement proves it. The fundamental principles are the same for a two-person fintech and a global institution. What grows is the number of rails, banks, and payments those principles must hold across, and the degree of automation required to enforce them at volume. The institution-specific conventions (which rails, which banks, which file formats, which thresholds) are details on top of a structure that does not move.
Mastery Questions
-
A payments engineer says, “We submit the ACH file, the rail returns no error, so we immediately tell the customer their money has arrived and make it spendable.” Walk through what is wrong with each part of that, and what the correct behavior is.
Answer. Three distinct errors are bundled together, each from conflating a stage of the lifecycle with a different one. First, “no error on submission” is not settlement; it only means the rail accepted the instruction into a batch. The money has not moved, clearing and settlement still lie ahead, and settlement is the only event that justifies saying the money has arrived. The correct signal is a match against the rail’s settlement file (or a real-time settlement confirmation), not the absence of an error. Second, even after settlement, ACH is reversible: a debit can be returned days later for insufficient funds or a closed account, so “arrived” is not the same as “irrevocable,” and the payment can move from an apparent success to Returned, requiring a reversal out of the in-transit account back to the customer. Third, “make it spendable immediately” confuses the recorded balance with the spendable balance. The correct design records the movement immediately so the books are complete, but governs spendability with a hold and an availability schedule that respects Regulation CC maximums and the program’s own fraud appetite, releasing funds only when the risk of reversal is acceptable. In short: submission is not settlement, settlement is not irrevocability, and recorded is not spendable. The platform must track each of those as a distinct fact.
-
Your orchestrator submits a payment to a rail, the call times out, and you never learn whether the rail accepted it. A junior engineer wants to resubmit immediately to be safe. Explain precisely why that is dangerous, what you should do instead, and what makes a later resubmission safe if you decide to do one.
Answer. A timeout is an unknown, not a failure. The rail may have accepted the payment and only the acknowledgement was lost, so blind resubmission risks moving the money a second time, a double movement, which is real, hard-to-reverse customer money gone. The correct response is to move the payment into a state that explicitly means “we do not know,” and resolve the unknown with the truth rather than a guess: query the rail for the payment’s status if it supports that, or wait for and match against the settlement file, which will either show the payment settled (advance to Settled) or absent (now you know it did not go, and may safely retry). What makes a later resubmission safe is the idempotency key: because the orchestrator attaches the same stable key to the logical payment across every attempt, a resubmission the rail has already seen is recognized as a duplicate and treated as a no-op, returning the original result instead of moving money again. So the safety comes from two things together: never treating an unknown as a failure (resolve via the settlement file or a status query), and idempotency that makes a retry harmless even if the first attempt did go through. Resubmitting without both is how platforms double-charge customers.
-
A regulator asks how you can prove that every customer’s money is safe in your FBO program, given that you hold all customer funds pooled in a single bank account. What is the structure that makes this provable, what daily control demonstrates it, and what would a break in that control mean?
Answer. The structure is the separation between the real money and the map of who owns it. The actual dollars sit in one for-benefit-of account at the bank partner, held for the benefit of the customers, not as the fintech’s own operating money. Who owns each piece is tracked in the fintech’s double-entry sub-ledger, where every customer has an account. The provable invariant is that the sum of all customer balances in the sub-ledger must always equal the balance in the FBO account at the bank. The daily control that demonstrates it is reconciliation: each day you match the sub-ledger total against the bank’s statement of the FBO account and against the rail settlement files, confirming all three agree and that every movement into or out of the pool is accounted for. That three-way agreement, run continuously and evidenced, is the proof that customer funds are intact, that the fintech is not commingling customer money with its own, and that the per-customer records exist to support pass-through deposit insurance. A break, where the sub-ledger total does not equal the FBO balance, means customer money is unaccounted for: either money moved that the sub-ledger did not record, or the sub-ledger records money the account does not hold. Because these are customer funds held in trust, a break is not a routine bug to schedule; it is investigated and resolved immediately, and the existence of the daily reconciliation is precisely what lets you detect and bound it rather than discover it when a customer’s money is missing.
Sources & evidence18 claims · 7 cited
Grounded in the standard US money-movement model: rail mechanics (ACH/Nacha, Fedwire, RTP, FedNow), the FBO/pooled-account and sub-ledger pattern, in-transit ledger accounting, Reg CC funds availability, exactly-once via idempotency plus reconciliation, and saga-style multi-step transfers. Specific Reg CC dollar figures and ACH timing are the load-bearing quantitative claims; rail/file-format details vary by program and are noted as institution-specific conventions rather than universal facts.
- Moving money between a fintech customer and an external account involves at least five independent systems (fintech ledger, bank partner, rail operator, receiving bank, receiving customer) that do not share a transaction and update at different times.internal reasoning
- Almost every money movement passes through five logical stages: initiation, authorization, clearing, settlement, and posting.stable common knowledge
- Standard ACH entries typically settle one business day after submission, and Same Day ACH settles the same business day across defined processing windows.verified
- Fedwire payments are real-time gross settlement and final, with authorization and settlement effectively the same moment.verified
- Card authorization happens in under a second but settlement typically lands a day or two later.stable common knowledge
- A for-benefit-of (FBO) account is a bank account held by the fintech or bank for the benefit of underlying customers, pooling many customers' funds in one account while a fintech sub-ledger tracks each customer's share.verified
- In an FBO program the sum of all customer balances in the sub-ledger must always equal the balance in the FBO account at the bank, and this reconciliation supports pass-through deposit insurance and prevents commingling of customer funds with operating funds.verified
- In-transit (clearing/suspense) accounts hold the value of money that has left its source but not yet reached its destination, keeping a double-entry ledger balanced at every instant a payment is in flight.stable common knowledge
- ACH debits are reversible and can be returned days after settlement (for example for insufficient funds or a closed account).verified
- Regulation CC implements the Expedited Funds Availability Act and sets maximum hold times for deposits into consumer transaction accounts; institutions may always make funds available faster.verified
- Under Regulation CC, the first $225 of a day's deposits must generally be made available by the next business day, a figure raised from $200 by an inflation adjustment, with longer maximums for exceptions such as new accounts, large deposits, and repeated overdrafts.verified
- Over an unreliable channel, true exactly-once delivery is impossible; you can only achieve at-most-once or at-least-once.stable common knowledge
- Effectively-once money movement is constructed by combining at-least-once retry (so the payment is not lost) with idempotency keys (so retries do not duplicate the movement).internal reasoning
- An idempotency key is a unique identifier reused across every retry of one logical payment, so a repeated submission returns the original result without moving money again.stable common knowledge
- A timeout when submitting to a rail is an unknown rather than a failure, because the rail may have accepted the payment; blind resubmission risks a double movement, so it should be resolved by status query or settlement-file reconciliation.internal reasoning
- A multi-step transfer is best modeled as a saga: a sequence of locally durable steps each with a compensating action, so a later failure unwinds the completed steps and leaves the system consistent without a single atomic transaction.verified
- Rails deliver settlement files or reports on a schedule (ACH operators and bank partners list settled and returned entries; card networks deliver daily settlement files), and reconciliation matches each line against the orchestrator's payment states.stable common knowledge
- The three reconciliation disagreement types are: matched (advance), in-file-but-not-in-states (money moved you did not initiate or wrong amount), and in-states-but-not-in-file (stuck or lost payment).internal reasoning
Cited sources
- Distributed transactions and orchestration patterns (sagas, outbox, idempotency) · Chris Richardson
- Nacha Operating Rules and Same Day ACH · Nacha (National Automated Clearing House Association)
- Fedwire Funds Service · Federal Reserve
- FBO accounts, pass-through deposit insurance, and custodial account record-keeping · FDIC
- Regulation CC, Availability of Funds and Collection of Checks (Expedited Funds Availability Act) · Federal Reserve / Consumer Financial Protection Bureau
- Idempotency keys for safe request retries in payments APIs · Stripe
- Saga and compensation for distributed long-lived transactions · Hector Garcia-Molina, Kenneth Salem