Amba

Inventory

User inventory, atomic virtual-currency purchases, consumable consumption.

All mutations run inside a transaction with SELECT ... FOR UPDATE on user_balances so concurrent purchases cannot double-spend.

Endpoints

MethodPathDescription
GET/client/inventoryUser's inventory with catalog-item info; quantity > 0.
GET/client/inventory/:itemIdOne of the user's inventory rows, by its id.
POST/client/inventory/purchasePurchase with virtual currency (atomic, idempotent per concurrency).
POST/client/inventory/:itemId/consumeDecrement quantity on a consumable.

GET /client/inventory

Response 200

{
  "data": [
    {
      "id": "…",
      "app_user_id": "…",
      "catalog_item_id": "…",
      "sku": "potion",
      "quantity": 3,
      "metadata": {},
      "instance_data": {},
      "acquired_via": "store_purchase",
      "acquired_at": "…",
      "catalog_items": {
        "key": "…",
        "name": "…",
        "description": "…",
        "icon_url": "…",
        "item_type": "consumable",
        "category": "…"
      }
    }
  ]
}

Errors

  • 500 FETCH_FAILED.

Try it:

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

Curl:

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

GET /client/inventory/:itemId

One inventory row, by the id that GET /client/inventory returns. Only the caller's own rows are found; any other id answers 404 NOT_FOUND. A row whose quantity has reached 0 is still returned, with quantity: 0.

Response 200

{
  "data": {
    "id": "…",
    "app_user_id": "…",
    "catalog_item_id": "…",
    "sku": "potion",
    "quantity": 3,
    "metadata": {},
    "instance_data": {},
    "acquired_via": "store_purchase",
    "acquired_at": "…",
    "catalog_items": {
      "key": "…",
      "name": "…",
      "description": "…",
      "icon_url": "…",
      "item_type": "consumable",
      "category": "…"
    }
  }
}

Errors

  • 400 INVALID_ITEM_ID — itemId is not a uuid.
  • 404 NOT_FOUND — the caller has no inventory row with this id.
  • 503 TENANT_UNAVAILABLE — the app's database is unavailable.
  • 500 FETCH_FAILED.

Try it:

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

Curl:

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

POST /client/inventory/purchase

Atomic: price resolution → balance check → deduction → ledger insert → inventory grant. Bundles are expanded — each bundle content item is granted separately.

Request (PurchaseItemInput)

FieldTypeRequiredDescription
catalog_item_iduuidyesItem to buy.
currency_codestringyesCurrency to spend.
store_iduuidnoIf present, the store's override_prices and max_purchases_per_user are applied. Required for an item an active store limits per user.
quantity1noA purchase buys one item. Leave it out or send 1; call the route once per item.

Response 200

{
  "data": {
    "purchased": true,
    "currency_code": "gems",
    "price": 100,
    "balance_after": 900,
    "id": "…",
    "catalog_item_id": "…",
    "sku": "potion",
    "quantity": 3,
    "acquired_at": "…",
    "metadata": {}
  }
}

A single-item purchase also returns the inventory row it added to (id, catalog_item_id, sku, quantity after the purchase, acquired_at, metadata). A bundle purchase grants several rows and returns the four receipt fields alone. A store listing's override price, when set, must be a whole number (a JSON number or a digit string); a null or empty entry uses the catalog price, and any other value answers 400 PRICE_NOT_CONFIGURED.

Errors

  • 400 PRICE_NOT_CONFIGURED — no price set for the (item, currency) pair.
  • 400 QUANTITY_NOT_SUPPORTED — quantity is present and is not 1. Nothing is charged.
  • 400 INVALID_STORE_ID — store_id is present but not a uuid.
  • 400 MAX_PURCHASES_REACHED — store listing's per-user cap reached.
  • 400 INSUFFICIENT_BALANCE — user's currency balance is less than the price.
  • 404 ITEM_NOT_FOUND — no active catalog item matches.
  • 404 NOT_FOUND — the item is not on sale in the supplied store_id: not listed, the store is inactive, the user is outside the store's segment, or the listing is outside its available_from / available_until window.
  • 403 STORE_ACCESS_REQUIRED — no store_id, and every active store that lists the item targets a segment the user is not in, so the item is exclusive to those segments. See Segment targeting.
  • 409 STORE_PURCHASE_REQUIRED — no store_id, and an active store lists the item with max_purchases_per_user. Buy it through that store: pass its store_id, or call POST /client/stores/:storeKey/purchase.
  • 503 TENANT_UNAVAILABLE — the app's database is unavailable.
  • 500 PURCHASE_FAILED.

Try it:

POST/client/inventory/purchase
client auth
curl -X POST 'https://api.amba.dev/v1/client/inventory/purchase' \
  -H 'Content-Type: application/json' \
  -d '{
  "catalog_item_id": "<catalog_item_id>",
  "currency_code": "gems"
}'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X POST "${BASE_URL}/client/inventory/purchase" \
  -H "X-Api-Key: ${CLIENT_API_KEY}" \
  -H "Authorization: Bearer ${SESSION_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{"catalog_item_id":"<catalog_item_id>","currency_code":"gems"}'

POST /client/inventory/:itemId/consume

Decrement quantity on a consumable. itemId is the catalog item id, or the id of the caller's inventory row for it (what GET /client/inventory returns).

Request

{ "quantity": 1 }

Defaults to quantity = 1 if body is empty or missing the field.

Response 200

{
  "data": {
    "id": "…",
    "app_user_id": "…",
    "catalog_item_id": "…",
    "sku": "potion",
    "quantity": 2,
    "metadata": {},
    "acquired_at": "…"
  }
}

Every inventory row carries sku (the catalog item's key) and metadata (the row's instance_data).

Errors

  • 400 INVALID_ITEM_ID — itemId is not a uuid.
  • 400 INVALID_QUANTITY — quantity is not a whole number of at least 1.
  • 400 NOT_CONSUMABLE — the catalog item is not item_type = 'consumable'.
  • 400 INSUFFICIENT_QUANTITY — user doesn't own enough to consume.
  • 500 CONSUME_FAILED.

Try it:

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

Curl:

curl -X POST "${BASE_URL}/client/inventory/{itemId}/consume" \
  -H "X-Api-Key: ${CLIENT_API_KEY}" \
  -H "Authorization: Bearer ${SESSION_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{"quantity":1}'

On this page