A UK customer pays a US startup £782.54 through Stripe. For the first year we kept that company's books, the ledger recorded it as $782.54.
Not $1,000-something at the day's rate. The digits, with the currency symbol swapped. Every non-USD balance from Stripe and Wise was landing that way, because the ledger had one amount column and it was assumed to be dollars.
This is the state of most startup books the first time a company sells outside the US. This post is what we changed, what "USD equivalent" means once you look closely, and the one transaction type that still needs a human.
One functional currency, two amounts per line
The books of a US company are kept in US dollars. That is not a preference; it is what the IRS and your investors expect to read. Every report, every balance and every P&L line is a dollar figure.
But the bank statement said £782.54, and an auditor will ask to see that. So every transaction and every journal entry line in our ledger now carries two amounts: the native amount, in the currency the bank actually moved, and the USD amount used by every report. Alongside them sit the currency code, the exchange rate applied, and where that rate came from.
For a dollar transaction the two amounts are equal and the rate is 1. The Stripe balance that holds the £782.54 is itself a bank account in the ledger, for the reasons in why your Stripe payouts don't match your revenue. For anything else, the USD amount is the native amount times the rate on the transaction date. Rates come from daily ECB-published data, fetched once per date and currency pair and cached permanently, so the same transaction converts the same way forever.
Reports stay in dollars, unchanged. Per-account views, the transaction list for a EUR account, its reconciliation screen, its ledger, show the native currency first and USD second, because that is the number you can check against the statement.
What "USD equivalent" is, and is not
The USD amount on a foreign-currency transaction is an estimate on a date. It is the rate that a central bank published for that day, applied to the amount your bank reported. It is not what you would have received had you converted, and it is not what the bank charged you when you did.
That distinction matters most for transfers. Say you move €1,000 from your EUR account to your USD account. Your bank converts at its rate and, after a spread, $1,087.50 arrives. The EUR leg of that transfer is recorded at the ECB rate for the day, say $1,092.40. The USD leg is recorded at what arrived, $1,087.50.
These are the same money. Our transfer matcher pairs legs by exact USD-cent equality, because that is what makes it safe: two legs that agree to the cent are almost certainly one transfer. Cross-currency legs never agree to the cent, so for months every EUR-to-USD transfer sat unmatched, with both sides waiting.
The adjustment
The fix was not to loosen the matcher; a tolerant matcher pairs things that are not transfers. The fix was to let a person say what the settled amount was.
On any unmatched foreign-currency transaction, a manager can now set the USD equivalent to what actually arrived on the other side. That rewrites the USD amount, records the implied exchange rate, marks the rate source as manual, and re-runs the matcher. In the case above the EUR leg is adjusted from $1,092.40 to $1,087.50, the legs match, and the transfer closes.
The $4.90 between the two figures is real. It is the bank's spread and the rate movement, and it is an FX loss. Today it is folded into the adjusted leg. A proper FX gain/loss account, with period-end revaluation of foreign balances, is the next step and is designed but not built. We would rather say that than pretend the number is not there.
The things a bank feed cannot tell you
PayPal reports one combined balance per account through the bank feed, in dollars, even when you hold EUR inside it. There is no per-currency wallet figure to fetch. So the bank column for a PayPal EUR balance shows a dash and a note, rather than a converted number that would imply precision the feed does not have. The ledger side still shows the EUR activity correctly, because every transaction carries its currency.
Legacy rows are the other gap. Transactions ingested before the currency columns existed default to USD, which mislabels genuine foreign activity on a foreign account. We identify them by the absence of a native amount, which every transaction ingested since has, and treat them as the account's currency at their stored value. A backfill that rewrites history would be tidier and far more dangerous; we chose the read-side rule.
What to ask of any bookkeeping setup
If you bill outside the US, or hold a Wise or Revolut balance, three questions (and a fourth, if you are working out runway: count foreign balances at a stated rate, not at their face value):
- Can you see the native amount and the USD amount on the same transaction? If only one is visible, one of them is being guessed.
- Which rate, from where, on which date? "The rate" is not an answer. It should be a published daily rate with the source recorded per transaction.
- What happens to a EUR-to-USD transfer? If the answer is that it matches automatically, ask how, because the two sides never agree to the cent.
The honest version of multi-currency bookkeeping has a manual step in it. The dishonest version has a £782.54 booked as $782.54.
Common questions
What is the USD equivalent of a foreign-currency transaction?
An estimate on a date: the native amount times a published daily rate for the transaction date, with the rate and its source recorded on the transaction. It is not what the bank would have paid you on conversion, because a bank conversion includes a spread and happens on its own day.
Why do EUR-to-USD transfers not match automatically?
The EUR leg is recorded at the day's published rate and the USD leg at what actually arrived after the bank's spread, so the two never agree to the cent, and a safe transfer matcher requires exact equality. A person sets the settled USD amount on the foreign leg, and the legs match.
Do the reports stay in US dollars?
Yes. Reports, balances and the P&L read the USD amount on every line, unchanged. The native amount sits alongside it as the audit trail, and per-account views for a foreign-currency account show the native currency first and USD second, because that is the figure you can check against the statement.
Tools that go with this
- SaaS accounting: revenue recognition and tax - billing international customers through Stripe and recognising the revenue
- E-commerce accounting: bookkeeping, inventory and sales tax - multi-currency storefronts and payouts
- Burn rate and runway calculator - runway from balances held in more than one currency