Перейти до основного вмісту

Currencies API

Auth: Clerk session.

List catalogue

GET /v1/currencies — per-tenant currency catalogue ordered (base first, then alphabetic).

{
"data": [
{ "code": "EUR", "kind": "fiat", "scale": 2, "symbol": "€", "display_name": "Euro", "is_active": true, "is_base": true },
{ "code": "USD", "kind": "fiat", "scale": 2, "symbol": "$", "display_name": "US Dollar", "is_active": true, "is_base": false },
{ "code": "BTC", "kind": "crypto", "scale": 8, "symbol": "₿", "display_name": "Bitcoin", "is_active": true, "is_base": false },
{ "code": "USDT", "kind": "crypto", "scale": 6, "symbol": "USDT", "display_name": "Tether", "is_active": true, "is_base": false }
]
}

Latest FX rate

GET /v1/currencies/fx?base=BTC&quote=EUR

{
"data": {
"base": "BTC",
"quote": "EUR",
"rate": 68201.0,
"ts": "2026-05-08T10:00:00+00:00",
"source": "coingecko"
}
}

Returns the latest rate ≤ now. 404 if no rate exists for that pair (operator can manually insert via SQL or the FX fetcher hasn't run for that pair yet).

FX fetcher

Run pnpm --filter @ucrm/api fx:fetch (cron-able):

  • FIAT pairs from Frankfurter (ECB-backed) — daily snapshots at 00:00 UTC
  • Crypto pairs from CoinGecko — hourly snapshots
  • Upserts on (tenant_id, base, quote, ts) PK
  • Output one JSON line: {"event":"fx-fetch-summary","upserted":56,"tenants":50}

Manual override

Operators can insert manual rates via direct SQL (or future admin endpoint):

INSERT INTO fx_rates (tenant_id, base, quote, ts, rate, source)
VALUES ('...', 'BTC', 'EUR', now(), 68000, 'manual')
ON CONFLICT DO NOTHING;

source = 'manual' rates take precedence in the lookup (latest wins regardless of source). Use sparingly — for pinning rates during incident review.