ServicesItem

Item

A unified inventory engine: a designer-managed catalog of item definitions, per-user item instances living in containers, stacking, slots, weight and volume limits, nested bags, atomic move/split/merge, cross-user transfer, reservation holds with fencing tokens, blueprint crafting, durability, and set collections. Every example on this page was executed against a live server.

When to reach for it: game inventories with gear slots and bags, crafting economies, collectible sets with completion bonuses, warehouse or license stock that gets held and then committed.

When not to: currency balances, grants, and payouts belong to Treasury; a checkout flow that prices a basket and captures a purchase belongs to Cart.

Concepts

  • Domains partition everything. The domain is a path segment (/api/v1/items/{domain}/...) that springs into existence on first use - there is no create-domain call, and nothing crosses domains.
  • Definition vs instance - an ItemDefinition is the catalog template (stacking rules, weight, rarity, durability defaults, salvage outputs); an instance is an owned row minted from it by grant, craft, or salvage.
  • Two roles - catalog CRUD (definitions, blueprints, collections) and grant require a platform admin token or the designer capability. Everything else is owner-scoped: only the item or container owner (or an admin) may act on it.
  • Containers hold instances: slotted grids, unbounded lists, or equipment loadouts, nestable via parent_container_id, with optional weight/volume limits and tag-based accept filters.
  • Stacking - depositing a stackable definition merges into the container's existing stack up to max_stack; non-stackable definitions mint one instance per unit.
  • Reservations hold stock against a container without moving it: available = on hand - reserved. Holds expire on a TTL and every mutation is guarded by a monotonic fencing token.
  • Idempotency keys - every inventory mutation accepts one; replaying the same key returns the original result instead of acting twice.

The catalog (designer or admin)

Definitions are created from a JSON body (snug item definitions create reads it from a file):

cat > iron_ingot.json <<'EOF'
{ "name": "Iron Ingot", "tags": ["metal"], "stackable": true,
  "max_stack": 10, "weight": 2, "volume": 1, "rarity": "common" }
EOF
snug item definitions create --domain docs4-forge --file iron_ingot.json
snug item definitions list --domain docs4-forge      # supports ?q= name search + pagination
snug item definitions update --domain docs4-forge --id FdtUrUxXVxaAxUqNCLZv --file patch.json

definitions batch-create --file imports an array of definitions in one capped request. Blueprints and collections follow the same create/get/list/update/delete shape - see Crafting, durability, and collections.

Deleting a definition does not delete its instances - and because capacity checks resolve every resident item's definition, deposits into a container still holding an orphaned instance then fail with 404 definition_not_found (reproduced live). Retire instances first.

Containers

Users create their own containers; kinds are slotted, list, equipment:

snug item containers create --domain docs4-forge --kind slotted --slots 8 --max-weight 100
snug item containers create --domain docs4-forge --kind list      # unbounded
snug --output json item containers get --domain docs4-forge --container-id cont_XtmDHBhEAZwK --expand

--expand (HTTP: ?expand=contents) returns contents plus recursively expanded children - containers created with parent_container_id nest inside their parent, with depth and cycle checks. A container with accepts: {"tags_none": ["weapon"]} rejects any weapon-tagged deposit with 400 accept_filter_rejected; a deposit that would push total weight or volume past a limit is 400 capacity_exceeded; reading another user's container is 403 insufficient_permissions (all reproduced live).

Granting items

grant mints instances from a definition straight into a container - it is the faucet, so it needs the designer or admin role:

snug item grant --domain docs4-forge --definition FdtUrUxXVxaAxUqNCLZv \
  --quantity 6 --container cont_XtmDHBhEAZwK --owner docs-wave

Over HTTP, granting 2 more ingots into a container that already holds a stack of 4 merges into it - same item_id, summed quantity:

{
  "status": 201,
  "msg": "Created",
  "data": {
    "items": [
      {
        "item_id": "item_tgznyZxfemKx",
        "definition_id": "FdtUrUxXVxaAxUqNCLZv",
        "owner_id": "docs-wave",
        "container_id": "cont_XtmDHBhEAZwK",
        "quantity": 6,
        "state": "normal",
        ...
      }
    ]
  }
}

A grant that would push the stack past max_stack fails whole: 400 Stack limit exceeded: stack would reach 13, exceeding max_stack of 10.

Move, split, merge

All owner-scoped, all atomic, all accepting --idempotency-key:

snug item move  --domain docs4-forge --item item_tgznyZxfemKx --to-container cont_XtmDHBhEAZwK --to-slot 2
snug item split --domain docs4-forge --item item_tgznyZxfemKx --quantity 2
snug item merge --domain docs4-forge --source item_yWjqffKFDjnp --target item_tgznyZxfemKx

split returns both the shrunken source and the newly minted created stack. Merging stacks of different definitions is 400 cannot merge stacks of different definitions; moving onto an occupied slot is 400 slot 2 is already occupied in this container. Destination accept filters and capacity limits apply to moves too.

Transfer between users

transfer reassigns ownership and relocates in one atomic step. The destination container must belong to the recipient:

snug item transfer --domain docs4-forge --item item_tgznyZxfemKx \
  --to-user docs4-other --to-container cont_bNJNtWtLhAjn

Naming a destination the recipient does not own fails with 400 destination container is not owned by the recipient; another user touching your item gets 403 only the owner or an admin may act on this inventory (both reproduced with a second identity).

Reservations

A reservation holds quantity against a source container you own without moving anything. Each successful mutation increments the fencing token, and you must present the current token - after create it is 1, after one refresh 2, and so on:

snug item reservations create  --domain docs4-forge --container cont_XtmDHBhEAZwK \
  --definition FdtUrUxXVxaAxUqNCLZv --quantity 2 --ttl-seconds 120
snug item reservations refresh --domain docs4-forge --reservation resv_yMsTBqTmDRVG --fencing-token 1 --ttl-seconds 300
snug item reservations shrink  --domain docs4-forge --reservation resv_yMsTBqTmDRVG --fencing-token 2 --quantity 1
snug item reservations commit  --domain docs4-forge --reservation resv_yMsTBqTmDRVG --fencing-token 3
snug item reservations release --domain docs4-forge --reservation resv_ZWsbNrDYyEuh --fencing-token 1

Reserving more than is available fails with 400 only 0 of ... available to reserve; a wrong token is 409 Stale fencing token (expected 1, got 99); commit debits the held quantity from the container's stacks; an expired hold simply disappears - later operations on it return 404 reservation_not_found. The default TTL is 15 minutes, capped at 24 hours.

Crafting, durability, collections

Blueprint crafting (including discovery by contents), wear/repair/salvage, and set collections with tier bonuses have their own page: Crafting, durability, and collections.

Limits and configuration

Container slots (1000), nesting depth (16), reservation TTLs, idempotency retention, and the batch-create cap (500) are tunable via the ITEM_* variables in the Item CONFIG reference.

Reference

On this page