Accounts & markets

The two top-level entities of multi-tenancy.

Accounts

An account is one e-shop. Every domain entity (product, order, customer, ...) lives under exactly one account. RLS isolates them in Postgres — even a bug in application code can't leak across tenants.

{
  "account": {
    "id": "308...",
    "workspace_id": "uuid-...",
    "handle": "my-shop",
    "name": "My Shop",
    "default_currency": "CZK",
    "default_locale": "cs-CZ",
    "default_timezone": "Europe/Prague",
    "status": "active",
    "version": 1
  }
}

The handle is globally unique (across all accounts on the platform). It's used in admin URLs:

/admin/2026-01/{handle}/products.json

Update with PUT /admin/2026-01/accounts/{handle}.json (requires expected_version for optimistic concurrency). Soft-delete with DELETE (archived_at set; rows persist for audit).

Markets

A market is one regional surface within an account: a unique combination of currency, locale, country, optional domain. Multiple markets per account let you sell in CZ + EU + US with different prices, languages, and tax rules from the same product catalog.

{
  "market": {
    "id": "308...",
    "handle": "cz",
    "name": "Česká republika",
    "currency": "CZK",
    "locale": "cs-CZ",
    "country_code": "CZ",
    "domain": null,
    "prices_include_tax": true,
    "status": "active"
  }
}

prices_include_tax flips the storefront display behavior (gross vs net pricing). Czech B2C is true, German B2B might be false.

Per-variant pricing rows (variant_pricing) are keyed by (variant, market) — explicit prices per region, no auto-FX.

Cart and checkout always carry a market

Every cart and checkout pins a single market_id. This decides:

  • Which currency the line prices are charged in
  • Which tax rates apply (per tax_rates rows for that market)
  • Which shipping zones / methods are eligible
  • Which locale is used for emails (unless overridden)

Trying to add a variant without pricing in the cart's market returns 409 (multi-currency cart reject — design decision Q8).

Relevant endpoints

VerbPathDescription
POST/admin/2026-01/accounts.jsonCreate account
GET/admin/2026-01/accounts.jsonList accounts (workspace-filtered)
GET/admin/2026-01/accounts/{id_or_handle}.jsonGet account
PUT/admin/2026-01/accounts/{id_or_handle}.jsonUpdate (optimistic version)
DELETE/admin/2026-01/accounts/{id_or_handle}.jsonSoft-delete
POST/admin/2026-01/{handle}/markets.jsonCreate market
GET/admin/2026-01/{handle}/markets.jsonList markets

Next: Products & variants.