How Integrations Work#
The inventory never assumes which framework or script you run. It looks at what is started when it boots and talks to it through small, editable bridge files under client/custom/ and server/custom/:
Layer | Supported | Without it |
|---|---|---|
Framework |
| Required: the inventory cannot load. |
Target |
| Points open with an [E] prompt. |
Appearance |
| Worn clothes are still kept by the inventory. |
Interface |
| ox_lib progress bars and notifications, native 3D text. |
Phone |
| With it, the hotbar keys and the quick wheel are blocked while the phone is open. |
ID cards |
| ID cards and licences are filled by the inventory with the character's details. |
Death (ESX) |
| The inventory still refuses to open for dead players, read from the player state. |
Jobs |
| Its job stashes, safes and armories open in qs-inventory, and its player search uses the inventory's. |
Throwing · placing |
| The built-in throw and place are used. Switch them over in Settings → Interaction. |
Every optional integration degrades silently. A missing resource makes its feature fall back to the inventory's own, never an error.
Frameworks#
Topic | ESX | QBCore | Qbox |
|---|---|---|---|
Player inventories |
|
|
|
Items read from |
|
|
|
Cash | The | The | The |
Dirty money | The | The crypto account is shown in its place | The crypto account is shown in its place |
Starter items | From the inventory (below) | qb-core's own | qbx_core's own |
On ESX, the money and black_money accounts follow the items, so cash can be dropped, stored and stolen. On QBCore and Qbox the player object's AddItem, RemoveItem, GetItemBySlot, GetItemByName, GetItemsByName, ClearInventory and SetInventory are handed to the inventory, so scripts that call them keep working. qb-inventory and qb-weapons are stopped when they start.
Every add and remove also fires the events scripts written for other inventories listen to: esx:onAddInventoryItem, esx:onRemoveInventoryItem, qb-inventory:server:itemAdded and qb-inventory:server:itemRemoved, with their client versions.
Compatibility With qb-inventory and ox_inventory#
Many scripts call another inventory directly. qs-inventory answers those calls itself, so they keep working without edits:
exports['qb-inventory']: AddItem, RemoveItem, HasItem, CanCarryItem, GetItemBySlot, GetItemByName, GetItemsByName, GetItemTotalAmount, GetSlotsByItem, GetFirstSlotByItem, GetTotalWeight, GetTotalUsedSlots, GetItemLabel, ClearInventory, SetInventory, SetItemData, CreateUsableItem, UseItem, LoadInventory, SaveInventory, OpenInventory (opens a stash), OpenInventoryById (opens another player), RegisterStash, GetStashItems, AddItemIntoStash, RemoveItemIntoStash, CreateShop, OpenShop and the trunk, glovebox and drop functions. On the client, HasItem.
exports.ox_inventory on the server: AddItem, RemoveItem, CanCarryItem, GetItem, GetSlot, GetSlotWithItem, GetSlotsWithItem, SetItem, SetMetadata, Search, Items, ClearInventory, GetCurrentWeapon, RegisterStash and RegisterShop. On the client: Search, Items, getCurrentWeapon and openInventory for shops and stashes.
A script that only checks GetResourceState('ox_inventory') will not see it started. The better fix is to remove the ox_inventory dependency from that script; on Qbox, the provide 'ox_inventory' line in fxmanifest.lua can be uncommented instead.
Clothing and Appearance Scripts#
The inventory watches what the character wears and turns every piece that differs from the default outfit into its clothing item, so clothes bought in any shop become items on their own. When the inventory closes after the outfit changed, the detected appearance script saves it:
Resource | Saved with |
|---|---|
|
|
| The player's skin in |
|
|
Clothing menus should pause that watcher while they are open, so trying clothes on does not create items:
-- when your clothing menu opens
exports['qs-inventory']:setInClothing(true)
-- when it closes
exports['qs-inventory']:setInClothing(false)Most clothing scripts made for qs-inventory already include these two calls. The save functions live in client/custom/clothing/, one file per script.
Clearing the Inventory on Death#
ClearInventory empties a player's inventory and can keep a list of items. Call it from your ambulance or death script where it removes the items after a death:
-- server side
local keep = { 'id_card', 'phone' } -- items that are NOT removed
exports['qs-inventory']:ClearInventory(source, keep)Stashes From Other Scripts#
Any resource can open a stash by id; the stash is created the first time it is opened. From a client script, RegisterStash opens Stash_<id> right away:
-- client: id, slots, max weight in grams, label
exports['qs-inventory']:RegisterStash('locker_' .. lockerId, 50, 100000, 'Locker')The event form gives you full control over the id:
local stashId = 'locker_' .. lockerId
TriggerServerEvent('inventory:server:OpenInventory', 'stash', stashId, { maxweight = 100000, slots = 50 })
TriggerEvent('inventory:client:SetCurrentStash', stashId)For a personal stash, include the player's identifier in the id. The server can put items in or take them out without opening it with AddItemIntoStash and RemoveItemIntoStash — see Commands and Exports.
Shops From Other Scripts#
Register a shop on the server, then open it for a player standing within 5 m of it:
exports['qs-inventory']:CreateShop({
name = 'police_armory',
label = 'LSPD Armory',
coords = vector3(452.3, -980.0, 30.6),
account = 'bank', -- 'money', 'bank', or 'black_money' / 'crypto'
restock = 30, -- minutes to refill limited items (optional)
items = {
{ name = 'weapon_pistol', price = 2500, amount = 1, stock = 10 }, -- stock: sells out after 10
{ name = 'pistol_ammo', price = 50, amount = 50 },
},
})
exports['qs-inventory']:OpenShop(source, 'police_armory')A client can also ask for a registered shop with TriggerServerEvent('inventory:openShop', 'police_armory'). Shops built on the client with prices sent from the client still work, unless Server-only shops is turned on in Settings → General (advanced), which only accepts shops made with these exports or in the creator.
Police Searches#
Open another player's inventory from a police or MDT script:
-- client
TriggerServerEvent('inventory:server:OpenInventory', 'otherplayer', targetServerId)The server allows it for admins, for the jobs in Settings → Robbery & police → Police jobs, and for anyone else only under the robbery rules (hands up or dead, nearby). Police see the protected slot unless that is switched off.
Starter Items (ESX)#
QBCore and Qbox give starter items themselves. On ESX, list them in server/custom/framework/esx.lua:
StarterItems = {
['id_card'] = 1,
['water_bottle'] = 5,
['tosti'] = 5,
['phone'] = 1,
}Then trigger the inventory's event once the character has been created — for example in esx_identity, on the client, right after registration:
TriggerEvent('inventory:client:GiveStarterItems')Trigger it only once per new character: the ID card and licences are filled with the character's name, birth date and sex.
Fuel Cans#
The petrol can's condition follows the fuel left in it. To keep it in step with LegacyFuel or a similar script, find in that script the line that empties the can:
SetPedAmmo(ped, 883325847, math.floor(GetAmmoInPedWeapon(ped, 883325847) - fuelToAdd * 100))and add this right below it:
TriggerEvent('inventory:client:LegacyFuel', math.floor(GetAmmoInPedWeapon(ped, 883325847)))Discord Logs#
Paste a webhook URL for each channel you want in server/custom/webhook/webhook.lua. An empty one simply logs nothing:
Channel | What goes there |
|---|---|
| /giveitem, /giveweapon, the creator's Give tab, imports and every anti-dupe alert. |
| Blocked attempts to cheat the inventory. |
| Shop purchases and sales to buyers. |
| Items put in or taken out, with the inventory id. |
| Items taken from or given to a robbed player. |
| The Give button, swaps inside a player's inventory, and items crafted at benches. |
Run testwebhooks in the server console to send a test message to every configured channel; the console says which ones Discord accepted. Messages are queued and sent at Discord's pace.
Hooks#
Hooks let your resource react to what the inventory does, without editing it. They are notifications: they cannot cancel the action.
Hook | Fires | Payload |
|---|---|---|
| After every successful shop purchase. |
|
| When an item is moved inside or between inventories, before the move is checked. |
|
exports['qs-inventory']:registerHook('swapItems', function(p)
if p.toType == 'stash' then
print(('%s put something in %s (%s)'):format(p.source, p.toInventory, p.action))
end
end)swapItems fires as the move is processed, so treat it as "a player is moving this" rather than "it moved". An error inside your hook makes that move fail, so keep hooks small.
Add-ons in the Package#
The download includes six optional resources. Start each one after qs-inventory and do not rename them.
Resource | What it does |
|---|---|
| 45 classic shops on the map — 24/7, LTD, Rob's Liquor, hardware, Ammu-Nation and job shops — with products limited by job, grade, gang or licence, job and gang stashes, and a delivery job that restocks the stores. Everything is in its |
| Usable |
| Bags with their own storage: |
| Counters where players buy their |
| Draw and holster animations when a weapon is taken out or put away: a quick draw when the character wears a holster, a slower one without. Add your add-on weapons to its list so they animate too. |
| Rifles, SMGs and shotguns in the inventory show on the character's back while they are not in hand, seen by every player, with their attachments and tints. Seventeen weapons are positioned in its config. |
Adding Support for Another Script#
Everything that talks to another resource is in unencrypted files you can edit:
To connect | Edit |
|---|---|
Notifications, progress bars, 3D text, HUD, death checks, hunger and thirst |
|
Accounts, jobs, admins, player names, vehicle ownership |
|
A renamed core or appearance resource | The detection lists in |
Another clothing script | Add it to the appearance list in |
Another target |
|
Item metadata when given, purchases, other scripts' stashes |
|
Languages#
41 complete translations ship in locales/, covering the inventory, its notifications and both admin panels: ar, bg, bn, ca, cs, da, de, el, en, es, et, fa, fi, fr, he, hi, hr, hu, id, it, ja, ko, lt, lv, ms, nl, no, pl, pt, ro, ru, sk, sl, sv, sw, th, tr, uk, ur, vi and zh. Pick one in Settings → General → Language, or set setr qs:locale "es" in server.cfg for every Quasar resource at once. A text missing from a translation falls back to English, and every file can be edited.



