# InventoryApi document shapes This service stores one MongoDB document per inventory item record. Inbound JSON documents - CreateItemDefinitionRequest (`POST /api/inventory/item-definitions`) ```json { "itemKey": "wood", "displayName": "Wood", "stackable": true, "maxStackSize": 20, "category": "resource", "equipSlot": null } ``` - UpdateItemDefinitionRequest (`PUT /api/inventory/item-definitions/{itemKey}`) ```json { "displayName": "Wood", "stackable": true, "maxStackSize": 20, "category": "resource", "equipSlot": null } ``` - GrantInventoryItemRequest (`POST /api/inventory/by-owner/{ownerType}/{ownerId}/grant`) ```json { "itemKey": "string", "quantity": 1, "preferredSlot": 0 } ``` `preferredSlot` is optional. If omitted, the service finds a valid destination slot. - MoveInventoryItemRequest (`POST /api/inventory/by-owner/{ownerType}/{ownerId}/move`) ```json { "itemId": "uuid-string", "toSlot": 1, "quantity": 1 } ``` `quantity` is optional. If omitted, move the full stack. - TransferInventoryItemRequest (`POST /api/inventory/transfer`) ```json { "itemId": "uuid-string", "fromOwnerType": "character", "fromOwnerId": "string", "toOwnerType": "location", "toOwnerId": "string", "toSlot": 0, "quantity": 1 } ``` `toSlot` and `quantity` are optional. If omitted, the service finds a valid destination and transfers the full stack. - ConsumeInventoryItemRequest (`POST /api/inventory/items/{itemId}/consume`) ```json { "quantity": 1 } ``` - EquipInventoryItemRequest (`POST /api/inventory/items/{itemId}/equip`) ```json { "ownerId": "string", "equipmentSlot": "weapon" } ``` Only valid for items currently owned by a character inventory. - UnequipInventoryItemRequest (`POST /api/inventory/items/{itemId}/unequip`) ```json { "ownerId": "string", "preferredSlot": 0 } ``` Only valid for items currently equipped by a character. Stored documents (MongoDB) - ItemDefinition ```json { "itemKey": "wood", "displayName": "Wood", "stackable": true, "maxStackSize": 20, "category": "resource", "equipSlot": null, "createdUtc": "string (ISO-8601 datetime)", "updatedUtc": "string (ISO-8601 datetime)" } ``` - InventoryItem ```json { "id": "string (UUID)", "itemKey": "wood", "quantity": 12, "ownerType": "character", "ownerId": "string (ObjectId or stable external id)", "ownerUserId": "string (ObjectId from auth token, null for public world owners)", "slot": 0, "equippedSlot": null, "createdUtc": "string (ISO-8601 datetime)", "updatedUtc": "string (ISO-8601 datetime)" } ``` Equipped character item example: ```json { "id": "string (UUID)", "itemKey": "pistol", "quantity": 1, "ownerType": "character", "ownerId": "string (Character ObjectId)", "ownerUserId": "string (ObjectId from auth token)", "slot": null, "equippedSlot": "weapon", "createdUtc": "string (ISO-8601 datetime)", "updatedUtc": "string (ISO-8601 datetime)" } ``` Location stack example: ```json { "id": "string (UUID)", "itemKey": "wood", "quantity": 12, "ownerType": "location", "ownerId": "string (Location ObjectId)", "ownerUserId": null, "slot": 3, "equippedSlot": null, "createdUtc": "string (ISO-8601 datetime)", "updatedUtc": "string (ISO-8601 datetime)" } ``` Outbound JSON documents - ItemDefinitionResponse ```json { "itemKey": "wood", "displayName": "Wood", "stackable": true, "maxStackSize": 20, "category": "resource", "equipSlot": null, "updatedUtc": "string (ISO-8601 datetime)" } ``` - InventoryItemResponse ```json { "id": "string (UUID)", "itemKey": "wood", "quantity": 12, "ownerType": "character", "ownerId": "string", "slot": 0, "equippedSlot": null, "updatedUtc": "string (ISO-8601 datetime)" } ``` - InventoryOwnerResponse (`GET /api/inventory/by-owner/{ownerType}/{ownerId}`) ```json { "ownerType": "character", "ownerId": "string", "items": [ { "id": "string (UUID)", "itemKey": "wood", "quantity": 12, "ownerType": "character", "ownerId": "string", "slot": 0, "equippedSlot": null, "updatedUtc": "string (ISO-8601 datetime)" } ] } ``` - TransferInventoryResponse (`POST /api/inventory/transfer`) ```json { "movedItemId": "string (UUID)", "fromOwnerType": "character", "fromOwnerId": "string", "toOwnerType": "location", "toOwnerId": "string", "fromItems": [], "toItems": [] } ``` Validation rules - `itemKey` must map to an existing item definition before item instances can be created - `ownerType` must be a supported container type - `ownerId` must map to an existing owning entity where applicable - non-`SUPER` callers may only access owned character items unless explicit gameplay rules allow a world container read/write - `quantity` must be greater than `0` - non-stackable item definitions must have `maxStackSize = 1` - non-stackable items must have `quantity = 1` - equipped items must have `slot = null` - unequipped bag items must have `equippedSlot = null` - an item must not have both `slot` and `equippedSlot` populated - slot occupancy must be unique for `(ownerType, ownerId, slot)` where `slot != null` - equipment occupancy must be unique for `(ownerType, ownerId, equippedSlot)` where `equippedSlot != null` Recommended indexes - unique on `itemKey` for item definitions - index on `(ownerType, ownerId)` - unique on `(ownerType, ownerId, slot)` for bag slots - unique on `(ownerType, ownerId, equippedSlot)` for equipped slots - index on `itemKey` for inventory items Behavior rules - moving a full non-stackable item should update its owner and slot in place - moving part of a stack should split the stack and create a new item record with a new UUID for the moved quantity - moving into a compatible stack should merge quantities and delete or reduce the source record - cross-owner transfer should be transactional when it mutates multiple records - auctions should reference `itemId` values directly instead of copying item state into the auction document - the service does not auto-seed item definitions; `ItemDefinitions` must be populated explicitly