> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wiseyield.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Currency in financial endpoints

> How to write, read, and aggregate financial records across multiple currencies.

WiseYield's financial endpoints (expenses, sales, invoices, payroll, P\&L, cash-flow forecasts) handle multi-currency data row-by-row. This guide covers the practical mechanics — what to send when creating a row, what comes back in aggregations, and how to surface the conversion transparently to your users.

For the conceptual model, see [Currency model](/concepts/currency).

## Creating a financial row

Every financial table carries a `currency` column. Pass it as an ISO 4217 code on every create:

```bash theme={null}
POST /api/v1/farms/{farmId}/expenses
{
  "amount": 1500.00,
  "currency": "EUR",
  "category": "fertilizer",
  "transactionDate": "2026-05-17",
  "note": "Spring NPK application"
}
```

Supported currency codes include `USD`, `EUR`, `EGP`, `GBP`, `SAR`, `AED`, `INR`, `BRL`, `CNY`, `JPY`, `MXN`, `ZAR`. The full list is the union of currencies covered by ECB's daily reference rates plus the open.er-api.com canary fallback.

If you omit `currency`, the row defaults to `USD`. **Pass it explicitly** — defaults are convenient but unforgiving when your data is multi-currency.

## Reading an aggregation

Aggregation endpoints (P\&L, cash-flow, financial summary) return a structured response that's transparent about how the total was assembled:

```json theme={null}
{
  "baseCurrency": "EUR",
  "total": 12345.67,
  "byCurrency": {
    "EUR": { "original": 8000.00, "converted": 8000.00, "rate": 1 },
    "USD": { "original": 4500.00, "converted": 4140.50, "rate": 0.92011 },
    "EGP": { "original": 9000.00, "converted": 205.17, "rate": 0.022797 }
  },
  "unconvertedCurrencies": []
}
```

| Field                   | Meaning                                                                                                                            |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `baseCurrency`          | The currency the `total` is expressed in (resolved per the farm)                                                                   |
| `total`                 | Sum of every `byCurrency[*].converted` value                                                                                       |
| `byCurrency`            | Per-original-currency breakdown — `original` is the raw sum, `converted` is after FX, `rate` is what was applied                   |
| `unconvertedCurrencies` | Codes that didn't match a known currency (free-form strings like `"eg pounds"`) — their amounts contributed to the total at rate=1 |

### How `baseCurrency` is resolved

The base currency for any farm-scoped aggregation follows this precedence:

1. **`farms.currency`** — the explicit override set in farm settings.
2. **Country-derived default** — inferred from `farms.country`.
3. **`USD`** — final fallback.

To change a farm's display currency, update the farm:

```bash theme={null}
PATCH /api/v1/farms/{farmId}
{ "currency": "EUR" }
```

Subsequent aggregations will report `baseCurrency: "EUR"`.

## Surfacing the breakdown in your UI

The `byCurrency` map exists specifically so integrations can show users what was converted and at what rate. A good UI pattern:

```jsx theme={null}
function PnLTotal({ total, baseCurrency, byCurrency, unconvertedCurrencies }) {
  return (
    <div>
      <h2>Total: {formatMoney(total, baseCurrency)}</h2>

      {Object.keys(byCurrency).length > 1 && (
        <details>
          <summary>Conversion breakdown</summary>
          <table>
            {Object.entries(byCurrency).map(([code, b]) => (
              <tr key={code}>
                <td>{code}</td>
                <td>{formatMoney(b.original, code)}</td>
                <td>→ {formatMoney(b.converted, baseCurrency)}</td>
                <td>@ {b.rate.toFixed(4)}</td>
              </tr>
            ))}
          </table>
        </details>
      )}

      {unconvertedCurrencies.length > 0 && (
        <Warning>
          Could not convert: {unconvertedCurrencies.join(', ')}.
          Total may under-represent reality.
        </Warning>
      )}
    </div>
  );
}
```

## When a currency can't be converted

If a row was written with a free-form string (`"eg pounds"`, `"local"`, an empty value), the FX layer can't look up a rate. The amount is still included in the total **at rate=1**, and the original code is added to `unconvertedCurrencies`. This is a graceful-degradation choice: the total is wrong by some amount, but the user is told it's wrong rather than receiving a silently-broken number.

Always surface `unconvertedCurrencies` in your UI. Then either:

* Edit the offending rows to use a canonical ISO code, or
* Filter them out client-side if you need a clean total

## Rate freshness

* Rates are refreshed daily from ECB.
* Each request uses the same rate snapshot for the entire response — two reads at the same moment return the same total.
* Historical row-date rates are **not** yet applied. A row from six months ago is converted at today's rate. This is acceptable for live P\&L; if you're producing accounting exports for an audit period, compute totals at export time and store them alongside the source data.

## Common patterns

### Force a specific display currency

If your integration always reports in USD regardless of the farm's setting, pass `baseCurrency` as a query parameter:

```bash theme={null}
GET /api/v1/farms/{farmId}/pnl?baseCurrency=USD
```

(Where supported. Not every aggregation endpoint accepts an override — see the endpoint reference.)

### Group by category and currency

Most aggregations also accept `groupBy=category` (or similar) and return the same `byCurrency` envelope within each group. The structure is recursive: every numeric total is paired with its breakdown.

### Reconciling against a bank statement

Bank statements are typically single-currency. Filter the rows by their original currency before aggregating:

```bash theme={null}
GET /api/v1/farms/{farmId}/expenses?currency=EUR
```

The response carries `data[]` with only EUR rows and a `pagination` object — no FX conversion happens because there's only one currency in play.

## See also

* [Currency model](/concepts/currency) — billing vs operational currency, rate sources, precedence
* [Subscription tiers](/concepts/tier-ladder) — billing currency is always EUR (Adaptive Currency at checkout)
