Conversion currency
ISO 4217 allowlist, error codes, Stripe normalization, and the home-currency settings endpoint.
Every conversion ingested by Rekomi carries a currency field. The full list of supported codes is below, plus the error response shape when an unsupported code is sent and the developer-side surface for reading or changing the org's home (display) currency.
For the brand-facing concept page (when to change HomeCurrency, how FX normalization works, how payouts are split per currency, how the 1099-NEC handles non-USD earnings), see Multi-currency.
Supported currencies (v1)
31 ISO 4217 codes. Validated server-side on every ingest path.
USD EUR GBP CAD AUD JPY CHF SEK NOK DKK
NZD SGD HKD MXN BRL INR ZAR PLN CZK HUF
RON ILS TRY KRW PHP THB MYR IDR AED SAR
CNYCodes are normalized to uppercase server-side, so usd and USD are equivalent on the wire. Anything else (lowercase still-not-in-list codes, crypto tickers, currency-substitute tokens like XBT, retired codes) is rejected.
Ingest paths that accept currency
POST /api/tracking/s2s
Server-to-server, HMAC-signed. See S2S tracking for auth + signing.
{
"externalEventId": "purchase_abc123",
"affiliateSlug": "jane-recommends",
"amountCents": 9900,
"currency": "EUR",
"customerId": "cus_external_xyz"
}currency is optional. Omit to default to "USD".
POST /api/tracking/pixel
Browser pixel, public-key + origin allowlist. See Pixel tracking.
{
"external_event_id": "ORDER_42",
"amount_cents": 9900,
"currency": "JPY",
"email": "buyer@example.com"
}Same defaulting rules: omit for USD, send a 3-letter ISO code otherwise.
Error response for unsupported currencies
Sent on both ingest endpoints (S2S and pixel) when the code is well-formed but not in the v1 allowlist:
HTTP/1.1 400 Bad Request
Content-Type: application/json{
"error": "unsupported_currency",
"currency": "XYZ"
}If the code is malformed (not 3 letters, empty string after trim, contains digits), you get the existing shape:
{ "error": "currency_must_be_3_letters" }These are distinct on purpose. currency_must_be_3_letters is a malformed-input issue your client should never emit. unsupported_currency is "we recognize this is a real currency code, we just don't have an FX rate for it"; handle by mapping to a supported code on your side, or contact support if you need the allowlist extended.
Curl examples
USD conversion (S2S)
BODY='{"externalEventId":"order_001","affiliateSlug":"jane","amountCents":9900,"currency":"USD"}'
T=$(date +%s)
SIG=$(printf "%s.%s" "$T" "$BODY" | openssl dgst -sha256 -hmac "$REKOMI_SIGNING_SECRET" -hex | awk '{print $2}')
curl -sS https://api.rekomi.com/api/tracking/s2s \
-H "Authorization: Bearer $REKOMI_API_KEY" \
-H "X-Rekomi-Signature: t=$T,sig=$SIG" \
-H "Content-Type: application/json" \
-d "$BODY"EUR conversion (S2S)
BODY='{"externalEventId":"order_002","affiliateSlug":"jane","amountCents":8900,"currency":"EUR"}'
T=$(date +%s)
SIG=$(printf "%s.%s" "$T" "$BODY" | openssl dgst -sha256 -hmac "$REKOMI_SIGNING_SECRET" -hex | awk '{print $2}')
curl -sS https://api.rekomi.com/api/tracking/s2s \
-H "Authorization: Bearer $REKOMI_API_KEY" \
-H "X-Rekomi-Signature: t=$T,sig=$SIG" \
-H "Content-Type: application/json" \
-d "$BODY"JPY conversion (S2S)
JPY has no minor unit. amountCents is still required, but Rekomi treats the integer as whole yen on display. A 12,000 JPY sale is "amountCents": 12000.
BODY='{"externalEventId":"order_003","affiliateSlug":"jane","amountCents":12000,"currency":"JPY"}'
T=$(date +%s)
SIG=$(printf "%s.%s" "$T" "$BODY" | openssl dgst -sha256 -hmac "$REKOMI_SIGNING_SECRET" -hex | awk '{print $2}')
curl -sS https://api.rekomi.com/api/tracking/s2s \
-H "Authorization: Bearer $REKOMI_API_KEY" \
-H "X-Rekomi-Signature: t=$T,sig=$SIG" \
-H "Content-Type: application/json" \
-d "$BODY"Stripe-webhook currency handling
For brands using the Stripe integration, conversion currency comes from the Stripe invoice.payment_succeeded / charge.succeeded event's currency field. Stripe lowercases everything ("usd", "eur"). Rekomi:
- Uppercases the code (
"usd"→"USD"). - Checks it against the same 31-code allowlist used by S2S + pixel.
- If supported, stores it on the conversion row.
- If unsupported (rare; Stripe supports more currencies than the ECB publishes daily rates for), the conversion is still recorded so payouts can run, but a Sentry warning fires with the org id and the offending code so Rekomi ops can decide whether to extend the allowlist.
This means: Stripe brands cannot accidentally drop conversions by selling in a non-allowlisted currency. S2S and pixel are stricter because the caller controls the field and can validate before sending.
Reading / updating the home currency
There is a settings-side endpoint your application can call to read or set the org's HomeCurrency. Same Authorization: Bearer rk_live_* auth as the rest of the API.
GET /api/settings/home-currency
{
"homeCurrency": "USD",
"changedAt": null,
"canChange": true,
"supportedCurrencies": [
"USD", "EUR", "GBP", "CAD", "AUD", "JPY", "CHF", "SEK", "NOK", "DKK",
"NZD", "SGD", "HKD", "MXN", "BRL", "INR", "ZAR", "PLN", "CZK", "HUF",
"RON", "ILS", "TRY", "KRW", "PHP", "THB", "MYR", "IDR", "AED", "SAR",
"CNY"
]
}homeCurrency: current setting."USD"for any org that never explicitly changed it.changedAt: ISO-8601 timestamp of the most recent change, ornullif never changed.canChange:truewhen the caller's plan tier is Pro (or trialing on any tier).falseotherwise. Use this to gate UI without making a failing PUT.supportedCurrencies: the 31-code allowlist. Provided so clients don't have to hard-code the list.
PUT /api/settings/home-currency
{ "homeCurrency": "EUR" }Requires:
- Owner role on the org (Admin / Manager / Viewer return 403).
- Pro plan or active trial.
- A supported currency code.
Response on success:
{
"homeCurrency": "EUR",
"changedAt": "2026-05-18T14:22:01Z",
"canChange": true
}Error responses:
| HTTP | error | Cause |
|---|---|---|
| 400 | unsupported_currency | Code not in the 31-code allowlist |
| 400 | currency_must_be_3_letters | Malformed code |
| 402 | plan_tier_required | Org below Pro and not trialing |
| 403 | (empty) | Caller is not Owner of the org |
PUTs are idempotent: setting the same currency twice returns 200 both times, and only the first call writes a changedAt + audit-log entry.
See also
- Multi-currency for brands: concept page, payout splitting, tax-form handling.
- S2S tracking: server-side ingestion + refund endpoint.
- Pixel tracking: browser-side ingestion for no-code stacks.
- Webhooks:
payout.created/payout.paidpayloads always carry the per-payoutcurrency. - Sub-affiliate API: override conversion rows carry the same
currencyfield as the parent sale.