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

# Currencies and countries setup and configuration

> Auth, caching, rate limits, and what to do when reference data changes underneath you.

## Credentials

Three of the four endpoints take an API key. One does not.

| Endpoint | Auth |
| - | - |
| `GET /v1/currencies` | `Authorization: Bearer sk_…` |
| `GET /v1/currencies/{code}/routes` | `Authorization: Bearer sk_…` |
| `GET /v1/transfer-corridors` | `Authorization: Bearer sk_…` |
| `GET /countries` | **None.** Public and unversioned. |

`GET /countries` is deliberately open: it feeds a country picker drawn before anyone has an account. It is also the only endpoint documented here that does not live under `/v1`, so do not build its URL from your `/v1` base path.

Every response is identical for every business. Nothing here is scoped to your key — the key is there for rate limiting and access control, not for tenancy.

## Caching

This is the main configuration decision, and the endpoints tell you what they expect.

| Data | Suggested TTL | Why |
| - | - | - |
| Countries | 24 hours | The response sets `Cache-Control: public, max-age=86400`. The list changes only on a deploy. |
| Currencies | 1–24 hours | Use `updatedAt` on each row as a cache key. |
| Routes and corridors | Per session, or 1 hour | Field descriptors change rarely, but a withdrawn route should not linger for a day. |

<Warning>
  Do not call these per request. A payout form that fetches corridors on every keystroke will meet the 120-per-minute corridor limit quickly, and the answer will not have changed.
</Warning>

Cache **the whole response**, not a derived subset. When a route gains a required field, a cache holding only `id` and `name` will keep building payloads that no longer validate.

## Rate limits

Per 60 seconds, counted once against your business and once against the key.

| Endpoint | Requests per minute |
| - | -: |
| `GET /v1/currencies` | 600 |
| `GET /v1/currencies/{code}/routes` | 600 |
| `GET /v1/transfer-corridors` | 120 |
| `GET /countries` | Not key-limited |

Corridors is limited fivefold lower than the others because it resolves currency and country data for every route it returns. If you are hitting that limit, you are calling it where a cache belongs.

## No events

Reference data emits no webhooks. There is no `currency.updated`.

That means a currency switching off is something you discover in one of two ways: proactively, by refreshing your cache on a schedule, or reactively, by handling `409 currency_capability_unavailable` and `409 fx_corridor_unavailable` at the point of use.

Do both. The refresh keeps your UI honest; the error handler keeps a stale cache from becoming a failed payout.

```ts theme={null}
// The cache is an optimisation, never the authority.
async function openAccount(currency: string) {
  if (!(await cachedCurrencies()).some((c) => c.code === currency && c.accountIssuanceEnabled)) {
    throw new UnsupportedCurrency(currency);      // fast path, good message
  }
  const result = await post('/v1/accounts', { currency, /* … */ });
  if (result.status === 409 && result.body.code === 'currency_capability_unavailable') {
    await refreshCurrencyCache();                 // we were stale; correct ourselves
    throw new UnsupportedCurrency(currency);
  }
  return result.body;
}
```

## Errors

These endpoints have almost no failure surface — no bodies, no path resources to miss.

| Status | `code` | Cause |
| - | - | - |
| `400` | `validation_failed` | `capability` outside the six values, a `code` that is not three characters, or a `country` that is not two. |
| `401` | `INVALID_API_KEY` | Wrong environment, revoked, expired, or IP-blocked. Not applicable to `GET /countries`. |
| `429` | `api_key_rate_limit_exceeded` | Usually a missing cache. |

There is no `404`. An unknown currency code returns an empty route array rather than an error, and an unknown country filter simply matches nothing.

## Configuration checklist

* [ ] `minorUnits` read from this service, not hardcoded — UGX has none and crypto has eight
* [ ] Countries cached for a day, and the picker built from `code`
* [ ] Currency capabilities cached, with `updatedAt` as the key
* [ ] Corridor data cached per session, not per request
* [ ] `409 currency_capability_unavailable` and `409 fx_corridor_unavailable` handled at the point of use
* [ ] A scheduled refresh, so a withdrawn currency surfaces before a customer finds it
* [ ] `GET /countries` called without an `Authorization` header and without the `/v1` prefix


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.