POST
Open a market

Authorizations

Authorization
string
header
required

A secret API key. Publishable keys cannot reach this API. A key may carry an expiry, and an expired key is refused exactly like an unknown one, with a 401 that names no reason; check the key's expires_at in the dashboard rather than inferring it from a response. When a merchant rolls a key's secret they choose a grace window of up to 3 days, and for its duration BOTH the new secret and the one it replaced authenticate, so an integration moves over on its own deploy schedule instead of at the instant the button is pressed. Move before the window closes: after it, the old secret is refused. Nothing else about this contract moves with a roll. The key keeps its id and its scopes, so the only thing an integration updates is the credential itself.

Headers

Idempotency-Key
string
required

A unique key per logical write. Replaying a request with the same key returns the first response byte for byte instead of applying the write twice.

Body

application/json

Creates one market: a named group of countries and the currency shoppers in them are charged in. Until this route existed a key could read the merchant's market map and not draw on it, so opening a market meant a human in the dashboard. THERE IS NO UPDATE AND NO DELETE ON A REGION, and neither absence is a staging decision. A DELETE is refused outright because order.region_id is ON DELETE SET NULL, so removing a market silently strips every historical order of the market it was sold into, and tax_class_rate.region_id is ON DELETE CASCADE, so it destroys every tax rate drawn on that market at the same time. There is no deleted_at and no restore for either loss, and apiscope mints no write_regions:delete for such a route to name. A PATCH is withheld separately, because re-denominating a live market is the one operation here that changes what a shopper is charged while nothing fails: prices resolve against the new currency and the storefront renders an entirely normal-looking number. Get the currency right on this call. IT DOES NOT CONVERGE PAST THE IDEMPOTENCY WINDOW. region carries no unique constraint on name or on anything else, so replaying the Idempotency-Key returns the recorded 201 while a retry with a FRESH key creates a SECOND market rather than colliding, and there is no delete to clean it up with. Reuse your own key on a retry. THE COUNTRY LIST IS ATOMIC WITH THE REGION. Every code is applied inside this call's own transaction, so a list naming one country that already belongs to another market fails the WHOLE create with 409 country_already_assigned rather than leaving a half-populated region for the merchant to find later. NO MONEY MOVES AND NO PRICE CHANGES, but what shoppers in these countries are QUOTED IN does: a region is what decides the currency at checkout. The storefront's cached region list is dropped as part of this call, so a new market is reachable immediately. Nothing is emitted: this family registers no webhook topics. currency, region, regions and country_code are refused BY NAME beside the provenance keys and is_default: the field is currency_code and it is a code rather than an object, the body IS the region and there is no bulk create, and the plural countries is where a list of codes goes. No key may be null, because no column on a region is nullable, and the whole body is capped at 8192 bytes.

name
string
required

Required. The merchant's own market label ("Europe", "North America"), and the only thing that makes a reg_01J... id legible in a UI. Blank or whitespace is a 400 rather than an unlabelled market in the dashboard.

currency_code
string
required

Required, and the field this whole family exists to answer. Three ASCII letters, an ISO 4217 code, STORED AND RETURNED LOWERCASE, so "USD" reads back "usd"; the same currency appears lowercase on orders, variants, shipping options, returns and exchanges, so a client joining on the string matches. A code this platform does not know is 400 unknown_currency, and one the STORE has not enabled is 422 currency_not_supported, because the body is fine and only the merchant's own configuration refuses it: retrying identically would fail forever until they enable it.

automatic_taxes
boolean

Optional, defaulting to TRUE, which is the column's own default. READ THIS BEFORE BUILDING ON IT: nothing in this platform's tax module reads the flag. It records the merchant's stated intent for the market, NOT a guarantee that this platform computed the tax, so a tax integration that treats true as "handled" and skips its own calculation will under-collect.

countries
string[]

The market's country membership, applied atomically with the create. Each code is two ASCII letters, ISO 3166-1 alpha-2, stored and returned lowercase; anything else is a 400. A code repeated within the list is a 400 naming it, rather than the 409 the unique index would produce, so your own duplicate is never reported as another market's claim. A COUNTRY BELONGS TO AT MOST ONE REGION PER STORE, so a code already assigned anywhere in this store is 409 country_already_assigned and nothing is written. More than 250 is 400 too_many_countries. AN OMITTED OR EMPTY LIST IS NOT "no countries". The platform seeds the currency's primary country best-effort, and SKIPS it silently when that country already belongs to another market, so a second region in the same currency can come back with countries: []. Read the response rather than assuming your input was applied verbatim.

Maximum array length: 250

Response

Created

data
object