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

# Cross-currency buying

> How a buyer can transact in a currency a Seller does not settle in — the marketplace converts each source cost at the platform spot rate while the source is still paid in its own currency.

A Seller settles in its own currencies — its `paymentCurrencies` (the payout
set). A buyer whose currency is one of these pays exactly what the Seller is paid
in: no FX.

When a buyer's currency is **not** one a Seller settles in, the **marketplace**
can still admit the buy via **cross-currency FX**: it converts each source cost to
the buyer's currency at the platform spot rate and quotes the buyer in their own
currency. The source keeps quoting and being paid in its own currency — nothing
about the source changes. Apostra sits on the Seller's curation edge and holds
the cross-currency relationship between buyer and source.

## Two decisions, two owners

Currency support is split between the operator and the marketplace:

* **The operator decides which currencies it settles in** —
  `paymentCurrencies`. These are the operator's money and the operator's call;
  there is no FX on them.
* **The marketplace decides which directed buyer→settlement pairs it accepts via
  FX.** A buyer currency is admitted only when the exact pair to the Seller's
  settlement currency is supported.

FX remains automatic where a pair exists, but support is not a global cross-product.
For example, IDR→USD may be supported while IDR→GBP and USD→IDR are not. An FX
pair also does not make its buyer currency a supported seller settlement currency.

The Seller must first confirm a supported settlement currency. Apostra
never assumes USD when that currency is missing: the Seller is blocked and
rejects new media buys until the Seller sets a supported currency. Its independent
pause override is unchanged. Official sales-adapter Sellers settle through
the connected platform instead and do not use this Apostra settlement gate.
They still have a separate **source execution currency**: the currency the
connected ad-server account uses for orders, budgets, bids, and reporting. Before
that source can accept a new buy, its execution currency must be verified (where
the platform exposes it), explicitly selected, and included in the Seller's
payment currencies. Apostra never assumes USD and never uses FX to hide a mismatch
between the Seller and its ad server.

Execution-currency readiness is source-local. If one managed ad-server source
cannot prove its currency, Apostra withholds that source and its products.
Current public products from another active managed, third-party sales-agent, or
modular own-supply source remain discoverable and buyable. This execution-currency
check remains Seller-Account-wide only when no other buyer-visible source can
serve; a campaign that selects a product from the withheld source is still
rejected until that source is repaired. Other independent readiness checks can
still block the Seller Account.

## When it applies

A buyer is locked to one currency (say ZAR) and wants to buy from a source that
prices and settles only in another (say USD). Because the marketplace supports
ZAR→USD, the Seller admits the buy, converts each USD source cost to ZAR at the
platform spot rate, and quotes the buyer in ZAR. The source is still paid in USD.

A buyer whose currency is neither settled by the Seller nor supported by an
exact pair to its settlement currency is not admitted — discovery returns no products (a normal
"nothing for me here" outcome, not an error), and a media buy in that currency is
rejected.

## How it works

<Steps>
  <Step title="Discovery admits the buyer's currency">
    A buyer whose currency the Seller settles, or whose exact directed FX pair
    the marketplace supports, is surfaced products in their own currency.
  </Step>

  <Step title="Quotes are converted at the rate-of-the-day">
    Each source cost (in the Seller's settlement currency) is converted to the
    buyer's currency at the **rate-of-the-day** before the buyer is quoted — one
    rate per pair per UTC day, fixed at first use.
  </Step>

  <Step title="The quote holds for the day, then re-prices">
    An FX-converted product carries a hard **`expires_at`** (the next UTC
    midnight). Within that window the rate the buyer was quoted is the rate the buy
    books — quote and `create_media_buy` resolve the *same* rate-of-the-day, so
    "quoted ZAR 52 → booked ZAR 52" holds. After it expires, re-discover to be
    re-quoted at the new day's rate. Re-executing from that fresh quote creates a
    new media buy; it never re-prices the earlier booked buy.
  </Step>

  <Step title="The booked rate is locked for the campaign">
    When a media buy is created, the rate in effect is snapshotted onto that buy,
    so it bills the source and settles the campaign at the rate committed at
    booking — every package, inventory source, update, delivery report, and payout
    for that media buy uses the same rate. A retry cannot replace it with a later
    rate; a later rate move never changes a booked buy.
  </Step>
</Steps>

## Rates

The conversion is keyed by a currency pair written `BASEQUOTE` (e.g. `USDZAR`):

* **BASE** is the settlement currency the source is paid in (the Seller's
  `defaultCurrency`).
* **QUOTE** is the buyer currency.
* The **rate** is QUOTE units per 1 BASE unit — the standard market quote
  (`USDZAR` = ZAR per USD). The buyer price in QUOTE = source cost in BASE × rate.

Rates come from a **platform spot feed** (ECB-backed reference rates). The
marketplace fixes a **rate-of-the-day** per pair (the first value seen each UTC
day is locked and not moved intraday), so a buyer quoted at discovery books at
that same rate as long as they transact within the day — that hold is the
window the FX-converted product's `expires_at` expresses. The day's rate is the
same for every buyer (not a per-buyer rate). A buy **locks the rate when it is
created** (`create_media_buy`): that snapshot bills the source and settles the
campaign for its whole life, so a later rate move never changes a booked buy. The
pilot uses spot rates and takes no hedge over the booking-to-settlement gap; a
longer hold means re-pricing (re-discovering) at the prevailing day's rate.

If the rate feed is temporarily unavailable when a new day's rate would be
fixed, the marketplace carries the **most recent locked rate forward** (up to 7
days old) as that day's rate-of-the-day rather than blocking buys, and pages its
operations team to restore the feed. Beyond that window the pair is treated as
unpriceable: cross-currency requests for it fail with `FX_RATE_UNAVAILABLE`
(retry later, or re-discover once rates are flowing) rather than pricing at a
stale rate.

## What the source sees

Nothing changes for the source. It advertises one currency, is paid in that
currency, and never sees the buyer's currency. The cross-currency relationship is
entirely a property of the Seller's curation edge between the buyer and the
source.

## Reporting delivered spend

Delivery reporting follows the same rule as billing: you see spend in your own
currency, converted at the rate locked onto the media buy when you booked it (a
buy created before rate-locking existed uses the rate-of-the-day for its creation
date). The source reports in the currency it is paid in, and we convert once.

We convert **only where the buy is actually cross-currency**. If a source reports
delivery in a currency it is not paid in — a labelling defect on the source's
side, not a real exchange — we do not apply a rate to it. Converting there would
multiply a correct amount by a real exchange rate and hand you a number that looks
plausible and is wrong. Instead:

* the daily/timeseries view keeps those rows in the currency the source reported,
  labelled as such, so nothing is silently relabelled;
* the summary view, which can only report one currency per advertiser, fails with
  `SPEND_DENOMINATION_UNRESOLVED` (HTTP 422) rather than mixing denominations.

The same holds when a source changes the currency it reports in partway through a
period: the buy's spend for that period is then a mix of denominations, and no
single currency describes it. We will not sum it into one figure — summary fails
the same way, rather than adding two currencies together. The daily view still
shows those days, each labelled with the currency that day was reported in.

If you hit that error, the response names the media buys involved — in the error
message itself, and in `details.mediaBuyIds`. Their delivery reports are
mislabelled at the source; contact support with those ids. The fix belongs on the
reporting side — your booked rate and your billing are unaffected.

**Do not retry it.** `SPEND_DENOMINATION_UNRESOLVED` is classified `terminal` and
returns 422, not 503: the answer depends on data that is wrong at rest, so every
retry reproduces it identically until the named buys are repaired. To keep
reporting on the rest of the account in the meantime, scope your request to
exclude the named buys — request a single campaign, or a period in which they did
not deliver. Every unaffected media buy continues to report normally.

`FX_RATE_UNAVAILABLE` (HTTP 503) is a different error and *is* worth retrying: it
means the exchange-rate feed could not price a currency pair at all, which clears
when the feed recovers.

### A media buy whose sources do not share one currency

`get_media_buy_delivery` reads one media buy across every source serving it and
rolls their delivery up into one response. Those sources should all report the
same currency: a buy created through Apostra is denominated in one currency
for its whole life, and products that settle in different currencies are split
into separate media buys (see [Multiple currencies in one
campaign](#multiple-currencies-in-one-campaign)).

A combined figure is only reportable when one currency covers every source that
contributed money to it. Two currencies cannot be added, and money reported
without a currency cannot be labelled — so in either case the response
**omits** its `currency`, its `aggregated_totals`, and the `spend` inside
`media_buy_deliveries[].totals`, and says why in `errors[]`:

| Error code                      | When                                                                                                                                                                                                                                                         |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `MIXED_CURRENCY_DELIVERY`       | Two sources reported different currencies. The codes are named in the message and in `details.currencies`. Happens on a legacy or externally created buy whose original contract allowed mixed denominations, or when a source mislabels what it is paid in. |
| `SPEND_DENOMINATION_UNRESOLVED` | A source reported spend without stating a currency for it, so part of the total has no known denomination. The same code the [reporting endpoint](/v2/guides/reporting-overview#currency) uses for a buy it cannot denominate.                               |

A source that reported **no** spend needs no currency — adding zero does not
change a denomination — so it never triggers either error.

Impressions, per-source status, and every other non-monetary metric are
unaffected and still reported; they do not depend on a currency.

Your money is not affected: each source is still paid, and each buy still bills
and settles at the rate locked when it was booked. What is withheld is a single
combined figure we cannot state truthfully. Read the sources separately to see
each amount in its own currency, and contact support with the media buy id — the
fix belongs with whichever source is reporting the wrong denomination.

## Where it applies

FX applies to both **composed** products and **wholesale / passthrough** products,
including live, account-specific products returned for a buyer's brief. In every
case, the source's price is converted to the buyer's currency for the quote and
the source is still paid in its own currency. Conversion happens **once, at the
buyer's edge** (the first Seller the buyer transacts with). In a curation
chain, internal Seller-to-Seller hops carry the settlement currency
through and never re-convert, so a buy is never FX'd twice.

## Multiple currencies in one campaign

A campaign is not restricted to a single currency. A buyer can select products
that settle in different currencies — across Sellers, and within a single
Seller that sells in more than one currency — all in the same campaign. The
buyer always transacts and is billed in their own primary currency; the
marketplace converts each product's cost at the rate-of-the-day described above.

Products are split into **one media buy per (Seller, settlement currency)**.
For example, a buyer in ZAR who selects USD and GBP products from Seller A and
USD products from Seller B gets three media buys — (A, USD), (A, GBP) and
(B, USD). Each media buy is denominated in the buyer's primary currency and carries
its own settlement currency and its own locked FX rate.

<Note>
  Cross-currency FX applies on currency-discovery, `create_media_buy`, and
  `update_media_buy`, including on manual-approval Sellers. Updating a buy
  re-applies its locked life rate — re-budgeting never re-quotes; **changing a
  buy's currency is not supported**, a buy's currency is fixed for its whole life.
  On a manual-approval Seller the rate quoted at submission is held for a
  limited window and re-applied when the buy is approved.
</Note>
