# 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
