# Regions, currencies, locales

Catalog-context primitives a storefront resolves at boot: regions feed the
pricing context (`region_id` → region currency), currencies tell you what the
store has enabled, locales drive the language switcher and the client's
`x-locale` header. All reads are anonymous. Money is EUR decimal major units
everywhere.

SDK module: `@cartbase/storefront/api/regions`.

---

## GET /api/store/regions

- **Purpose** — list the store's regions; a storefront usually picks one at
  boot (or by shopper choice) and passes its id/currency as the pricing
  context on catalog reads.
- **Auth** — anon: `x-client-id` required.
- **Request** — `GET /api/store/regions`

```jsonc
// query (all optional)
{
  "q": "bulg",             // case-insensitive substring on name
  "currency_code": "eur",  // exact match, lowercased server-side
  "limit": 50,             // 1–200, default 50
  "offset": 0
}
```

- **Response** — ordered by name. NOTE: Cartbase regions carry NO embedded
  `countries` array (divergence from Medusa's Store API — country/tax scope
  lives server-side in `tax_regions`).

```jsonc
{
  "regions": [
    {
      "id": "reg_01tst000000000000000000001",
      "name": "Bulgaria",
      "currency_code": "eur",       // lowercase — feeds the pricing context
      "automatic_taxes": true,
      "metadata": null,
      "created_at": "2026-07-01T00:00:00.000Z",
      "updated_at": "2026-07-01T00:00:00.000Z"
    }
  ],
  "count": 1,
  "offset": 0,
  "limit": 50
}
```

- **Working curl**

```bash
REGIONS=$(curl -sf "$BASE/api/store/regions" -H "x-client-id: $CLIENT_ID")
echo "$REGIONS" | grep -q '"regions"'
echo "$REGIONS" | grep -q '"count"'
REGION_ID=$(echo "$REGIONS" | grep -o '"id":"reg_[^"]*"' | head -1 | cut -d'"' -f4)
test -n "$REGION_ID"
```

- **Errors** — 400 `missing_client_id` (header absent/empty),
  400 `validation_failed` (bad limit/offset).
- **SDK** — `listRegions(client, query?)`.
- **Components** — region/currency selector (see components.md).
- **Settings** — Admin → Settings → Regions.

```bash
# Auth contract: no x-client-id → 400 missing_client_id
STATUS=$(curl -s -o /dev/null -w '%{http_code}' "$BASE/api/store/regions")
test "$STATUS" = 400
curl -s "$BASE/api/store/regions" | grep -q '"code":"missing_client_id"'
```

---

## GET /api/store/regions/:id

- **Purpose** — retrieve one region (e.g. re-hydrate the shopper's stored
  choice).
- **Auth** — anon: `x-client-id` required.
- **Request** — `GET /api/store/regions/{region_id}` — no query.
- **Response** — `{ "region": { ...same shape as the list rows... } }`
- **Working curl**

```bash
REGION=$(curl -sf "$BASE/api/store/regions/$REGION_ID" -H "x-client-id: $CLIENT_ID")
echo "$REGION" | grep -q '"region"'
echo "$REGION" | grep -q '"currency_code"'
```

- **Errors** — 404 `not_found` (unknown id, soft-deleted, or another
  tenant's region — invisible, not forbidden).

```bash
STATUS=$(curl -s -o /dev/null -w '%{http_code}' \
  "$BASE/api/store/regions/reg_doesnotexist$RUN" -H "x-client-id: $CLIENT_ID")
test "$STATUS" = 404
curl -s "$BASE/api/store/regions/reg_doesnotexist$RUN" \
  -H "x-client-id: $CLIENT_ID" | grep -q '"code":"not_found"'
```

- **SDK** — `retrieveRegion(client, regionId)`.
- **Components** — region/currency selector.
- **Settings** — Admin → Settings → Regions.

---

## GET /api/store/currencies

- **Purpose** — list the currencies ENABLED on this store (the shared
  currency catalog filtered by the store's `store_currencies` links). Use
  for formatting metadata (symbol, decimal digits).
- **Auth** — anon: `x-client-id` required.
- **Request** — query `{ code?, limit?, offset? }` (`code` exact,
  lowercased; `limit` 1–200, default 50).
- **Response** — ordered by code; a store with no enabled currencies
  returns an empty list.

```jsonc
{
  "currencies": [
    {
      "code": "eur",
      "name": "Euro",
      "symbol": "€",
      "symbol_native": "€",
      "decimal_digits": 2,
      "rounding": 0,
      "created_at": "2026-07-01T00:00:00.000Z",
      "updated_at": "2026-07-01T00:00:00.000Z"
    }
  ],
  "count": 1,
  "offset": 0,
  "limit": 50
}
```

- **Working curl**

```bash
CURRENCIES=$(curl -sf "$BASE/api/store/currencies" -H "x-client-id: $CLIENT_ID")
echo "$CURRENCIES" | grep -q '"currencies"'
echo "$CURRENCIES" | grep -q '"code":"eur"'   # the dev store enables EUR
```

- **Errors** — 400 `missing_client_id`, 400 `validation_failed`.
- **SDK** — `listCurrencies(client, query?)`.
- **Components** — price formatting helpers.
- **Settings** — Admin → Settings → Store → currencies (enable/disable +
  default).

---

## GET /api/store/currencies/:code

- **Purpose** — retrieve one currency by code (case-insensitive).
- **Auth** — anon: `x-client-id` required.
- **Request** — `GET /api/store/currencies/{code}` — no query.
- **Response** — `{ "currency": { ...same shape as the list rows... } }`
- **Working curl**

```bash
CURRENCY=$(curl -sf "$BASE/api/store/currencies/eur" -H "x-client-id: $CLIENT_ID")
echo "$CURRENCY" | grep -q '"currency"'
echo "$CURRENCY" | grep -q '"symbol"'
```

- **Errors** — 404 `not_found`.
- **CODE-TRUTH NOTE** — unlike the list, the single read is NOT filtered by
  the store's enabled set: any currency in the shared catalog resolves.
  Treat the LIST as the authority on what the store supports.
- **SDK** — `retrieveCurrency(client, code)`.
- **Components** — price formatting helpers.
- **Settings** — Admin → Settings → Store → currencies.

---

## GET /api/store/locales

- **Purpose** — the store's supported locale codes; drives the language
  switcher and the value your client sends as `x-locale`.
- **Auth** — anon: `x-client-id` required.
- **Request** — no query.
- **Response** — default locale FIRST, then alphabetical:

```jsonc
{ "locales": ["en", "bg"] }
```

- **Working curl**

```bash
LOCALES=$(curl -sf "$BASE/api/store/locales" -H "x-client-id: $CLIENT_ID")
echo "$LOCALES" | grep -q '"locales"'
echo "$LOCALES" | grep -q '"en"'
```

- **Errors** — 400 `missing_client_id`.
- **SDK** — `listLocales(client)`.
- **Components** — locale switcher; `StorefrontClient`'s `getLocale` hook.
- **Settings** — Admin → Settings → Store → locales (per-store
  `store_locales` manager; Cartbase's intentional divergence from Medusa's
  global locale catalog).
