Markets
A market is a distinct business context inside a tenant: a country, a region, or a storefront segment such as B2C versus B2B. It has a base currency, the locales it renders in, the currencies it trades in, and its own tax classes.
Base path: https://api.revenexx.com/v1/markets.
The row
| Field | Meaning |
|---|---|
code | Stable identifier. Most routes on this page accept it in place of the uuid. |
name, labels | Display name, and localized names. |
currency | The market's base currency. |
status | active or inactive. |
is_default | A tenant has one default market — what every call naming none falls back to. |
position | Sort order. |
Do not create a market — clone one
curl -X POST https://api.revenexx.com/v1/markets \
-H "X-Revenexx-Tenant: <TENANT_SLUG>" \
-H "X-Revenexx-Api-Key: rvxk_..." \
-H "Content-Type: application/json" \
-d '{"code":"at","name":"Austria","currency":"EUR"}'
POST /v1/markets needs a code and a name, and it leaves a row that cannot serve anybody: no locale to render in, no currency registered to price in, no rate to tax with.
curl -X POST "https://api.revenexx.com/v1/markets/de/clone" \
-H "X-Revenexx-Tenant: <TENANT_SLUG>" \
-H "X-Revenexx-Api-Key: rvxk_..." \
-H "Content-Type: application/json" \
-d '{"code":"at","name":"Austria"}'
Clone takes the source market's locales, traded currencies and tax classes with it in a single call, so what comes back is a market that can trade. The path id is the source. It resolves by uuid or by market code.
The three sub-resources
Each is a full CRUD collection under a market, and each carries a code, an is_default flag and a position.
| Sub-resource | Path |
|---|---|
| Locales | /v1/markets/{market_id}/locales |
| Currencies | /v1/markets/{market_id}/currencies |
| Tax classes | /v1/markets/{market_id}/tax_classes |
market_id is filled from the route path on write and never read out of the body. Deleting the market cascades to all three.
Locales
{ "code": "de-AT", "language": "de", "country": "AT", "is_default": true, "position": 0 }
code is language-COUNTRY, unique per market, and it is the key a translation is stored under. language and country are stored separately so a client can group markets by language without parsing.
country here is a spelling of the language, not a shipping destination — a market may register de-AT and ship nowhere near Austria.
is_default is the locale a storefront renders when the request asks for none. At most one per market; where none carries the flag, the lowest position wins.
Currencies
{ "code": "CHF", "is_default": false, "position": 10 }
One entry per currency this market trades in, as opposed to the single base currency on the market row. is_default is what is offered first to a buyer stating no preference, and it should be the market's base currency — readiness reports it when it is not.
Tax classes
{ "code": "reduced", "name": "Reduced rate", "rate": 7, "is_default": false,
"labels": { "de-DE": "Ermäßigter Satz" } }
code is the rate bucket a product or a shipping method is assigned to — standard, reduced, zero — and other apps resolve against it by code. A product's tax_class column and a shipping method's tax class are both plain text naming one of these.
rate is in percent, 0–100. 20 means 20 %, not 0.2. Whether a stored price already contains it is a separate question, answered per price list.
is_default is applied to a line naming none. labels is a flat {locale: label} map for storefronts and invoices.
The storefront bootstrap
curl "https://api.revenexx.com/v1/markets/{id}/context" \
-H "X-Revenexx-Tenant: <TENANT_SLUG>" \
-H "X-Revenexx-Api-Key: rvxk_..."
One call instead of five, and — more to the point — one place the resolution rules live rather than a slightly different copy in every storefront. It answers:
| Key | Is |
|---|---|
market | The market row. |
locales, currencies, tax_classes | All three collections in position order, capped at 200 each. |
default_locale | Which locale to render in, and source naming where that answer came from — a locale flagged default, the first by position, or a tenant fallback. |
locale_policy | The key a client writes a translation under, and the order it reads — resolved, not named. |
pricing | Whether a stored price in this market is net or gross. |
readiness | Whether any of it is trustworthy. |
400, not a lookup.locales being empty is a real answer — read default_locale before assuming a language.
For a surface that stands in no market at all — a Cockpit editing a tenant-level default rather than one market's value — GET /v1/markets/locale-policy answers the union of every market's locales, each already resolved to the key it is written under, and each naming the markets that asked for it. A tenant-level value has to be readable by every market, and an editor listing six inputs without saying who needs them invites translations nobody will read.
Readiness is a diagnosis, not a yes/no
curl "https://api.revenexx.com/v1/markets/at/readiness" \
-H "X-Revenexx-Tenant: <TENANT_SLUG>" \
-H "X-Revenexx-Api-Key: rvxk_..."
Every check runs on every call and comes back with its own severity.
- Blocking turns
readyfalse: no currency registered means nothing to price in; no tax class means nothing to tax with. - Warning leaves the market serviceable: no locale of its own, while the tenant declares a fallback that covers for it.
Resolves by uuid or market code.
Repairing a market
curl -X POST "https://api.revenexx.com/v1/markets/at/backfill" \
-H "X-Revenexx-Tenant: <TENANT_SLUG>" \
-H "X-Revenexx-Api-Key: rvxk_..." \
-H "Content-Type: application/json" \
-d '{"source":"de"}'
Repairs the market in the path out of a source market that is already right. The two are compared by code, collection by collection, and only the codes the target does not already carry are added — so a locale, currency or tax class it already holds is left exactly as the merchant left it, rate included, and is never overwritten.
Idempotent: running it twice adds nothing the second time. Both ids resolve by uuid or code.
Moving the default flag
curl -X POST "https://api.revenexx.com/v1/markets/at/make-default" \
-H "X-Revenexx-Tenant: <TENANT_SLUG>" \
-H "X-Revenexx-Api-Key: rvxk_..." \
-H "Content-Type: application/json" \
-d '{}'
One call promotes the market in the path and demotes whoever held the flag, in the same operation. Doing it from a client as promote-then-demote leaves two defaults when the second write does not land, and none when the first does.
It writes once per row that was actually wrong and touches nothing else. It answers the market plus the codes it demoted; repeating the call writes nothing. Accepts an id or a code.
Where to go next
- Channels — the other scoping axis.
- Price lists — what resolves against a currency and a tax class.
- Settings — per-market configuration, and the header's effect on writes.
- Market concept — making your own app's entities market-scoped.