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
| Method | Path | Description |
|---|---|---|
| GET | /client/inventory | User's inventory with catalog-item info; quantity > 0. |
| GET | /client/inventory/:itemId | One of the user's inventory rows, by its id. |
| POST | /client/inventory/purchase | Purchase with virtual currency (atomic, idempotent per concurrency). |
| POST | /client/inventory/:itemId/consume | Decrement quantity on a consumable. |
GET /client/inventory
Response 200
Errors
500 FETCH_FAILED.
Try it:
/client/inventorycurl -X GET 'https://api.amba.dev/v1/client/inventory'Curl:
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
Errors
400 INVALID_ITEM_ID—itemIdis not a uuid.404 NOT_FOUND— the caller has no inventory row with thisid.503 TENANT_UNAVAILABLE— the app's database is unavailable.500 FETCH_FAILED.
Try it:
/client/inventory/%7B%7BitemId%7D%7Dcurl -X GET 'https://api.amba.dev/v1/client/inventory/%7B%7BitemId%7D%7D'Curl:
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)
| Field | Type | Required | Description |
|---|---|---|---|
catalog_item_id | uuid | yes | Item to buy. |
currency_code | string | yes | Currency to spend. |
store_id | uuid | no | If present, the store's override_prices and max_purchases_per_user are applied. Required for an item an active store limits per user. |
quantity | 1 | no | A purchase buys one item. Leave it out or send 1; call the route once per item. |
Response 200
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—quantityis present and is not1. Nothing is charged.400 INVALID_STORE_ID—store_idis 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 suppliedstore_id: not listed, the store is inactive, the user is outside the store's segment, or the listing is outside itsavailable_from/available_untilwindow.403 STORE_ACCESS_REQUIRED— nostore_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— nostore_id, and an active store lists the item withmax_purchases_per_user. Buy it through that store: pass itsstore_id, or callPOST /client/stores/:storeKey/purchase.503 TENANT_UNAVAILABLE— the app's database is unavailable.500 PURCHASE_FAILED.
Try it:
/client/inventory/purchasecurl -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"
}'Curl:
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
Defaults to quantity = 1 if body is empty or missing the field.
Response 200
Every inventory row carries sku (the catalog item's key) and metadata (the row's instance_data).
Errors
400 INVALID_ITEM_ID—itemIdis not a uuid.400 INVALID_QUANTITY—quantityis not a whole number of at least 1.400 NOT_CONSUMABLE— the catalog item is notitem_type = 'consumable'.400 INSUFFICIENT_QUANTITY— user doesn't own enough to consume.500 CONSUME_FAILED.
Try it:
/client/inventory/%7B%7BitemId%7D%7D/consumecurl -X POST 'https://api.amba.dev/v1/client/inventory/%7B%7BitemId%7D%7D/consume' \
-H 'Content-Type: application/json' \
-d '{
"quantity": 1
}'Curl: