Home › Documentation › Server Hooks · origen_inventory
Hooks let external resources subscribe to inventory events on the server side and either inspect the payload, mutate it, or cancel the action by returning false.
The hook system is exposed through two exports — registerHook and removeHooks — and is symmetrical with the client-side hook API.
All hooks documented on this page run server-side. For client-side hooks (world points, UI events) see Custom · Client-side hooks.
registerHook(event, callback, options?) → string (id)Registers a server-side hook. Returns the internal hookId so you can remove it later.
local hookId = exports.origen_inventory:registerHook('createItem', function(payload)
return payload.metadata
end)| Parameter | Type | Description |
|---|---|---|
event | string | Hook event name (see list below). |
callback | function | Invoked with the hook payload. |
options | table? | Optional filters (itemFilter, inventoryFilter, typeFilter, debugger). |
options)exports.origen_inventory:registerHook('swapItems', function(payload)
-- only fires for these inventories / items / types
end, {
itemFilter = { tosti = true, water_bottle = true },
inventoryFilter = { '^stash%-', '^trunk%-' },
typeFilter = { player = true, stash = true },
debugger = true, -- prints execution time and resource origin
})| Filter | Type | Description |
|---|---|---|
itemFilter | table<string, true> | Only fire when the involved item name is in this set. |
inventoryFilter | string[] | Lua patterns matched against the source/target inventory id. |
typeFilter | table<string, true> | Restrict to specific inventory/shop types (player, stash, shop, drop, ...). |
debugger | boolean | Logs every hook call (resource, event, slot, execution time). |
removeHooks(id?) → voidRemoves hooks belonging to the calling resource. Pass an id to remove a single hook, or omit it to remove every hook registered by the resource.
exports.origen_inventory:removeHooks(hookId) -- single hook
exports.origen_inventory:removeHooks() -- every hook from this resourceHooks are also cleaned up automatically on onResourceStop, so you only need to call removeHooks for hot-swapped registrations.
Most server hooks honour a cancellation contract:
false → the inventory aborts the action (item is not added/swapped/bought/etc.).The only exception is createItem, which is mutating: you must return the (possibly modified) metadata table.
createItemFires when an item is being added to an inventory and metadata is being initialized. Mutating — must return metadata.
Payload
| Field | Type | Description |
|---|---|---|
inventoryId | number|string | Inventory receiving the item (player src, stash id, plate, ...). |
inventoryType | string | player, stash, trunk, glovebox, ... |
item | table | Item definition being created. |
count | number | Amount being added. |
metadata | table | Mutable metadata table. |
exports.origen_inventory:registerHook('createItem', function(payload)
if payload.item.name == 'paperbag' and not payload.metadata.bagId then
payload.metadata.bagId = math.random(1000, 9999)
end
return payload.metadata -- MUST return metadata
end)If you forget to return metadata, the item will be created with an empty metadata table.
itemCreatedFires after an item has been added to an inventory. Read-only / observational — return value is ignored.
Payload
| Field | Type | Description |
|---|---|---|
inventoryId | number|string | Inventory id. |
inventoryType | string | Inventory type. |
item | table | The item slot just created (with slot, count, metadata). |
count | number | Amount added. |
exports.origen_inventory:registerHook('itemCreated', function(payload)
print(('Item %s x%s added to %s'):format(payload.item.name, payload.count, payload.inventoryId))
end)itemUsedFires after a slot is successfully used. Observational.
Payload
| Field | Type | Description |
|---|---|---|
source | number | Player server id. |
inventoryId | number|string | Inventory the slot belongs to. |
inventoryType | string | Inventory type. |
item | table | The item slot used. |
slot | number | Slot id. |
exports.origen_inventory:registerHook('itemUsed', function(payload)
if payload.item.name == 'lockpick' then
-- log lockpick usage
end
end)useSlotFires before a slot is used. Return false to cancel the use.
Payload
| Field | Type | Description |
|---|---|---|
source | number | Player server id. |
inventoryId | number|string | Inventory id. |
inventoryType | string | Inventory type. |
item | table | The slot being used. |
slot | number | Slot id. |
exports.origen_inventory:registerHook('useSlot', function(payload)
if payload.item.name == 'weapon_pistol' and IsRestrictedZone(payload.source) then
return false -- cancel use
end
end)swapItemsFires before any move / swap / split / give between two inventories (player-to-player, stash transfers, trunk, drops, etc.). Return false to cancel the transfer.
Payload
| Field | Type | Description |
|---|---|---|
source | number? | Player server id (when applicable). |
action | string | move, swap, split, give. |
fromInventory | string|number | Source inventory id. |
fromType | string | Source inventory type (player, stash, ...). |
fromSlot | table | Slot data being moved. |
toInventory | string|number | Destination inventory id. |
toType | string | Destination inventory type. |
toSlot | table? | Slot data being swapped (only present on swap/merge). |
count | number | Amount being moved. |
exports.origen_inventory:registerHook('swapItems', function(payload)
-- block moving illegal items into a public trunk
if payload.toType == 'trunk' and payload.fromSlot.name == 'weapon_rpg' then
return false
end
end)createDropFires before a ground drop is created. Return false to block the drop.
Payload
| Field | Type | Description |
|---|---|---|
source | number | Player creating the drop. |
coords | vector3 | Drop coordinates. |
item | table | Slot data being dropped. |
count | number | Amount being dropped. |
exports.origen_inventory:registerHook('createDrop', function(payload)
if Config.NoDropZones[GetZoneAtCoords(payload.coords)] then
return false
end
end)buyItemFires before a shop purchase is committed. Return false to cancel the purchase.
Payload
| Field | Type | Description |
|---|---|---|
source | number | Buyer server id. |
shopType | string | Shop id (e.g. barber_supplies). |
item | table | Item being bought. |
count | number | Quantity. |
price | number | Unit price. |
currency | string? | Optional currency override (cash, bank, custom). |
exports.origen_inventory:registerHook('buyItem', function(payload)
-- block restricted weapons from being bought outside police stations
if payload.item.type == 'weapon' and not IsAtPoliceStation(payload.source) then
return false
end
end)openInventoryFires before a stash or shop is opened. Return false to deny access.
Payload
| Field | Type | Description |
|---|---|---|
source | number | Player server id. |
inventoryId | string|number | Stash / shop id. |
inventoryType | string | stash, shop, trunk, glovebox. |
exports.origen_inventory:registerHook('openInventory', function(payload)
if payload.inventoryType == 'stash' and payload.inventoryId:match('^faction%-') then
return PlayerHasFactionAccess(payload.source, payload.inventoryId)
end
end)clearInventoryFires before an inventory is cleared. Return false to cancel the clear.
Payload
| Field | Type | Description |
|---|---|---|
inventoryId | string|number | Inventory being cleared. |
inventoryType | string | Inventory type. |
exports.origen_inventory:registerHook('clearInventory', function(payload)
if payload.inventoryType == 'stash' and IsAuditedStash(payload.inventoryId) then
return false -- audited stashes cannot be wiped
end
end)itemExpiredFires when an item with decay reaches its expiration. Return false to keep the item in the slot anyway (rare — usually you let it be removed).
Payload
| Field | Type | Description |
|---|---|---|
inventoryId | string|number | Inventory holding the item. |
inventoryType | string | Inventory type. |
item | table | The expired item slot. |
slot | number | Slot id. |
exports.origen_inventory:registerHook('itemExpired', function(payload)
if payload.item.name == 'sealed_evidence' then
return false -- evidence does not decay even if config says so
end
end)pcall. Errors inside your hook are isolated and won't crash the inventory.itemFilter / inventoryFilter / typeFilter options whenever possible — they short-circuit the dispatch loop and are dramatically cheaper than filtering inside your callback.