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

# Multi-currency matching

> Reconcile transactions across currencies in Matcher by converting to a base amount using per-transaction FX hints, then apply your existing match rules.

Matcher lets you reconcile transactions in different currencies by converting amounts to a common base currency before comparison. This enables matching across international transactions, treasury operations, and multi-entity reconciliations.

## Overview

***

Multi-currency matching converts both transaction amounts to a base currency using the appropriate FX rate, then applies standard matching rules. If the converted amounts fall within tolerance, Matcher creates a match. Otherwise, it creates an exception for review.

<Frame caption="Multi-currency matching flow.">
  <img src="https://mintcdn.com/lerian-49cb71fc/SEOef3JqTInYAAau/images/en/d2/matcher-multicurrency-matching.svg?fit=max&auto=format&n=SEOef3JqTInYAAau&q=85&s=9f773cf7a55bda246a46349536efbbe4" alt="Multi-currency matching flow." width="1482" height="426" data-path="images/en/d2/matcher-multicurrency-matching.svg" />
</Frame>

## How it works

***

Multi-currency support is built into the existing context types (`1:1`, `1:N`, `N:M`) and match rules — there is no separate "multi-currency" context type.

When transactions have different currencies, Matcher uses the `amountBase` and `currencyBase` fields on each transaction to compare converted amounts. Today, these base fields are populated **at match time**: Matcher derives them from per-transaction FX hints carried in the transaction's own metadata (see [FX from transaction metadata](#fx-from-transaction-metadata) below).

You cannot supply a base amount directly at file upload — the field-map vocabulary has no base-amount columns. If a transaction already carries a base amount, Matcher honors it and never overwrites it, but the supported way to get base amounts onto your transactions is the FX metadata path.

There is no external FX provider or rate-lookup service: the rate always comes from the transaction row itself.

### Key components

| Component                                         | Where it lives       | Purpose                                                                           |
| ------------------------------------------------- | -------------------- | --------------------------------------------------------------------------------- |
| `amountBase` / `currencyBase`                     | Transaction fields   | Base-currency amounts used for comparison, derived at match time from FX metadata |
| `matchBaseAmount` / `matchBaseCurrency`           | Rule config          | Tell a rule to compare base amounts instead of originals                          |
| `fx_rate`, `fx_base_currency`, `fx_notional_expr` | Transaction metadata | Per-transaction FX hints used to derive the base amount at match time             |

## Configuring rules for multi-currency

***

Enable multi-currency comparison by setting `matchBaseAmount` and `matchBaseCurrency` to `true` in the rule config.

### Exact rule with base amount matching

```bash cURL theme={null}
curl -X POST "https://api.matcher.example.com/v1/contexts/{contextId}/rules" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
   "type": "EXACT",
   "priority": 1,
   "config": {
     "matchBaseAmount": true,
     "matchBaseCurrency": true,
     "matchDate": true,
     "matchReference": false,
     "matchScore": 100,
     "matchBaseScore": 90
   }
 }'
```

When `matchBaseAmount` is `true`, the rule compares `amountBase` fields instead of `amount`. When `matchBaseCurrency` is `true`, it compares `currencyBase` instead of `currency`.

### Tolerance rule with base amount matching

```bash cURL theme={null}
curl -X POST "https://api.matcher.example.com/v1/contexts/{contextId}/rules" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
   "type": "TOLERANCE",
   "priority": 2,
   "config": {
     "matchBaseAmount": true,
     "matchBaseCurrency": true,
     "percentTolerance": 0.02,
     "absTolerance": 10.0,
     "matchScore": 85,
     "matchBaseScore": 80
   }
 }'
```

### Confidence scoring

The `matchScore` and `matchBaseScore` fields are **accepted and validated** in the rule config, but they **do not influence the calculated confidence score**. The scoring engine always uses the fixed internal component weights (`DefaultConfidenceWeights`: amount 40, currency 30, date 20, reference 10) to produce a 0–100 score. Values like `matchScore: 100` or `matchBaseScore: 90` are **not** applied directly as the match output.

These fields are currently **reserved for future use** (retained for parity across rule configs and for metrics); setting them has no effect on how a match is scored or auto-confirmed today.

<Warning>
  Do not rely on `matchScore` / `matchBaseScore` to control confidence. Whether a rule matches original or base amounts, the confidence score is computed from the same 40/30/20/10 component weights. To reflect FX uncertainty, tune the matching **rule** itself (for example, use a TOLERANCE rule or adjust date/reference requirements) rather than these score fields.
</Warning>

For the full scoring model, see [Confidence scoring](/en/matcher/reference/matcher-confidence-scoring).

## FX from transaction metadata

***

When a transaction has no base amount yet, Matcher converts it at match time using FX hints carried in that transaction's `metadata`. Matcher does **not** call any external rate provider — the rate travels with the row.

Conversion only runs when `fx_base_currency` is present, and it never overwrites a base amount that is already set on the transaction. The original `amount` and `currency` are never mutated — conversion changes only the comparison.

### Metadata fields

| Metadata field     | Required                              | Purpose                                                                                                                                       |
| ------------------ | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `fx_base_currency` | Yes (to trigger)                      | The base currency the amount is converted **into**. Becomes `currencyBase`.                                                                   |
| `fx_rate`          | Yes, unless `fx_notional_expr` is set | Multiplicative rate. `amountBase = amount * fx_rate`.                                                                                         |
| `fx_notional_expr` | No                                    | Expression evaluated against the transaction metadata to derive the base notional directly. When present, it takes precedence over `fx_rate`. |
| `fx_rate_source`   | No                                    | Optional label identifying where the rate came from, kept for validation and audit. Defaults to `metadata`.                                   |

### Example transaction with FX metadata

```json theme={null}
{
  "external_id": "txn_001",
  "amount": 1000.00,
  "currency": "EUR",
  "date": "2024-01-15",
  "metadata": {
    "fx_base_currency": "USD",
    "fx_rate": "1.085",
    "fx_rate_source": "ecb"
  }
}
```

With the metadata above, Matcher derives `amountBase = 1000.00 * 1.085 = 1085.00` and `currencyBase = USD`, then compares against the other side using the rule's `matchBaseAmount` / `matchBaseCurrency` settings.

<Info>
  If a transaction already carries a base amount, these metadata hints are ignored — Matcher never overwrites an existing base amount. If the hints are missing or malformed (unparseable rate, failed expression), the transaction simply does not participate in base-amount matching — the run is not aborted.
</Info>

### When base fields are missing

When a rule requires base-amount matching (`matchBaseAmount` / `matchBaseCurrency`) and transactions lack a base amount or base currency, Matcher records the condition under the `FX_RATE_UNAVAILABLE` exception reason. You can filter the exceptions list by `reason=FX_RATE_UNAVAILABLE` (along with the related `MISSING_BASE_AMOUNT` and `MISSING_BASE_CURRENCY` reasons) to find transactions that could not join base-amount comparison.

## FX rate-variance band

***

Cross-currency amounts often disagree slightly because each side was converted at a different rate or on a different day. The `fxVarianceBand` key on TOLERANCE rules handles this: it defines a **second threshold stacked above the match tolerance**, expressed as a decimal fraction (`0.0001` = 1 basis point).

After the strict-tolerance pass, Matcher rescans unmatched cross-currency `1:1` pairs. A pair whose base-amount residual exceeds the match tolerance but stays within the band still **matches** — the pair becomes a proposed group with a fixed confidence of 75, below the auto-confirm threshold, so it always requires human review. Both transactions are flagged with the `FX_RATE_VARIANCE` exception reason so the residual is recorded as a typed exception instead of collapsing to `UNMATCHED`.

The band applies only when:

* both sides carry a base amount and the same base currency;
* the original currencies differ (same-currency drift is a plain mismatch, not an FX case);
* all of the rule's other gates (date window, reference, currency, composite fields) still pass.

A zero or absent `fxVarianceBand` disables the band.

```bash cURL theme={null}
curl -X POST "https://api.matcher.example.com/v1/contexts/{contextId}/rules" \
 -H "Authorization: Bearer ***" \
 -H "Content-Type: application/json" \
 -d '{
   "type": "TOLERANCE",
   "priority": 3,
   "config": {
     "matchBaseAmount": true,
     "matchBaseCurrency": true,
     "percentTolerance": 0.01,
     "fxVarianceBand": "0.005"
   }
 }'
```

## Transaction fields

***

For multi-currency matching, each transaction carries both original and base currency fields. You supply `amount` and `currency` at upload; Matcher derives `amountBase` and `currencyBase` at match time from the FX metadata:

| Field          | Type    | Description                                                   |
| -------------- | ------- | ------------------------------------------------------------- |
| `amount`       | Decimal | Original transaction amount (supplied at upload)              |
| `currency`     | String  | Original ISO 4217 currency code (supplied at upload)          |
| `amountBase`   | Decimal | Amount converted to base currency (derived from FX metadata)  |
| `currencyBase` | String  | Base currency ISO 4217 code (derived from `fx_base_currency`) |

### Example transaction

After FX conversion, a transaction looks like this internally:

```json theme={null}
{
  "external_id": "txn_001",
  "amount": 1000.00,
  "currency": "EUR",
  "amountBase": 1085.00,
  "currencyBase": "USD",
  "date": "2024-01-15",
  "description": "PAY-2024-001"
}
```

## Example: cross-currency reconciliation

***

**Source (EUR account):**

| ID       | Amount       | Base amount  |
| -------- | ------------ | ------------ |
| txn\_001 | 1,000.00 EUR | 1,085.00 USD |

**Target (USD account):**

| ID       | Amount       | Base amount  |
| -------- | ------------ | ------------ |
| txn\_002 | 1,095.00 USD | 1,095.00 USD |

With a TOLERANCE rule (`matchBaseAmount: true`, `percentTolerance: 0.02`):

* Base amounts: $1,085.00 vs $1,095.00
* Variance: \$10.00 (0.92%)
* Tolerance: 2%
* Result: **Match** (0.92% \< 2%)

## Best practices

***

<AccordionGroup>
  <Accordion title="Supply stable FX metadata per transaction">
    Attach `fx_base_currency` and `fx_rate` (or `fx_notional_expr`) to each transaction's metadata at the source, using the rate that applied when the transaction settled. Because the rate travels with the row, results are reproducible across runs — no runtime rate lookups.
  </Accordion>

  <Accordion title="Reflect FX uncertainty through rule design">
    `matchBaseScore` and `matchScore` are reserved fields and do not change the calculated confidence score — the engine always uses the fixed 40/30/20/10 weights. To flag FX-converted matches for review, design the rule itself (e.g. tighter tolerances or required reference/date checks) instead of relying on these score fields.
  </Accordion>

  <Accordion title="Combine with tolerance rules">
    FX conversions introduce small variances. Use TOLERANCE rules with matchBaseAmount to allow for rounding and rate timing differences.
  </Accordion>

  <Accordion title="Document your base currency choice">
    Use a consistent base currency across all contexts. USD is common for international operations; use your reporting currency for domestic + international.
  </Accordion>
</AccordionGroup>

## Next steps

***

<Card title="Confidence Scoring" icon="chart-simple" href="/en/matcher/reference/matcher-confidence-scoring" horizontal>
  How match scores work and what thresholds apply.
</Card>

<Card title="Match Rules" icon="scale-balanced" href="/en/matcher/configuration/matcher-match-rules" horizontal>
  Full reference for rule types and config fields.
</Card>
