> For the complete documentation index, see [llms.txt](https://docs.younium.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.younium.com/currency-and-exchange-rates-integrations/currency-exchange-rates.md).

# Currency & Exchange Rates

## Overview

Younium imports foreign exchange rates into `CurrencyExchangeRate` records for use in billing, invoicing, and base-currency conversion. Three providers are available: **European Central Bank (ECB)**, **OANDA**, and **Sveriges Riksbank**. Each writes rates from foreign currencies **to** the legal entity base currency (ECB and OANDA for any configured base; Riksbanken requires **SEK**). Rates can be imported one at a time or in bulk, and optionally backfilled over a historical date range.

***

## European Central Bank (ECB)

### Overview

Younium downloads the daily euro reference rates published by the ECB and, when your base currency is not EUR, derives the cross rates from them.

### Data source

| Parameter        | Value                                                                         |
| ---------------- | ----------------------------------------------------------------------------- |
| Series template  | `https://data-api.ecb.europa.eu/service/data/EXR/D.{currencyFrom}.EUR.SP00.A` |
| Query            | `startPeriod`, `endPeriod`, `format=csvdata`                                  |
| Default window   | Last 7 days through today if dates omitted                                    |
| Single-date mode | When either date omitted, only latest observation per currency                |

### Rate mapping (ECB → Younium)

| ECB CSV field (parsed) | Younium `CurrencyExchangeRate`          |
| ---------------------- | --------------------------------------- |
| Column date            | `EffectiveFromDate`                     |
| Foreign currency code  | `FromCurrency` (tenant currency entity) |
| Base currency          | `ToCurrency` (legal entity base)        |
| Computed rate vs EUR   | `Rate` (cross via EUR when base ≠ EUR)  |

Cross logic: if `currencyFrom == EUR`, invert latest EUR→target; else divide foreign/EUR series element-wise by base/EUR series for matching dates.

### Import behaviour

| Mode                   | How imported rates are applied       |
| ---------------------- | ------------------------------------ |
| Explicit from/to dates | `addHistoricalExchangeRates = true`  |
| Default/latest only    | `addHistoricalExchangeRates = false` |

Per-currency failures are collected; partial success sets `ImportMessage` with joined errors.

### Settings

| Setting                       | Description                     |
| ----------------------------- | ------------------------------- |
| `ExchangeRateImport`          | Last run overall success flag   |
| `LastExchangeRateImport`      | UTC timestamp                   |
| `LastExchangeRateImportCount` | Count imported                  |
| `ImportMessage`               | Partial failure summary or null |

### Scheduled job

`ImportECBRatesJob` — European Central Bank exchange rates job (minimum daily interval in job configuration).

***

## OANDA

### Overview

OANDA rates are fetched from the Rates API v2 spot endpoint, using an API key you store against the integration.

### Authentication

| Younium Field          | OANDA API                                                  |
| ---------------------- | ---------------------------------------------------------- |
| `OandaSettings.ApiKey` | `api_key` query parameter                                  |
| Validation request     | `GET .../rates/spot.json?api_key={key}&base=USD&quote=SEK` |
| `ApiKeyValid`          | Set true on HTTP 200 parse success                         |

`ValidateApiKey` persists key and clears `ActivationErrorMessage` on success.

### Sandbox vs. Production

Single public endpoint host: `https://web-services.oanda.com/rates/api/v2/` (no separate sandbox URL in code).

### Sync workflow

`SyncExchangeRates`:

1. Base currency from `entityContext.BaseCurrency()`.
2. Build query with `&base={eachForeignCode}` and `&quote={baseCode}`.
3. For each quote in response `quotes` array:

| OANDA JSON field  | Younium `CurrencyExchangeRate`       |
| ----------------- | ------------------------------------ |
| `base_currency`   | `FromCurrencyId` (match code)        |
| `ask`             | `Rate`                               |
| Current date/time | `EffectiveFromDate` (`DateTime.Now`) |
| Base currency id  | `ToCurrencyId`                       |

A rate that overlaps an existing interval for the same currency pair is not imported, and the conflict is reported on the import result.

### Settings

| Setting                     | Description                                  |
| --------------------------- | -------------------------------------------- |
| `ApiKey`                    | Stored API key                               |
| `ApiKeyValid`               | Activation flag                              |
| `ExchaneRateSync`           | Last sync succeeded (typo preserved in code) |
| `LastExchangeRateSync`      | UTC timestamp                                |
| `LastExchangeRateSyncCount` | Rates created                                |
| `ActivationErrorMessage`    | Auth or per-rate errors                      |

`Deactivate` removes settings row.

***

## Sveriges Riksbank

### Overview

Riksbank rates are imported from the Riksbank API as observation series, grouped by series, and are always quoted against **SEK**.

### Eligibility

| Rule                        | Behaviour                                                                                                        |
| --------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| Base currency must be `SEK` | Otherwise `SyncMessage` = “Riksbanken can only synchronize with SEK as base currency.”, sync flag false, count 0 |

### Observation window

| Condition                                 | From date                     |
| ----------------------------------------- | ----------------------------- |
| Never synced (`LastExchangeRateSync` min) | One month ago                 |
| Prior sync exists                         | Last sync date (`yyyy-MM-dd`) |

Series filtered: `SeriesId` prefix matches base currency code, series not closed, `ObservationMaxDate` ≥ from date.

### Rate mapping (Riksbanken → Younium)

| Riksbanken observation           | Younium `CurrencyExchangeRate` |
| -------------------------------- | ------------------------------ |
| Series currency (from series id) | `FromCurrency`                 |
| `SEK` base                       | `ToCurrency`                   |
| Observation date                 | `EffectiveFromDate`            |
| Observation value                | The imported rate              |

### Settings

| Setting                     | Description                      |
| --------------------------- | -------------------------------- |
| `ExchaneRateSync`           | Combined success across groups   |
| `LastExchangeRateSync`      | Timestamp (always updated)       |
| `LastExchangeRateSyncCount` | Incremented per series processed |
| `SyncMessage`               | Errors or informational text     |

***

## Shared exchange rate semantics

| Concept             | Behaviour                                                                       |
| ------------------- | ------------------------------------------------------------------------------- |
| Direction           | Foreign currency → base currency                                                |
| Duplicate intervals | `Create` may return null (“rate exist in another interval”)                     |
| Historical ECB      | Bulk `ImportExchangeRates` with historical flag                                 |
| ERP imports         | NetSuite, Fortnox, Exact, etc. have separate jobs — not covered in this article |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.younium.com/currency-and-exchange-rates-integrations/currency-exchange-rates.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
