Skip to main content

Currency and Exchange Rates

Cashlytics supports multi-currency display, transaction metadata, and a converter tool. Since 0.8.13, all totals (dashboard, analytics, forecast, reserves, insights, and the AI tools) are converted with live exchange rates. The static table in src/lib/currency.ts is only the last fallback.

Settings​

User settings store locale and base currency (table user_settings, read server-side in src/lib/user-preferences.ts). Defaults come from NEXT_PUBLIC_DEFAULT_LOCALE and NEXT_PUBLIC_DEFAULT_CURRENCY when no user-specific setting exists. These are build-time variables: they are baked into the image and cannot be changed with the published Docker image.

Supported Currencies​

The supported currency list is defined in src/lib/currency.ts and includes EUR, USD, JPY, GBP, CNY, CHF, AUD, CAD, HKD, SGD, KRW, MXN, INR, NZD, SEK, NOK, PLN, TRY, ZAR, BRL, and DKK.

Exchange Rate Source​

src/lib/exchange-rates.ts fetches EUR-based rates from the Frankfurter API (https://api.frankfurter.app/latest?base=EUR&symbols=...) and persists them in the exchange_rates table (unique base/target, rate, fetched_at).

Caching works in layers:

  • In-memory cache per process: 1 hour. Concurrent requests share one pending fetch, and getRates is cached per request.
  • Database cache: 24 hours.
  • API timeout: 5 seconds.
  • After a failed API call, the next refresh attempt is delayed by 15 minutes.

Fetch priority is:

  1. fresh in-memory or database cache
  2. Frankfurter API refresh when stale or empty
  3. stale database cache if the API is unavailable
  4. built-in fallback rates

The root layout loads the rates once and passes them to the client, so the Tools page and all totals use the same values.

API​

GET /api/exchange-rates returns current rates with EUR as base as a flat object ({ "EUR": 1, "USD": ... }), or 500 with { error }. It requires a session. No bundled client calls this endpoint; it is only useful for external use.

Transaction writes can store original amount, saved currency, and exchange rate metadata so later display does not depend only on current rates.