| exchange_rate_id | string, UUID | ✅ Yes | unique; default gen_random_uuid() | — | Public stable identifier for this exchange rate row. Generated server-side via gen_random_uuid(); never set by the client. |
| source_currency | string (CurrencyCodeEnum) | ✅ Yes | NOT NULL; must differ from target_currency (CHECK source_currency != target_currency); maps to Postgres native enum currency_code_enum | ISO 4217 codes: USD, EUR, GBP, JPY, CHF, CAD, AUD, NZD, SEK, NOK, DKK, PLN, CZK, HUF, RON, BGN, … (full CurrencyCodeEnum, ~180+ values) | The currency being converted from. Together with target_currency and rate_date, forms part of the composite uniqueness key (scoped to workspace). |
| target_currency | string (CurrencyCodeEnum) | ✅ Yes | NOT NULL; must differ from source_currency (CHECK source_currency != target_currency); maps to Postgres native enum currency_code_enum | ISO 4217 codes: USD, EUR, GBP, JPY, CHF, CAD, AUD, NZD, SEK, NOK, DKK, PLN, CZK, HUF, RON, BGN, … (full CurrencyCodeEnum, ~180+ values) | The currency being converted to. The rate expresses how many target_currency units equal one source_currency unit. |
| rate | string (decimal, 18,8 precision) | ⚪ No | nullable; DECIMAL(18,8); CHECK rate > 0 (enforced in migration DDL) | — | The conversion rate from source_currency to target_currency on rate_date. Stored as a decimal string to preserve full precision. A null value indicates the rate was recorded but the numeric value is not yet resolved. |
| rate_date | Date (date column) | ✅ Yes | NOT NULL; columnType date (date-only, no time component); part of the composite uniqueness key (workspace, source_currency, target_currency, rate_date) | — | The calendar date for which this rate is valid. One row per (workspace, source_currency, target_currency, date) tuple. |
| source | string | ⚪ No | nullable; length 100 | — | Free-text provenance label for the rate — e.g. the feed name, connector slug, or manual entry identifier. Used to distinguish ECB daily rates from connector-sourced or user-overridden rates. |
| created_at | string (ISO 8601 datetime), 🔒 system | ✅ Yes | set by MikroORM onCreate lifecycle hook; never writable after creation | — | Timestamp when the row was first persisted. Set server-side only. |
| updated_at | string (ISO 8601 datetime), 🔒 system | ⚪ No | set by MikroORM onCreate and onUpdate lifecycle hooks | — | Timestamp of the last mutation to this row. Auto-maintained by the ORM. |
| deleted_at | string (ISO 8601 datetime) | null | ⚪ No | nullable; soft-delete sentinel; queries must filter deleted_at IS NULL | — | Soft-delete timestamp. Non-null means this rate has been logically removed. All standard queries must include the deleted_at IS NULL predicate. |