You ask what you'll get. The rail returns a destAmount using the current rate minus our 50 bps spread, and a 150 bps fee. Everything is stored in minor units so there's no float math. Quotes expire in 60 seconds.
Sender pays — card, ACH, or bank — and the deposit lands in platform cash. The ledger records it as an asset increase paired with a customer-funds liability (we owe them until the transfer completes).
KYC provider returns a decision; OFAC and PEP screens run. For low-risk small transfers the hold is instant. Larger or riskier ones go to compliance_hold until an analyst clears them.
The fee moves to revenue. The remaining USD is swapped into NGN at the FX hub. Spread revenue is recognized in USD; the recipient's committed NGN is booked as an in-flight payout. Two paired journal entries, one per currency.
The rail adapter sends. Rapyd, Thunes, Nium, Wise — any one of them can own this step. Mock in dev, real in prod. The orchestrator stores the rail transaction id so webhooks can find their transfer.
Rail confirms the recipient received the money. The in-flight NGN clears and we recognize the payout expense.
Reconciled. Terminal state. Tenant net balance is zero per currency: USD $0, NGN ₦0. Open positions remain in the FX hub for the trading desk to rebalance.