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.
API
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). |
Filters (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?) → void
Removes 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.
Cancellation contract
Most server hooks honour a cancellation contract:
- Return
false→ the inventory aborts the action (item is not added/swapped/bought/etc.). - Return anything else (or nothing) → the action proceeds normally.
The only exception is createItem, which is mutating: you must return the (possibly modified) metadata table.
Hooks reference
createItem
Fires 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.
itemCreated
Fires 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)itemUsed
Fires 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)useSlot
Fires 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)swapItems
Fires 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)createDrop
Fires 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)buyItem
Fires 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)openInventory
Fires 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)clearInventory
Fires 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)itemExpired
Fires 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)Performance notes
- Each hook is wrapped in a
pcall. Errors inside your hook are isolated and won't crash the inventory. - Execution time is measured. If a hook takes longer than 100ms the inventory will print a debug log so you can spot slow callbacks.
- Use the
itemFilter/inventoryFilter/typeFilteroptions whenever possible — they short-circuit the dispatch loop and are dramatically cheaper than filtering inside your callback.
See also
- Exports · origen_inventory — full export reference.
- Custom · Hooks (overview) — original hook overview and client-side hooks.
- Custom · Client-side hooks — world point and UI hooks.