Amba

Stores

Active stores, their listings with the price each currency is sold at, purchase options, and store purchases.

Listings are filtered by availability windows (available_from / available_until) server-side so clients only see what's currently sellable.

A store with a segment_id is open only to the members of that segment. Membership is what the segment's last evaluation recorded: a user it has not evaluated yet (a new user, or a segment created or changed since its last run) does not see the store until the next evaluation adds them, within 15 minutes of a change to your users' data (see Evaluation cadence). For anyone outside the segment the store is absent from the list and every route below answers 404 NOT_FOUND, the same as for a store that does not exist.

A listing's override_prices replaces the catalog price in a currency. The store read, the purchase options and the purchase all use the same price: the override when the listing sets one for that currency, the catalog price otherwise.

Endpoints

MethodPathDescription
GET/client/storesActive stores open to the user.
GET/client/stores/:storeIdStore + available listings (joined to catalog items, with the store price).
GET/client/stores/:storeKey/optionsOne purchase option per (listing, currency), at the store price.
POST/client/stores/:storeKey/purchaseBuy a purchase option with the user's currency balance.

:storeKey is the store's id (the key field of a store equals its id).

Example: buy from a store

With the SDK:

const [store] = await Amba.stores.list();
const options = await Amba.stores.getPurchaseOptions(store.key);
const option = options.find((o) => o.currency === 'coins');
if (option) {
  // option.price_cents is what the purchase charges, in option.currency.
  const purchase = await Amba.stores.purchase(store.key, option.id, {});
}

With curl (jq picks the ids):

STORE_ID=$(curl -s "${BASE_URL}/client/stores" \
  -H "X-Api-Key: ${CLIENT_API_KEY}" \
  -H "Authorization: Bearer ${SESSION_TOKEN}" | jq -r '.data[0].id')
 
OPTION_ID=$(curl -s "${BASE_URL}/client/stores/${STORE_ID}/options" \
  -H "X-Api-Key: ${CLIENT_API_KEY}" \
  -H "Authorization: Bearer ${SESSION_TOKEN}" | jq -r '.data[0].id')
 
curl -X POST "${BASE_URL}/client/stores/${STORE_ID}/purchase" \
  -H "X-Api-Key: ${CLIENT_API_KEY}" \
  -H "Authorization: Bearer ${SESSION_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d "{\"purchase_option_id\":\"${OPTION_ID}\"}"

GET /client/stores

Response 200

{
  "data": [
    {
      "id": "…",
      "key": "…",
      "name": "…",
      "description": "…",
      "is_active": true,
      "segment_id": null
    }
  ]
}

Try it:

GET/client/stores
client auth
curl -X GET 'https://api.amba.dev/v1/client/stores'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X GET "${BASE_URL}/client/stores" \
  -H "X-Api-Key: ${CLIENT_API_KEY}" \
  -H "Authorization: Bearer ${SESSION_TOKEN}"

GET /client/stores/:storeId

Listings of deactivated catalog items are left out. Each listing's prices is what the store charges in each currency, sorted by currency code: every catalog price with the listing's override_prices applied (the price /options shows and a purchase charges), plus any currency only the override names, which POST /client/inventory/purchase sells with the store's store_id. A currency whose override is not a whole number is left out. catalog_items.catalog_item_prices keeps the catalog prices.

Response 200

{
  "data": {
    "store": { "id": "…", "key": "…", "name": "…" },
    "listings": [
      {
        "id": "…",
        "store_id": "…",
        "catalog_item_id": "…",
        "sort_order": 0,
        "override_prices": { "gems": 80 },
        "max_purchases_per_user": null,
        "prices": [{ "currency_code": "gems", "price": 80 }],
        "catalog_items": {
          "key": "…",
          "name": "…",
          "icon_url": "…",
          "item_type": "…",
          "category": "…",
          "tags": [],
          "metadata": {},
          "catalog_item_prices": [{ "currency_code": "gems", "price": 100 }]
        }
      }
    ]
  }
}

Errors

  • 400 INVALID_STORE_ID — storeId is not a uuid.
  • 404 NOT_FOUND — no such store, the store is inactive, or the user is outside its segment.
  • 500 FETCH_FAILED.

Try it:

GET/client/stores/%7B%7BstoreId%7D%7D
client auth
curl -X GET 'https://api.amba.dev/v1/client/stores/%7B%7BstoreId%7D%7D'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X GET "${BASE_URL}/client/stores/{storeId}" \
  -H "X-Api-Key: ${CLIENT_API_KEY}" \
  -H "Authorization: Bearer ${SESSION_TOKEN}"

GET /client/stores/:storeKey/options

One option per listing and currency the item has a catalog price in, for listings of active catalog items inside their availability window, ordered by sort_order. id is the option's purchase_option_id. price_cents is the amount of currency a purchase of the option charges: the listing's override for that currency when set, the catalog price otherwise. An option whose override is not a whole number can't be bought, so it is left out. period is always null (store items are one-time purchases).

Response 200

{
  "data": [
    {
      "id": "…",
      "store_id": "…",
      "sku": "gems_100",
      "name": "100 Gems",
      "price_cents": 80,
      "currency": "coins",
      "period": null,
      "metadata": {}
    }
  ]
}

Errors

  • 404 NOT_FOUND — no such store, the store is inactive, or the user is outside its segment.
  • 500 FETCH_FAILED.

Try it:

GET/client/stores/%7B%7BstoreKey%7D%7D/options
client auth
curl -X GET 'https://api.amba.dev/v1/client/stores/%7B%7BstoreKey%7D%7D/options'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X GET "${BASE_URL}/client/stores/{storeKey}/options" \
  -H "X-Api-Key: ${CLIENT_API_KEY}" \
  -H "Authorization: Bearer ${SESSION_TOKEN}"

POST /client/stores/:storeKey/purchase

Buys one purchase option with the user's balance in the option's currency, in one transaction: the store's rules are checked again, then the listing's max_purchases_per_user, then the balance; the balance is debited by the option's price_cents, the spend is logged, and the item is granted (a bundle grants each of its contents). receipt is accepted and ignored; real-money purchases go through the platform store flows.

Request

FieldTypeRequiredDescription
purchase_option_iduuidyesAn option id from GET /client/stores/:storeKey/options.
receiptanynoIgnored.

Response 200

{
  "data": {
    "id": "…",
    "user_id": "…",
    "purchase_option_id": "…",
    "status": "completed",
    "purchased_at": "2026-10-15T12:00:00.000Z",
    "receipt": {},
    "store_id": "…",
    "listing_id": "…"
  }
}

id is the user's inventory row for the item.

Errors

  • 400 INVALID_PURCHASE_OPTION_ID — purchase_option_id is missing or not a uuid.
  • 400 MAX_PURCHASES_REACHED — the user has bought the item as many times as the listing's max_purchases_per_user allows (purchases straight from the catalog count too).
  • 400 INSUFFICIENT_BALANCE — the user's balance in the option's currency is below its price.
  • 400 PRICE_NOT_CONFIGURED — the listing's override for this currency is not a whole number.
  • 404 NOT_FOUND — no such store, the store is inactive, the user is outside its segment, or the option is not on sale in this store (not listed, a deactivated item, or outside the listing's availability window).
  • 503 TENANT_UNAVAILABLE — your database is unavailable.
  • 500 PURCHASE_FAILED.

Try it:

POST/client/stores/%7B%7BstoreKey%7D%7D/purchase
client auth
curl -X POST 'https://api.amba.dev/v1/client/stores/%7B%7BstoreKey%7D%7D/purchase' \
  -H 'Content-Type: application/json' \
  -d '{
  "purchase_option_id": "<purchase_option_id>"
}'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X POST "${BASE_URL}/client/stores/{storeKey}/purchase" \
  -H "X-Api-Key: ${CLIENT_API_KEY}" \
  -H "Authorization: Bearer ${SESSION_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{"purchase_option_id":"<purchase_option_id>"}'

On this page