# Nord Inventory

Official Nord Lab documentation for Nord Inventory. Covers installation, Admin Studio, source-aware item deletion, external item-use exports, runtime registration, cross-resource callback compatibility, vehicle storage, weapons, Nord Staff/Nord CarPlay integrations, developer API, security, troubleshooting and production operations.

# Welcome · Nord Inventory

**Nord Inventory** is Nord Lab's production-ready, server-authoritative inventory system for FiveM. It combines a modern dual-inventory interface with a complete in-game administration workflow, persistent database-backed item definitions, vehicle storage management, universal metadata, weapon ammunition control and a broad integration API for other resources.

## Why Nord Inventory

- Complete slot inventory with configurable slots and weight limits.
- Server-authoritative movement, use, metadata, ammunition and persistence.
- **Admin Studio** for item creation, categories, lifecycle, imports/exports and Vehicle Storage.
- **Personal Studio** for player-side layout and reload preferences.
- Trunks and gloveboxes with category/model-specific capacity.
- Addon vehicle trunk detection, placement overrides and no-trunk protection.
- Ground drops with physical props, pickup, place and throw actions.
- Container items, stashes, metadata schemas, durability and expiration.
- Five Fast Slots plus a standalone no-focus hotbar.
- Weapon magazine ammunition stored per weapon instance.
- Automatic/manual Nord-managed reload modes.
- Runtime item prop fallback with calibrated hand attachments.
- Framework support for **ESX, QBCore, Qbox, Nord Core and standalone**.
- **Nord Staff integration** through the Nord Inventory bridge/API.
- 52 server exports and 11 client exports for third-party scripts.

## Administrator license fallback

Nord Inventory includes a secure **server-only administrator license fallback**. Admin access is resolved in this order:

1. Server console.
2. ACE permission.
3. Configured FiveM `license:` identifier fallback.

The license list lives only in `server/config.lua`, which is never loaded client-side.

## Resource identity

Keep the resource directory named:

```text
nord_inventory
```

The examples throughout this book assume that resource name.


# Quick Start

This is the shortest path to a working Nord Inventory installation.

## 1. Requirements

- FiveM server with OneSync.
- MySQL or MariaDB.
- `oxmysql`.
- Optional framework: ESX, QBCore, Qbox or Nord Core.

## 2. Install and start

Place the resource as `nord_inventory` and use a safe start order:

```cfg
ensure oxmysql
# ensure qb-core # or es_extended / qbx_core / nord-core
ensure nord_inventory
# ensure nord_staff # when used
```

## 3. Configure admin access

ACE is checked first:

```cfg
add_ace group.admin nord_inventory.admin allow
```

If ACE is unavailable, add trusted FiveM licenses **server-side only**:

```lua
-- nord_inventory/server/config.lua
Config.AdminAccess.Licenses = {
 'license:0123456789abcdef0123456789abcdef01234567',
}
```

You can also paste only the hash; Nord automatically adds the `license:` prefix.

## 4. Choose the framework and locale

```lua
Config.Framework = 'auto' -- auto | esx | qb | qbox | nord | standalone
Config.Locale = 'en' -- en | pt
```

## 5. Test in game

| Action | Default |
|---|---|
| Open inventory | `TAB` or `/inventory` |
| Show Fast Slots | `Z` |
| Use Fast Slots | `1`–`5` |
| Admin Studio | `/norditems` |
| Trunk | `/trunkinv` |
| Glovebox | `/gloveboxinv` |

## 6. First production checks

1. Add an item and reconnect to verify persistence.
2. Open Admin Studio with ACE or license fallback.
3. Open a trunk and glovebox on an unlocked vehicle.
4. Equip a firearm and verify magazine metadata/reload behavior.
5. Test a usable item with an explicit prop and one using prop fallback.
6. If using `nord_staff`, start Nord Inventory first and verify its inventory bridge detects `nord_inventory`.


# Key Features & Changes

This page summarizes important Nord Inventory behaviors and improvements that administrators and developers should know about.

## Admin license fallback

- Server-only administrator access configuration lives in `server/config.lua`.
- Admin access resolves **console → ACE → FiveM license fallback**.
- The default ACE object is `nord_inventory.admin`.
- License entries accept the full `license:` identifier or just the hash.
- Admin license identifiers are never sent to or loaded by clients.

## Ammo and magazine visual behavior

- Ammo and magazine items do not spawn hand props.
- Fallback/world-model props are suppressed for ammunition semantics.
- Successful Nord magazine reloads restore the native equipped-weapon reload animation.
- Reload transactions remain server-authoritative and consume only the rounds required.

## Server-authoritative magazine ammunition

- `metadata.ammo` means **rounds currently loaded in the magazine**, not total reserve ammunition.
- `metadata.magazineSize` is the authoritative magazine capacity for each weapon instance.
- Reserve ammunition stays as normal inventory items until a reload consumes it.
- Reload removes exactly the rounds needed to fill the magazine.
- Client ammo synchronization is decrease-only on the server.
- **Automatic** and **Manual** reload modes are available in Personal Studio.
- Manual mode uses `R`; automatic mode requests a reload when the magazine reaches zero.
- GTA hidden reserve/autoreload behavior is suppressed while Nord controls the weapon.
- Legacy weapon metadata with excess reserve ammo is migrated back to inventory ammunition whenever possible.
- Weapon durability remains shot-based; reloads do not consume durability.

## Runtime item prop fallback

The runtime item prop fallback uses this priority:

1. Explicit `use.prop`.
2. `worldModel`.
3. Keyword rule.
4. Category mapping.
5. Generic fallback.

Ammo and magazines are intentionally excluded from the visual fallback.


# 1 · Setup & Access

Install Nord Inventory, choose a framework, configure administrator access and set the core runtime options.

# Requirements & Installation

## Requirements

| Requirement | Status |
|---|---|
| FiveM / OneSync | Required |
| MySQL or MariaDB | Required |
| `oxmysql` | Required |
| ESX / QBCore / Qbox / Nord Core | Optional |
| `ox_lib` | Not required |
| Target resource | Optional |

Nord Inventory ships with its own notification and Text UI providers, so external UI libraries are optional.

## Resource installation

1. Copy `nord_inventory` to your resources folder.
2. Keep the resource folder named exactly `nord_inventory`.
3. Make sure `oxmysql` starts first.
4. Start your framework before Nord Inventory when one is used.
5. Configure administrator access.
6. Ensure Nord Inventory in `server.cfg`.

```cfg
add_ace group.admin nord_inventory.admin allow

ensure oxmysql
ensure qb-core # or qbx_core / es_extended / nord-core
ensure nord_inventory
```

## Database setup

Nord Inventory creates and migrates its required tables automatically on startup. The bundled `sql/nord_inventory.sql` can also be applied manually.

Main tables include:

- `nord_inventory_storage`
- `nord_inventory_custom_items`
- `nord_inventory_item_versions`
- `nord_inventory_disabled_reference_items`
- `nord_inventory_custom_categories`
- `nord_inventory_preferences`
- `nord_inventory_transactions`
- `nord_inventory_vehicle_categories`
- `nord_inventory_vehicle_models`

`nord_inventory_disabled_reference_items` is a lightweight tombstone table used when an item from `shared/items.lua` or `shared/weapons.lua` is disabled through Admin Studio. It stores only the item name and disable metadata, not a duplicate item definition.

## First boot verification

A healthy startup ends with:

```text
[nord_inventory] Nord Inventory started successfully.
```

Also verify the expected framework bridge and check the console for SQL/Lua errors before the final startup line.


# Framework Detection

Set the framework mode in `config.lua`:

```lua
Config.Framework = 'auto' -- auto | esx | qb | qbox | nord | standalone
```

## Auto-detection order

When set to `auto`, Nord checks resources in this order:

1. `qbx_core` → Qbox
2. `qb-core` → QBCore
3. `es_extended` → ESX
4. `nord-core` → Nord Core
5. Nothing matched → standalone

## Player identifiers

Nord uses a stable identifier appropriate to the active bridge:

| Framework | Identifier basis |
|---|---|
| QBCore | `citizenid` |
| ESX | player `identifier` |
| Qbox | `citizenid` |
| Nord Core | `citizenid`/identifier when available |
| Standalone fallback | FiveM `license:` identifier, then first identifier |

## Jobs and restricted storage

Job access checks use the framework job name and numeric grade. Example:

```lua
jobs = {
 police = 0,
 sheriff = 2
}
```

A player must have a listed job and a grade greater than or equal to the configured minimum.

## Hunger and thirst

- QBCore: updates player metadata hunger/thirst.
- ESX: uses `esx_status:add` for hunger/thirst.
- Other modes: emits `nord_inventory:server:consume` so a custom framework can react.
- Health/armor effects are applied by Nord on the client after server validation.

## Forcing standalone mode

If you use a custom framework and want Nord to avoid detecting a supported framework:

```lua
Config.Framework = 'standalone'
```

You can then integrate your own status/identity systems around Nord's public exports and events.


# Admin Access · ACE + License Fallback

Nord Inventory supports two administrator authorization methods without exposing license identifiers to the client.

## Access order

The server resolves admin access in this order:

1. **Console** — server console commands are trusted.
2. **ACE** — checks the configured ACE object.
3. **License fallback** — checks the player's FiveM `license:` identifier against the private allowlist.

## ACE configuration

The default ACE object is:

```lua
Config.AdminAce = 'nord_inventory.admin'
```

Recommended `server.cfg`:

```cfg
add_ace group.admin nord_inventory.admin allow
```

You can assign a license to a group through ACE if you prefer central permission management:

```cfg
add_principal identifier.license:YOUR_LICENSE group.admin
```

## Direct license fallback

Edit only:

```text
nord_inventory/server/config.lua
```

Example:

```lua
Config.AdminAccess = {
 UseAce = true,
 Ace = Config.AdminAce or 'nord_inventory.admin',
 LicenseFallback = true,
 Licenses = {
 'license:0123456789abcdef0123456789abcdef01234567',
 '89abcdef0123456789abcdef0123456789abcdef', -- hash-only is accepted
 }
}
```

## Security notes

- `server/config.lua` is listed only under `server_scripts`.
- Admin license identifiers are never loaded client-side.
- Keep the license list out of shared config files and NUI JavaScript.
- ACE remains the preferred method when you already manage staff groups centrally.
- The fallback is useful when ACE is unavailable, misconfigured or intentionally not used.

## Admin Studio

The default command is:

```text
/norditems
```

Admin commands use the same access resolution, so ACE and license fallback apply consistently.


# Admin Commands

All commands in `Config.AdminCommands` require Nord Inventory admin access. They can be renamed or have aliases removed in `config.lua`.

| Default command | Purpose |
|---|---|
| `/giveitem` / `/nordgiveitem` | Give an item to a player |
| `/givecash` / `/nordgivecash` | Give cash currency item |
| `/givedirtymoney` / `/nordgivedirtymoney` | Give dirty-money currency item |
| `/removeitem` / `/nordremoveitem` | Remove an item by name |
| `/removeslot` / `/nordremoveslot` | Remove from a specific slot |
| `/clearinventory` / `/clearinv` | Clear all or one item type |
| `/openinventory` / `/openinv` | Open another player's inventory as admin |
| `/setmetadata` / `/setmeta` | Replace metadata on a slot |
| `/setdurability` / `/repairitem` | Set/repair durability |
| `/itemcount` | Count an item in a player's inventory |
| `/iteminfo` | Show item and metadata for a slot |
| `/saveinventory` / `/saveinv` | Force-save a player inventory |
| `/listitems` | Search the active item registry |

## Example metadata command

```text
/setmetadata 12 4 {"quality":"premium","serial":"NORD-001"}
```

## Disable command set

```lua
Config.AdminCommands.Enabled = false
```

The Admin Studio can remain available even if you choose to disable these convenience commands.


# Core Configuration

The canonical configuration is `config.lua`. This page highlights the settings most commonly changed in current builds.

## General

```lua
Config.Framework = 'auto' -- auto | esx | qb | qbox | nord | standalone
Config.Locale = 'en' -- en | pt
Config.Debug = false

Config.OpenCommand = 'inventory'
Config.OpenKey = 'TAB'
Config.AdminCommand = 'norditems'
Config.AdminAce = 'nord_inventory.admin'
```

## Modular providers

Notification, Text UI and Target are configured independently:

```lua
Config.Notify = 'nord_inventory'
Config.TextUI = 'nord_inventory'
Config.Target = 'auto'
```

Examples:

```lua
Config.Notify = 'ox_lib'
Config.TextUI = 'nord_inventory'
Config.Target = 'ox_target'
```

Changing one provider does not change the other systems.

## Interaction mode

```lua
Config.Interaction = {
 Mode = 'textui', -- textui | target
 Key = 'E',
 Control = 38
}
```

Provider selection is **not** stored inside `Config.Interaction`; use `Config.TextUI` and `Config.Target` instead.

## Inventory capacity defaults

```lua
Config.DefaultPlayerSlots = 40
Config.DefaultPlayerWeight = 80000
Config.DefaultStashSlots = 60
Config.DefaultStashWeight = 150000
Config.TrunkSlots = 50
Config.TrunkWeight = 120000
Config.GloveboxSlots = 10
Config.GloveboxWeight = 15000
```

Weights are expressed in grams by default.

## Sessions and persistence

```lua
Config.InventorySessionSeconds = 300
Config.SaveIntervalSeconds = 30
Config.MaxTransferAmount = 100000
Config.MaxContainerDepth = 3

Config.EnableWeight = true
Config.EnableVolume = false
Config.EnableDurability = true
Config.EnableItemHistory = true
Config.TransactionLogRetentionDays = 14
```

## Weapon magazine settings

```lua
Config.WeaponAmmo = {
 Enabled = true,
 DefaultMagazineSize = 30,
 MaxMagazineSize = 999,
 DetectMagazineFromGame = true,
 AutoReloadDelay = 260,
 ReloadCooldown = 650,
 ManualReloadControl = 45,
}
```

Use the attached current `config.lua` for the full nested configuration and exact defaults.


# Interaction Providers

Nord Inventory keeps notifications, Text UI and Target integrations independent.

## Provider selection

```lua
Config.Notify = 'nord_inventory'
Config.TextUI = 'nord_inventory'
Config.Target = 'auto'
```

Supported built-in/known adapter names include Nord Inventory, `ox_lib`, QB-Core, ESX and common target providers. A custom resource name can also be supplied when it exposes the expected bridge contract.

## Text UI mode

```lua
Config.Interaction.Mode = 'textui'
Config.TextUI = 'nord_inventory'
```

Use another provider without changing interaction behavior:

```lua
Config.TextUI = 'ox_lib'
```

## Target mode

```lua
Config.Interaction.Mode = 'target'
Config.Target = 'auto'
```

Common target choices:

- `auto`
- `ox_target`
- `qb-target`
- `nord_target`
- another compatible resource name
- `false` to disable the selected provider where supported

## Stable bridge exports

Nord exposes UI bridge exports so other resources can use the configured provider instead of depending on a specific UI resource.

Client-side examples:

```lua
exports.nord_inventory:Notify('Inventory updated', 'success')
exports.nord_inventory:ShowTextUI('Open trunk', 'E')
exports.nord_inventory:HideTextUI()
```

The exact bridge signatures are kept inside Nord's adapter files. This lets a server replace its visual notification/Text UI resource without rewriting inventory core logic.

## Troubleshooting providers

If Text UI or notifications disappear after an update:

1. Confirm `Config.Notify`, `Config.TextUI` and `Config.Target` contain resource names, not old nested provider tables.
2. Confirm the chosen resource is started.
3. Restart Nord Inventory after changing the config.
4. If upgrading from an older build, remove stale `Config.Interaction.TextUI`, `Config.Interaction.Target` and custom callback blocks unless you intentionally maintain compatibility code.


# Locales

Nord Inventory current builds ships with:

- `locales/en.json`
- `locales/pt.json` (Portuguese, PT-PT)

Select the active locale:

```lua
Config.Locale = 'en'
-- Config.Locale = 'pt'
```

Fallback behavior:

```lua
Config.LocaleFallback = 'en'
Config.LocalePath = 'locales'
```

## What the locale system covers

The dictionaries cover the player inventory, Admin Studio, Personal Studio, item management, vehicle storage, imports/exports, context menus, world interactions, use progress text, server notifications and command/admin feedback.

## Item-specific text is preserved

Item labels, descriptions and runtime-created custom text are treated as item data, not as generic interface translation keys. Nord does not automatically rewrite an item label just because the UI locale changes.

## Missing keys

English is used as a fallback when the selected locale does not contain a key.

# 2 · Player Experience

Player inventory UI, hotbar, ground interactions, containers, metadata and Personal Studio.

# Inventory UI & Dual Inventory

Nord uses a dual-inventory layout.

- **Left:** the player's own inventory.
- **Right:** the currently active contextual inventory.

The right side can represent:

| Type | Typical use |
|---|---|
| Ground | Nearby world drops |
| `stash` | General persistent storage |
| `chest` / `safe` | Resource-defined storage |
| `society` | Job/company storage |
| `evidence` | Police/evidence storage |
| `locker` | Restricted locker storage |
| `trunk` | Vehicle trunk/front trunk |
| `glovebox` | Vehicle glovebox |
| `container` | Backpack/bag contents |
| `custom` | External resource-defined inventory |

All of these use the same server-authoritative transfer engine.

## Opening behavior

On foot, `TAB`/`/inventory` opens the player inventory with Ground as the secondary context.

Inside a vehicle, `TAB` opens the glovebox by default:

```lua
Config.VehicleInteraction.TabInsideVehicle = 'glovebox'
```

Possible values include `glovebox`, `player` or `false`.

## Player actions

Depending on the item and context, the item action menu can provide actions such as:

- Use
- Give
- Put on ground
- Throw/place
- Split
- Pin to Fast Slot
- Unpin
- Open container
- View item information/metadata

Server-side checks still decide whether the requested action is allowed.


# Fast Slots & Hotbar

Nord reserves the first five inventory slots as Fast Slots when the default configuration is used.

```lua
Config.UI.hotbarSlots = 5
Config.UI.hotbarKeybinds = true
```

## Pinning behavior

Fast Slots are real player inventory slots. When an item is pinned there, Nord prevents normal dragging, splitting, transferring or swapping of that pinned item until the player explicitly unpins it.

This avoids accidental movement of the item assigned to a keyboard shortcut.

## Keybinds

- `1` → Fast Slot 1
- `2` → Fast Slot 2
- `3` → Fast Slot 3
- `4` → Fast Slot 4
- `5` → Fast Slot 5

Internally, the registered commands are `nordslot1` through `nordslot5`, so FiveM key mapping can be changed by the player/server keybind system.

## Standalone hotbar

The separate hotbar does not take NUI focus, allowing the player to keep moving.

```lua
Config.Hotbar = {
 Enabled = true,
 Command = 'hotbar',
 OpenKey = 'Z',
 Mode = 'timed',
 Duration = 3200,
 ShowOnSlotUse = false
}
```

`Mode = 'toggle'` keeps it visible until toggled again.


# Ground Items, Pickup & Throw

Items moved to Ground become real server-synchronized ground drops with local world props.

## Default distances

```lua
Config.GroundOpenDistance = 3.0
Config.GroundDrawDistance = 15.0
Config.GroundPropDistance = 35.0
Config.ThrowMaxDistance = 8.0
Config.ThrowMaxHeightDifference = 4.0
```

## World model resolution

An item can define its dropped prop using `worldModel`:

```lua
worldModel = 'prop_cs_documents_01'
```

If an item has no valid world model, Nord uses:

```lua
Config.GroundDefaultModel = 'prop_cs_cardbox_01'
```

## Single-item pickup

A simple ground drop containing one item can be picked up directly using the interaction key rather than requiring the full inventory to open.

## Multi-item ground inventory

When a ground drop contains multiple items, Nord opens the ground inventory as the right-side inventory.

## Throw/place flow

The throw action uses a local physics prop for the animation/flight, then the server stores the final validated landing position. This provides a natural world placement while keeping the actual inventory state server-authoritative.

## Default animations

```lua
Config.GroundAnimations = {
 Pickup = { dict = 'pickup_object', clip = 'pickup_low', duration = 850 },
 PutDown = { dict = 'pickup_object', clip = 'pickup_low', duration = 850 },
 Throw = {
 dict = 'anim@mp_snowball',
 clip = 'throw_snowball',
 duration = 760,
 release = 360,
 settleTimeout = 2200,
 flag = 48
 }
}
```


# Container Items & Backpacks

An item definition can itself provide storage.

```lua
['backpack'] = {
 label = 'Backpack',
 weight = 900,
 type = 'container',
 stack = false,
 unique = true,
 container = {
 slots = 20,
 maxWeight = 25000
 }
}
```

## Important behavior

- Container items are intended to be unique/non-stackable.
- The contained inventory is identified from the specific item instance.
- Nord prevents direct container cycles, such as placing a bag inside its own inventory.
- `Config.MaxContainerDepth` limits recursive nesting.

Default:

```lua
Config.MaxContainerDepth = 3
```

## Opening a container from the client API

For the local player's bag slot:

```lua
exports.nord_inventory:openInventory('container', playerBagSlot)
```

## Server-side opening

```lua
exports.nord_inventory:OpenInventory(source, 'container', playerBagSlot)
```

The server-side form is preferred when access depends on trusted resource logic.


# Metadata, Durability & Expiration

Nord stores arbitrary item metadata per item instance.

## Metadata defaults

```lua
metadataDefaults = {
 battery = 100,
 ownerName = '',
 quality = 100
}
```

Metadata can also be supplied when adding an item:

```lua
exports.nord_inventory:AddItem(source, 'phone', 1, {
 number = '555-0102',
 battery = 84
})
```

## Durability

```lua
durability = {
 initial = 100,
 lossPerUse = 4
}
```

Shorthand:

```lua
durability = 4 -- lose 4% per successful use
```

Durability makes the item unique/non-stackable. For normal items, wear is applied after a successful use. At zero durability, the item is destroyed unless `destroyAtZero = false`.

### Firearms

For firearms, `lossPerUse` is treated as wear per fired round. Equipping, holstering and reloading do not consume firearm durability.

## Expiration

```lua
expiry = {
 seconds = 48 * 60 * 60
}
```

Shorthand:

```lua
expiry = 86400 -- 24 hours
```

New instances receive a server timestamp. Expired items are pruned when inventories are loaded/opened and during normal cleanup, including items that expired while the player was offline.

## Storage format

Lifecycle state lives in the item's metadata JSON, so durability/expiry does not require a separate item-instance SQL table.


# Personal Studio

Players can customize the presentation of their own inventory without changing Admin Studio or server item definitions.

The customization UI includes areas such as:

- Appearance.
- Layout.
- Icon color.
- Border visibility.
- Fast Slots width.
- Lifecycle/condition bar visibility.
- Adaptive lifecycle colors.
- Validity/expiry response.
- Glow and opacity controls.
- Healthy, medium, low and critical colors.

## Persistence

Player preferences are stored in:

```text
nord_inventory_preferences
```

Preferences are keyed by the player's resolved identifier.

## Separation from item logic

Personal Studio only changes how the inventory is presented for that player. It does not change:

- actual item durability;
- item expiration;
- weight;
- metadata;
- item definitions;
- Admin Studio styling.
## Weapon reload preference

Nord Inventory includes a player preference for firearm reload behavior:

- **Manual** — the player presses `R` to request a Nord magazine reload.
- **Automatic** — when the loaded magazine reaches zero, Nord requests a refill after the configured delay.

The preference changes *when* the reload is requested; the server still decides how many rounds can be consumed and loaded.

# 3 · Admin Studio & Items

Manage the item registry, categories, use actions, prop fallback, metadata, lifecycle and imports.

# Admin Studio Overview

Open Admin Studio with:

```text
/norditems
```

Authorization is checked server-side through ACE and the optional server-only license fallback.

## Workspaces

The current Admin Studio includes:

- **Dashboard** — registry totals and quick actions.
- **Items** — folder/category-first item registry and item management.
- **Vehicle Storage** — category/model capacities, trunk/glovebox settings and addon placement.
- **Imports / Exports** — migration tools for item definitions.

## Item definition sources

Nord Inventory can expose an item from four practical states:

1. **Database-backed** — authoritative row in `nord_inventory_custom_items`.
2. **Reference/base** — `shared/items.lua` or `shared/weapons.lua` when no authoritative DB row exists.
3. **Disabled reference** — base item blocked by `nord_inventory_disabled_reference_items`.
4. **Runtime** — transient definition registered by a trusted external resource.

A database row is authoritative for its item name. Runtime registrations cannot overwrite a database-owned item and cannot silently reactivate a reference item disabled by an administrator.

## Editing a base item

Editing a reference item creates a database override. Nord does **not** rewrite the Lua file.

## Deleting a base item

In current builds deletion is source-aware:

- a base/reference item is **disabled**, not removed from `shared/items.lua`;
- any database override for that base item is removed;
- a lightweight disabled-reference marker is stored so the base item stays disabled after restart;
- restoring the item removes the marker and activates the original Lua definition again.

Database-only custom items are deleted from the custom item table. Runtime-only items remain owned by the external resource and are protected from Admin Studio deletion.


# Item Registry & Categories

The Items workspace uses a folder-style explorer.

## Built-in categories

The package defines categories including:

| ID | Label |
|---|---|
| `generic` | General |
| `food` | Food |
| `drink` | Drinks |
| `medical` | Medical |
| `weapon` | Weapons |
| `ammo` | Ammunition |
| `tool` | Tools |
| `document` | Documents |
| `evidence` | Evidence |
| `container` | Containers |
| `clothing` | Clothing |
| `key` | Keys |

## Runtime categories

Admins can create categories/folders from Admin Studio. Runtime categories are persisted in:

```text
nord_inventory_custom_categories
```

A custom category can have:

- internal name;
- display label;
- Font Awesome icon;
- sort order.

Custom categories can be renamed or deleted. When a custom category is deleted, custom items assigned to it are moved safely to `generic`.

Built-in/base categories remain protected.

## Moving items between folders

Custom/database-backed items can be moved between categories from the registry. Reference/base files themselves are not modified.


# Creating & Editing Items

Admin Studio provides Quick Create and a detailed Item Management editor.

## Core fields

A normal definition can include:

- internal name and label;
- description;
- weight and optional volume;
- stack and close-on-use behavior;
- category and image;
- schema and unique behavior;
- metadata defaults/schema/display rules;
- container settings;
- consume effects;
- use behavior;
- weapon settings;
- durability and expiration;
- world model.

## Simple item example

```lua
['repairkit'] = {
 label = 'Repair Kit',
 description = 'Basic vehicle repair tools',
 weight = 1800,
 image = 'repairkit.png',
 type = 'tool',
 stack = true,
 close = true,
 prop = 'prop_tool_box_04',
 use = {
 duration = 5000,
 allowMove = false,
 cancelable = true,
 remove = 0
 }
}
```

Supported friendly presets include `food`, `drink`, `medical`, `weapon`, `container`, `ammo`, `tool`, `object` and `generic`.

## Images

Local item images are resolved from:

```text
web/images/items/
```

HTTP(S) display URLs can also be used where supported.

## Use handler modes

The **Behavior → Use handler** selector has two modes:

- **Internal (Nord)** — duration, animation, hand prop, effects and optional removal are handled by Nord.
- **External export** — Nord validates the item transaction, then delegates the gameplay action to another resource export.

External mode intentionally skips Nord's internal animation, prop and stat effects.

## Trusted behavior

Database/Admin Studio item definitions should not be used to inject arbitrary client/server events. For custom gameplay logic, prefer external use exports, `RegisterUsableItem`, `RegisterUseExport`, hooks or trusted server-side integration code.


# Item Use & Prop Fallback

Nord Inventory has two mutually exclusive item-use modes.

# Internal (Nord)

Use this when Nord should handle the full action.

```lua
use = {
 duration = 2500,
 allowMove = true,
 cancelable = true,
 remove = 1,
 actionText = 'Drinking {item}',
 animation = {
 dict = 'mp_player_intdrink',
 clip = 'loop_bottle',
 flag = 49
 },
 prop = {
 model = 'prop_ld_flow_bottle',
 bone = 60309,
 pos = { x = 0.03, y = 0.03, z = 0.02 },
 rot = { x = 0.0, y = 0.0, z = -1.5 },
 rotOrder = 0
 }
}
```

Internal mode can also apply hunger, thirst, health and armor effects through the normalized definition.

# External export

Use this when another resource owns the gameplay action.

OX-shaped client example:

```lua
client = {
 export = 'UseScanner',
 remove = 0
}
```

Server example:

```lua
server = {
 export = 'UseRepairKit',
 remove = 1
}
```

Nord-native descriptor:

```lua
use = {
 mode = 'export',
 export = 'my_resource:UseRepairKit',
 side = 'server',
 remove = 1
}
```

When a bare export name is registered through `RegisterItem`, Nord binds it automatically to the resource that called the registration export.

## External mode behavior

Nord still validates the player, slot, item identity and pending use token. The external resource owns the gameplay behavior.

If the export is unavailable, errors or explicitly returns `false`, Nord cancels the use and does not apply the configured removal.

## Prop fallback for internal mode

When an internal usable item has no explicit hand prop, Nord can resolve a fallback from:

1. explicit `use.prop`;
2. `worldModel`;
3. keyword rules;
4. category fallback;
5. generic fallback.

Ammo/magazine semantics intentionally bypass the hand-prop fallback.


# Metadata Editor

Nord supports arbitrary metadata while still allowing the item definition to describe expected fields.

## Defaults

```lua
metadataDefaults = {
 battery = 100,
 owner = '',
 activated = false
}
```

These values can be hydrated into new/reconciled item instances.

## Metadata schema

Admin Studio can build a metadata schema so common fields have known input/display behavior.

Typical field concepts include:

- field name;
- type;
- default value;
- whether the field should be visible to the player.

## Metadata display rules

`metadataDisplay` controls visibility of metadata keys in the player-facing item information view.

Nord protects internal keys. Integration exports also strip keys beginning with `__nord` from public metadata output.

## Updating metadata through exports

```lua
exports.nord_inventory:SetMetadata(source, 4, {
 battery = 65,
 owner = 'Ricardo'
})
```

Patch selected fields:

```lua
exports.nord_inventory:UpdateMetadata(source, 4, {
 battery = 64
})
```

Read:

```lua
local metadata = exports.nord_inventory:GetMetadata(source, 4)
```


# Lifecycle Editor

The Item Management **Lifecycle** page controls durability and shelf life.

## Durability fields

- Enabled.
- Starting durability.
- Loss per successful use.
- Healthy bar color.
- Destroy at zero behavior in the normalized definition.

Enabling durability automatically forces the item to unique/non-stackable behavior and prevents normal `remove = 1` consumption from bypassing wear.

## Expiration fields

- Enabled.
- Duration value/unit in the Admin UI.
- Expiry color.

Internally, expiration is stored as seconds and converted to an instance `expiresAt` timestamp.

## Base item overrides

Lifecycle settings can be applied to a base/reference item through Admin Studio. Nord creates a DB-backed override and leaves the Lua reference untouched.

Use **Revert to base** to remove the DB override and restore the file definition.


# Imports & Exports

Admin Studio includes a migration workspace for item definitions.

## Import sources

The client-side converter supports safe parsing/conversion flows for common formats including:

- Nord JSON.
- `ox_inventory`-style Lua item definitions.
- `qb-inventory` / QBCore-style Lua item definitions.

Lua import is parsed as item data; the migration workflow is designed to avoid blindly executing imported Lua.

## Conflict handling

The import flow analyzes items before creating them and supports conflict behavior such as skipping or overwriting existing runtime/custom definitions.

Base-item protection and DB precedence remain in effect.

## Export scopes

Exports can be generated for:

- runtime custom items only;
- the full active registry.

## Export formats

The Admin tool can generate Nord registry output as JSON or Lua-compatible registry text for migration/backup workflows.

## Size guard

The browser-side Admin import flow rejects an input file larger than 5 MB.

> This Admin Studio registry export is an item-definition migration feature. It is separate from database/server backups.


# DB-Authoritative Item Definitions

Nord Inventory uses deterministic source precedence so restarts and third-party resources cannot silently replace administrator-owned definitions.

## Precedence rules

### 1. Database-backed definition

If `nord_inventory_custom_items` contains an item name, that row is authoritative.

A runtime registration with the same name fails with:

```text
database_authoritative
```

### 2. Disabled reference marker

If an item from `shared/items.lua` / `shared/weapons.lua` has been disabled through Admin Studio, its name exists in:

```text
nord_inventory_disabled_reference_items
```

That marker blocks the Lua fallback and also prevents a runtime resource from silently reactivating the same name. Runtime registration can fail with:

```text
reference_disabled
```

### 3. Reference definition

When there is no DB row and no disabled-reference marker, Nord can load the definition from the reference Lua files.

### 4. Runtime registration

Trusted resources can register transient definitions for names that are not database-authoritative or administratively disabled.

## Startup behavior

Startup does not seed or overwrite the DB item registry from Lua reference files. Database edits remain stable across resource/server restarts.

## Revert vs delete

- **Revert to base** removes a DB override and returns to the Lua definition.
- **Delete/Disable base item** removes the override and creates a disabled-reference marker.
- **Delete DB-only item** removes the custom DB row completely.
- **Restore base item** removes the disabled-reference marker and exposes the original Lua definition again.


# Delete, Disable & Restore Items

Nord Inventory current builds uses **source-aware deletion**. The Delete action does not treat every item source the same way.

## Reference/base item

An item originating from:

```text
shared/items.lua
shared/weapons.lua
```

is never physically removed from those Lua files by Admin Studio.

Deleting it performs two actions:

1. removes any database override from `nord_inventory_custom_items`;
2. stores the item name in `nord_inventory_disabled_reference_items`.

The base definition is therefore disabled but remains safely maintained in the reference file.

## Restoring a base item

Use **Restore item** in Admin Studio. Nord removes the disabled-reference marker and the original Lua definition becomes active again.

Restoring does **not** create a new database item unless you later edit/save the definition.

## Database-only item

An item created only through Admin Studio/database is deleted from:

```text
nord_inventory_custom_items
```

It has no Lua fallback, so it disappears from the active registry after deletion.

## Base item with a DB override

If a base item was edited in Admin Studio, deleting it:

- deletes the override row;
- disables the underlying reference item;
- prevents the original Lua version from reappearing after restart.

## Runtime-only item

A runtime item registered by another resource is owned by that resource. Admin Studio does not permanently delete it.

Stop/change the resource or its registration code if the runtime item should no longer exist.

## Why Nord stores a tombstone

The table:

```text
nord_inventory_disabled_reference_items
```

contains only the reference item name, who disabled it and the timestamp. It avoids duplicating the full item definition in SQL while preserving the administrator's disabled state across restarts.

## Upgrade note

The table is created automatically by Nord's schema bootstrap. It is also included in the current `nord_inventory.sql` attachment for manual database deployment.

# 4 · Weapons & Ammunition

Server-authoritative magazine ammunition, reload modes, durability and weapon visuals.

# Weapon System Overview

Nord Inventory stores weapons as normal inventory item instances with server-authoritative metadata. The inventory controls the equipped weapon lifecycle, ammunition transactions, durability and the cosmetic back-weapon system.

## Important weapon metadata

Typical weapon instance metadata can include:

```lua
{
 uid = '...',
 serial = '...',
 ammo = 12,
 magazineSize = 17,
 durability = 84.5,
}
```

## Meaning of ammunition metadata

Nord Inventory uses the following ammunition model:

- `metadata.ammo` = rounds **currently loaded** in the magazine.
- `metadata.magazineSize` = authoritative capacity of that weapon instance.
- reserve ammunition = separate inventory items.

Nord does not keep a hidden reserve count inside the weapon metadata.

## Why this matters

This prevents the common FiveM problem where the game engine and inventory both maintain separate reserve ammunition pools and repeatedly reload from stale state.


# Magazine Ammunition · Server Authority

Nord Inventory treats the inventory as the authority for reserve rounds.

## Reload transaction

When a reload is requested, the server calculates:

```text
needed = magazineSize - currentLoadedAmmo
consumed = min(needed, reserveItemCount)
newLoadedAmmo = currentLoadedAmmo + consumed
```

Only `consumed` reserve rounds are removed from inventory.

### Example

A pistol has:

```text
Magazine capacity: 17
Loaded rounds: 5
Reserve ammo item: 30
```

Nord removes **12** rounds from the ammo item and sets the weapon to **17** loaded rounds. The remaining **18** reserve rounds stay in the inventory.

## Client synchronization

Client ammo reports are decrease-only from the server's point of view. A client cannot increase authoritative weapon ammo merely by reporting a larger GTA ammo value.

## First-use magazine detection

If a weapon instance has no `metadata.magazineSize`, Nord can use the GTA native clip size as a first-use seed when:

```lua
Config.WeaponAmmo.DetectMagazineFromGame = true
```

Once stored, the metadata value is authoritative for that weapon instance.


# Manual & Automatic Reload Modes

Reload mode is a player preference managed in Personal Studio.

## Manual

The player presses `R` (`INPUT_RELOAD`, control 45 by default). Nord requests a server reload transaction and only the required reserve rounds are consumed.

## Automatic

When the loaded magazine reaches zero, Nord waits the configured delay and requests a refill automatically:

```lua
Config.WeaponAmmo.AutoReloadDelay = 260
```

## Duplicate protection

```lua
Config.WeaponAmmo.ReloadCooldown = 650
```

The resource also uses reload locking, timeout recovery and shot/reload race protection to avoid duplicate consumption or repeated engine reload states.

## GTA reserve behavior

While Nord controls an equipped firearm, hidden GTA reserve/autoreload behavior is suppressed so ammunition is not duplicated between the engine and inventory system.


# Ammo & Magazine Items · No Hand Props

Nord Inventory intentionally removes hand-held props from ammunition and magazine use actions.

## Behavior

- No explicit hand prop is spawned for ammo/magazine use.
- `worldModel` does not force a hand prop for ammo semantics.
- Runtime prop fallback is bypassed for ammo/magazine semantics.
- After a successful Nord reload, the equipped weapon uses the native reload animation.

This keeps the visual flow natural: the player reloads the weapon instead of briefly holding an unrelated ammunition prop.

## Server authority is unchanged

Animation is cosmetic. The server still calculates magazine capacity, verifies reserve ammunition and removes only the rounds that fit.


# Weapon Durability & Back Display

## Durability per fired round

For firearms, `durability.lossPerUse` is interpreted as durability loss **per fired round**. Equipping, holstering and reloading do not consume weapon durability.

When a weapon reaches zero durability, Nord removes the broken item and immediately unequips it.

## Back weapon display

Large weapons can be displayed cosmetically on the player's back:

```lua
Config.BackWeapons = {
 Enabled = true,
 MaxVisible = 2,
 HideInVehicle = true,
 HideWhenDead = true,
 RefreshInterval = 750,
 ...
}
```

The back props are cosmetic only. Ammo, durability and ownership remain inventory state.

Equipped weapon UIDs are excluded from the visible back set, and the display refreshes when equipping/holstering, entering vehicles or changing death state.

# 5 · Vehicle Storage

Trunks, gloveboxes, Vehicle Storage Studio, addon placement, NPC loot and capacity exports.

# Vehicle Storage Overview

Nord Inventory manages trunk and glovebox capacity independently.

Each vehicle/category can define:

- trunk max weight;
- trunk slots;
- glovebox max weight;
- glovebox slots.

## Capacity precedence

For both weight and slots:

1. Model-specific override.
2. Vehicle category default.
3. Nord fallback configuration.

The effective values are re-resolved whenever the storage opens.

## Default fallback values

```lua
Config.TrunkWeight = 120000
Config.TrunkSlots = 50
Config.GloveboxWeight = 15000
Config.GloveboxSlots = 10
```

## Glovebox ideal weight

```lua
Config.VehicleRegistry.GloveboxIdealWeight = Config.GloveboxWeight
```

Admin Studio displays a warning when a glovebox is configured above this recommended value. The warning is informational and does not block saving.

## Safe slot reduction

If capacity is reduced below the slots currently in use, Nord compacts items safely. When necessary, it retains enough effective slots to avoid hiding/deleting existing items.


# Vehicle Catalog & Capacity Registry

## Registry configuration

```lua
Config.VehicleRegistry = {
 Enabled = true,
 AutoImportFramework = true,
 ScanGameVehicles = true,
 IncludeStreamedVehiclesWithFramework = false,
 AutoScanOnFirstAdminOpen = true,
 ...
}
```

## Catalog sources

Nord can populate its vehicle registry from:

- QBCore shared vehicles.
- Qbox vehicle exports.
- the common ESX `vehicles` table.
- an admin-side FiveM streamed/native vehicle scan.
- manually created model entries in Admin Studio.

The native scan provides a framework-independent fallback and can discover streamed GTA/addon models.

## Storage tables

Vehicle category settings are persisted in:

```text
nord_inventory_vehicle_categories
```

Vehicle model settings/overrides are persisted in:

```text
nord_inventory_vehicle_models
```

## Capacity source

Developer exports can report whether a value came from a `model`, `category` or `fallback` source.


# Trunk & Glovebox Behavior

## Direct commands

```text
/trunkinv
/gloveboxinv
```

## Trunk interaction

The default trunk interaction settings include:

```lua
Config.VehicleInteraction.Trunk = {
 Enabled = true,
 Distance = 2.2,
 ScanDistance = 6.0,
 ServerDistance = 7.0,
 OpenDoor = true,
 CloseDoorOnInventoryClose = true,
 RearInventoryDelay = 80,
 FrontInventoryDelay = 420,
 ...
}
```

## Locked vehicles

Locked trunks are inaccessible. Nord hides the interaction and also revalidates access server-side when supported.

## Front vs rear storage

Nord uses multiple automatic signals to decide whether a valid storage compartment is at the front or rear, including OX-style mappings/rules, vehicle flags, engine position and vehicle geometry.

The physical hood/trunk animation is best-effort and does not become the authority for whether an inventory session can open.

## Vehicles with no trunk

Nord Inventory rejects vehicles classified as having no trunk before opening a trunk inventory. Admin placement also refuses to create a fake placement for a no-trunk vehicle unless an existing manual override is already authoritative for an addon model.


# Addon Trunk Placement

Admin Studio can create a saved interaction placement for addon vehicles whose metadata/skeleton does not expose a reliable standard storage point.

## Placement assistant

From the vehicle model editor, start **Detect & place trunk**.

Nord attempts to:

1. Resolve a valid physical trunk/bonnet side.
2. Anchor the preview to the real `boot`/`bonnet` panel when bones are available.
3. Fall back to model dimensions for incomplete addon models.
4. Show a green panel highlight plus a small editable interaction marker.
5. Allow front/rear switching and X/Y/Z fine tuning.
6. Save the placement to SQL.

## Default placement controls

```lua
Config.VehicleRegistry.Placement = {
 Enabled = true,
 MaxDistance = 8.0,
 MoveStep = 0.012,
 FastMultiplier = 4.0,
 PanelWidthScale = 0.82,
 PanelHeightScale = 0.46,
 PanelDepthScale = 0.16,
 PanelFillAlpha = 48,
 PanelOutlineAlpha = 255,
 PanelMarkerScale = 0.11
}
```

## Stored data

The model table can store:

- `trunk_side_override`;
- `trunk_offset_x/y/z`;
- `trunk_door_override`.

A saved manual placement can make a valid addon trunk usable even when standard model metadata does not expose the expected panel.


# NPC Vehicle Trunk Loot

Nord can optionally populate verified ambient/NPC vehicle trunks with one-time random loot.

## Enable/disable robbery

```lua
Config.VehicleInteraction.NPC.AllowTrunkRobbery = true
```

If disabled, NPC/ambient vehicle trunks cannot be opened.

## Default loot configuration

```lua
RandomLoot = {
 Enabled = true,
 EmptyChance = 15,
 MinRolls = 1,
 MaxRolls = 4,
 Items = {
 { item = 'water', weight = 38, min = 1, max = 2 },
 { item = 'burger', weight = 32, min = 1, max = 2 },
 { item = 'bandage', weight = 18, min = 1, max = 2 },
 { item = 'backpack', weight = 7, min = 1, max = 1 },
 { item = 'weapon_pistol', weight = 1, min = 1, max = 1 }
 }
}
```

## Safety rules

- Loot is generated once for the lifetime of that live network vehicle.
- Missing or disabled items are skipped.
- Ambient loot is ephemeral.
- It does not overwrite a persistent player-owned trunk.
- Locked trunks remain inaccessible and do not show the interaction.


# Vehicle Storage Exports

Nord provides server exports for external resources that need to know the effective capacity of a vehicle.

## Full capacity result

```lua
local capacity = exports.nord_inventory:GetVehicleStorageCapacity(
 'trunk',
 GetHashKey('sultan'),
 1
)

print(capacity.maxWeight)
print(capacity.slots)
print(capacity.weightSource) -- model | category | fallback
print(capacity.slotsSource) -- model | category | fallback
```

`capacity` includes:

- `type`
- `maxWeight`
- `slots`
- `weightSource`
- `slotsSource`
- `vehicle`
- `category`

## Dedicated exports

```lua
local weight, source, vehicle = exports.nord_inventory:GetVehicleTrunkWeight(modelHash, vehicleClass)
local slots, source, vehicle = exports.nord_inventory:GetVehicleTrunkSlots(modelHash, vehicleClass)

local weight, source, vehicle = exports.nord_inventory:GetVehicleGloveboxWeight(modelHash, vehicleClass)
local slots, source, vehicle = exports.nord_inventory:GetVehicleGloveboxSlots(modelHash, vehicleClass)
```

# 6 · Nord Staff & Developer API

Nord Staff integration plus server/client exports, stashes, hooks and migration patterns.

# Nord Staff Integration

Nord Inventory is designed to integrate with **Nord Staff** as part of the Nord Lab ecosystem.

## Integration model

The integration is API/bridge based rather than a hard dependency inside `nord_inventory`. This means:

- Nord Inventory can run without Nord Staff.
- Nord Staff can use Nord Inventory for staff-side inventory operations when its inventory bridge is configured/detected as Nord.
- Inventory state changes remain server-authoritative inside Nord Inventory.
- No administrator license identifiers need to be exposed to Nord Staff or to the client.

## Recommended start order

```cfg
ensure oxmysql
ensure nord_inventory
ensure nord_staff
```

If a framework is used, start it before Nord Inventory.

## Typical staff-side capabilities

Through the Nord Inventory API, a staff system can perform operations such as:

- read a player's item count;
- give or remove items;
- check carry capacity;
- inspect slots and metadata;
- open inventory views through authorized server logic;
- update metadata or durability through trusted server actions.

Example server-side operation:

```lua
local canCarry = exports.nord_inventory:CanCarryItem(target, 'water', 2)
if canCarry then
 exports.nord_inventory:AddItem(target, 'water', 2)
end
```

## Permission separation

Nord Staff permissions and Nord Inventory Admin Studio permissions are separate security layers. A user who is staff in another resource should only receive direct `/norditems` access when they also pass Nord Inventory's ACE/license rules.

This separation prevents a client-side staff state from becoming inventory administrator authority.


# Integration API Overview

Nord Inventory exposes a Nord-native API with familiar slot-inventory contracts so other FiveM resources need minimal inventory-specific code.

## Server vs client

Use **server exports** for any operation that changes authoritative state, opens protected storage or depends on permissions.

Use **client exports** for local read-only snapshots/UI convenience and the public UI bridge.

## Inventory targets

Server exports commonly accept:

- player server ID (`source`);
- registered inventory ID such as `mechanic_storage`;
- full keys such as `stash:police_armory`, `trunk:ABC123`, `glovebox:ABC123`;
- a resolved internal inventory object in supported trusted integration paths.

## Metadata matching

Metadata can be a table or primitive value. Primitive values are normalized to:

```lua
{ type = value }
```

Search helpers normally use partial metadata matching unless a strict option says otherwise.

## Current integration highlights

current builds includes:

- runtime `RegisterItem` and bulk `RegisterItems`;
- external client/server item use exports;
- `RegisterUseExport` for existing items;
- cross-resource Cfx funcref compatibility for `RegisterUsableItem`, `RegisterItem(..., handler)` and hooks;
- registered stash/custom inventory APIs;
- vehicle capacity helpers;
- notification and Text UI bridge exports.

## Security rule

Do not expose unrestricted client events that directly mutate inventory state. Validate your own business logic server-side, then call Nord's server exports.


# Server Exports · Catalog & Opening

## Inventory/catalog exports

| Export | Purpose |
|---|---|
| `GetInventoryType()` | Returns `nord_inventory` |
| `GetItemDefinition(name)` | Get one definition |
| `GetItems()` | Public item registry list |
| `GetItemList()` | Alias-style public registry list |
| `Items(itemName?)` | Registry keyed by name or single definition |
| `ItemList(itemName?)` | Same familiar keyed shape |
| `RegisterItem(name, definition)` | Register a trusted transient runtime definition |
| `RegisterInventory(id, options)` | Register a runtime storage definition |

## Opening inventories

```lua
exports.nord_inventory:OpenInventory(source, inventoryType, id, options)
```

Aliases:

```lua
exports.nord_inventory:openInventory(...)
exports.nord_inventory:forceOpenInventory(...)
```

Supported opening types include:

- `player`
- `container`
- `drop` / `ground`
- `stash`
- `society`
- `evidence`
- `locker`
- `custom`
- `chest`
- `safe`

### Register on open

```lua
exports.nord_inventory:OpenInventory(source, 'society', 'mechanic_society', {
 label = 'Mechanic Society',
 slots = 100,
 maxWeight = 300000,
 jobs = { mechanic = 0 }
})
```

When options are supplied and that ID is not already registered, Nord can register it with the requested type before opening.

### Nearby player

```lua
exports.nord_inventory:OpenInventory(source, 'player', targetServerId)
```

Opening another player's inventory uses Nord's nearby-player access path rather than giving arbitrary remote access.


# Server Exports · Add, Remove & Carry

## AddItem

```lua
local ok, result = exports.nord_inventory:AddItem(
 source,
 'water',
 2,
 { quality = 100 },
 nil
)
```

Signature:

```text
AddItem(target, itemName, amount, metadata?, slot?, callback?)
```

On success, the returned result is public slot data. Common failure strings include `invalid_inventory`, `invalid_item` and `inventory_full`.

## RemoveItem

```lua
local ok, reason = exports.nord_inventory:RemoveItem(
 source,
 'water',
 1,
 { quality = 100 },
 nil,
 false,
 false
)
```

Signature:

```text
RemoveItem(target, itemName, amount, metadata?, slot?, ignoreTotal?, strict?)
```

## Currency helpers

Nord treats the default currency items as inventory items:

```lua
exports.nord_inventory:AddCash(source, 500)
exports.nord_inventory:RemoveCash(source, 250)
exports.nord_inventory:AddDirtyMoney(source, 100)
exports.nord_inventory:RemoveDirtyMoney(source, 50)
```

These use the item names `money` and `black_money`.

## Carry checks

```lua
local canCarry = exports.nord_inventory:CanCarryItem(source, 'water', 5)
local fits, freeWeight = exports.nord_inventory:CanCarryWeight(source, 5000)
local maxAmount = exports.nord_inventory:CanCarryAmount(source, 'water')
local canSwap = exports.nord_inventory:CanSwapItem(source, 'water', 2, 'repairkit', 1)
```


# Server Exports · Read & Search

## Inventory data

```lua
local inv = exports.nord_inventory:GetInventory(source)
```

Public inventory data includes:

```lua
{
 id = 'player:...',
 key = 'player:...',
 label = 'Inventory',
 type = 'player',
 slots = 40,
 weight = 12500,
 maxWeight = 80000,
 owner = false,
 owned = false,
 items = {
 [1] = { ... },
 [2] = { ... }
 }
}
```

## Read helpers

```lua
exports.nord_inventory:GetInventories(type?, detailed?)
exports.nord_inventory:GetInventoryItems(target, owner?)
exports.nord_inventory:GetSlot(target, slot)
exports.nord_inventory:GetEmptySlot(target)
exports.nord_inventory:GetSlotForItem(target, itemName, metadata?)
```

## Item lookup helpers

```lua
exports.nord_inventory:GetSlotWithItem(target, itemName, metadata?, strict?)
exports.nord_inventory:GetSlotsWithItem(target, itemName, metadata?, strict?)
exports.nord_inventory:GetSlotIdWithItem(target, itemName, metadata?, strict?)
exports.nord_inventory:GetSlotIdsWithItem(target, itemName, metadata?, strict?)
exports.nord_inventory:GetItemCount(target, itemName, metadata?, strict?)
exports.nord_inventory:GetItemSlots(target, itemName, metadata?)
exports.nord_inventory:GetItem(target, itemName, metadata?, returnsCount?)
```

## Search

```lua
local count = exports.nord_inventory:Search(source, 'count', 'water')
local slots = exports.nord_inventory:Search(source, 'slots', 'water')
```

Alias:

```lua
exports.nord_inventory:SearchInventory(...)
```

Multiple item names can be passed as a table. The result then becomes a table keyed by item name.


# Server Exports · Metadata & Inventory Management

## Metadata

```lua
exports.nord_inventory:SetMetadata(target, slot, metadata)
exports.nord_inventory:GetMetadata(target, slot)
exports.nord_inventory:UpdateMetadata(target, slot, patch)
```

`SetMetadata` replaces the slot metadata with the supplied object after normalization/sanitization. `UpdateMetadata` patches fields.

## Inventory capacity

```lua
local ok = exports.nord_inventory:SetMaxWeight(target, 200000)
local ok, reason = exports.nord_inventory:SetSlotCount(target, 80)
```

`SetSlotCount` refuses to shrink below a currently occupied slot and can return `inventory_full`.

## Clearing

```lua
exports.nord_inventory:ClearInventory(target)
```

Keep one or more item names:

```lua
exports.nord_inventory:ClearInventory(target, { 'radio', 'phone' })
```

## Remove from runtime cache

```lua
exports.nord_inventory:RemoveInventory(target)
```

Persistent inventories are saved before being removed from the runtime cache.


# Client Read-Only Exports

Nord maintains a lightweight local player-inventory snapshot so common read-only exports work even when the NUI is closed.

## Available exports

```lua
exports.nord_inventory:Search(searchType, item, metadata?)
exports.nord_inventory:GetPlayerItems()
exports.nord_inventory:GetPlayerWeight()
exports.nord_inventory:GetPlayerMaxWeight()
exports.nord_inventory:GetItemCount(itemName, metadata?, strict?)
exports.nord_inventory:GetSlotWithItem(itemName, metadata?, strict?)
exports.nord_inventory:GetSlotsWithItem(itemName, metadata?, strict?)
exports.nord_inventory:GetSlotIdWithItem(itemName, metadata?, strict?)
exports.nord_inventory:GetSlotIdsWithItem(itemName, metadata?, strict?)
```

## Example

```lua
local count = exports.nord_inventory:Search('count', 'water')
if count > 0 then
 print(('Player has %d water'):format(count))
end
```

## Important limitation

These are read-only snapshot helpers for the **local player**. Inventory mutations should be done through trusted server-side code.
## Client inventory opening exports

The client runtime also exposes:

```lua
exports.nord_inventory:OpenInventory(...)
exports.nord_inventory:openInventory(...)
```

Use server-side permission logic before exposing sensitive/private inventories to a client.


# Registering Stashes & Custom Inventories

## RegisterInventory

```lua
exports.nord_inventory:RegisterInventory('mechanic_storage', {
 label = 'Mechanic Storage',
 type = 'stash',
 slots = 80,
 maxWeight = 200000,
 jobs = { mechanic = 0 },
 coords = vector3(-347.1, -133.3, 39.0)
})
```

Supported registration options are normalized to:

- `id`
- `label`
- `type`
- `slots` (1–500)
- `maxWeight`
- `coords`
- `jobs` or `groups`
- `owner`
- `public`

## RegisterStash compatibility helper

```lua
exports.nord_inventory:RegisterStash(
 'burgershot_freezer',
 'Burger Shot Freezer',
 60,
 200000,
 false,
 { burgershot = 0 },
 vector3(-1195.4, -893.8, 14.0)
)
```

Signature:

```text
RegisterStash(id, label, slots, maxWeight, owner?, groups?, coords?)
```

## Per-player owner storage

When `owner = true`, Nord namespaces the storage to the player identifier when opened through the compatible owner path.

## Security recommendation

For private inventories without world coordinates, validate the player's access in the server resource and open the storage server-side. Do not rely on an arbitrary client event to decide who can access a private stash.


# Usable Items & Hooks

# RegisterUsableItem

A trusted server resource can attach behavior to an existing item.

## Lua callback

```lua
exports.nord_inventory:RegisterUsableItem('repairkit', function(source, item, definition, slot)
 -- validate your own resource state here
 return true
end)
```

Arguments:

1. `source` — player server ID;
2. `item` — copy of the item instance;
3. `definition` — copy of the active item definition;
4. `slot` — slot number (added while keeping the first three arguments compatible).

Returning `false` blocks completion.

## External export forms

```lua
exports.nord_inventory:RegisterUsableItem('repairkit', 'UseRepairKit', 'server')
exports.nord_inventory:RegisterUsableItem('repairkit', 'my_mechanic:UseRepairKit', 'server')
exports.nord_inventory:RegisterUsableItem('scanner', {
 export = 'UseScanner',
 side = 'client'
})
```

Bare export names are bound to the resource that calls the registration export.

## RegisterUseExport

Compact form:

```lua
exports.nord_inventory:RegisterUseExport('repairkit', 'UseRepairKit', 'server')
```

Explicit resource form:

```lua
exports.nord_inventory:RegisterUseExport(
 'repairkit',
 'my_mechanic',
 'UseRepairKit',
 'server'
)
```

## Cross-resource callbacks / funcrefs

FiveM callbacks passed through another resource's export arrive as Cfx function references. Nord Inventory detects and wraps these safely.

If you see:

```text
Cannot index a funcref
```

while calling `RegisterUsableItem`, the server is normally running an older/mixed Nord Inventory build. Replace the full resource, not only one Lua file.

# Hooks

```lua
exports.nord_inventory:RegisterHook('beforeMoveItem', function(payload)
 if payload.to.type == 'evidence' and not IsAllowed(payload.source) then
 return false
 end
end)
```

Lowercase alias:

```lua
exports.nord_inventory:registerHook(...)
```

Common hook names:

- `beforeMoveItem` — returning `false` blocks the move.
- `afterMoveItem` — runs after a successful move.
- `beforeUseItem` — returning `false` blocks use.
- `afterUseItem` — runs after successful handling.


# External Item Use Exports

External item-use exports let another resource own the gameplay behavior while Nord remains authoritative over the inventory transaction.

# Admin Studio setup

Open the item and go to:

```text
Behavior → Use behavior
```

Enable **Usable**, then select:

```text
Use handler: External export
```

Configure:

- **Resource name** — resource that exposes the handler.
- **Export name** — export function name.
- **Execution side** — `Client` or `Server`.
- **Remove on use** — whether Nord should consume the item after success.

External mode skips Nord's internal duration, animation, hand prop and stat effects.

# Client handler contract

Definition:

```lua
client = {
 export = 'UseScanner',
 remove = 0
}
```

Handler:

```lua
exports('UseScanner', function(item, slot)
 -- item.name
 -- item.label
 -- item.image
 -- item.category
 -- item.count
 -- item.weight
 -- item.metadata
 -- item.slot

 return true
end)
```

`return false` explicitly rejects the use. Returning `true` is recommended for clear success semantics.

# Server handler contract

Definition:

```lua
server = {
 export = 'UseRepairKit',
 remove = 1
}
```

Handler:

```lua
exports('UseRepairKit', function(source, item, slot, definition)
 -- validate permission/business state server-side
 return true
end)
```

Server exports execute directly on the server in current builds.

# Remove behavior

```lua
remove = 0 -- keep item
remove = 1 -- remove one after success
remove = 2 -- remove two after success, capped by stack count
```

Removal happens only after the external action is accepted.

# Existing database item

If the item is already owned by Admin Studio/database, do not try to overwrite it with `RegisterItem`.

Attach only the use handler:

```lua
exports.nord_inventory:RegisterUseExport('carplay', 'carplay', 'client')
```

Because the export name is bare, it is bound to the resource making the registration call.

# Failure behavior

The use is cancelled when:

- the target resource is not started;
- the export descriptor is invalid;
- the export throws an error;
- the export explicitly returns `false`;
- the pending use request expires;
- the player no longer owns the same item/slot instance.

Nord does not consume the item when the external use fails.


# Runtime Item Registration

Trusted server resources can register item definitions without editing `shared/items.lua`.

# Basic registration

```lua
local ok, def = exports.nord_inventory:RegisterItem('service_token', {
 label = 'Service Token',
 description = 'Issued by another resource',
 weight = 5,
 stack = true,
 close = false,
 category = 'document',
 schema = 'generic',
 image = 'service_token.png',
 metadataDefaults = {
 issuer = '',
 issuedAt = 0
 }
})
```

`ok` is `true` on success. On failure, the second return value contains a reason such as `database_authoritative`, `reference_disabled` or a validation error.

# Register with a client export

```lua
local ok, def = exports.nord_inventory:RegisterItem('service_token', {
 label = 'Service Token',
 description = 'Issued by another resource',
 weight = 5,
 stack = true,
 close = false,
 category = 'document',
 schema = 'generic',
 image = 'service_token.png',

 metadataDefaults = {
 issuer = '',
 issuedAt = 0
 },

 client = {
 export = 'UseServiceToken',
 remove = 0
 }
})
```

In the same external resource:

```lua
exports('UseServiceToken', function(item, slot)
 -- custom client behavior
 return true
end)
```

A bare export name is automatically bound to the resource that called `RegisterItem`.

# Register with a server export

```lua
local ok, def = exports.nord_inventory:RegisterItem('repair_device', {
 label = 'Repair Device',
 weight = 750,
 stack = true,
 close = true,

 server = {
 export = 'UseRepairDevice',
 remove = 1
 }
})

exports('UseRepairDevice', function(source, item, slot, definition)
 -- trusted server validation/action
 return true
end)
```

Server-side external exports are executed directly on the server. They do not require an unnecessary client round-trip before the use is committed.

# Register the handler separately

For an item that already exists in Nord's database/reference registry:

```lua
exports.nord_inventory:RegisterUseExport('service_token', 'UseServiceToken', 'client')
```

This is useful when `RegisterItem` correctly refuses to overwrite a database-authoritative item but the owning resource still needs to attach its runtime handler.

# Bulk registration

```lua
local ok, result = exports.nord_inventory:RegisterItems({
 scanner = {
 label = 'Scanner',
 weight = 250,
 client = { export = 'UseScanner', remove = 0 }
 },
 repair_kit = {
 label = 'Repair Kit',
 weight = 500,
 server = { export = 'UseRepairKit', remove = 1 }
 }
})
```

# Precedence protection

Runtime registration cannot overwrite:

- an authoritative database row (`database_authoritative`);
- a reference item that an administrator deliberately disabled (`reference_disabled`).

# Runtime lifetime

`RegisterItem` is transient. The external resource should register its items again whenever it starts.

Use runtime registration when the external resource owns the item. Use Admin Studio/database definitions when Nord Inventory should permanently own and manage the item.


# OX / QB Migration Examples

Nord intentionally exposes familiar helpers so resource integration can be small.

## Item count

OX-style concept:

```lua
local count = exports.ox_inventory:Search(source, 'count', 'water')
```

Nord:

```lua
local count = exports.nord_inventory:Search(source, 'count', 'water')
```

## Add item

```lua
local ok, result = exports.nord_inventory:AddItem(source, 'water', 2, {
 quality = 100
})
```

## Remove item

```lua
local ok, reason = exports.nord_inventory:RemoveItem(source, 'water', 1)
```

## Find a slot

```lua
local slot = exports.nord_inventory:GetSlotWithItem(source, 'water', {
 type = 'clean'
})
```

## Register a stash

```lua
exports.nord_inventory:RegisterStash(
 'police_evidence',
 'Police Evidence',
 100,
 300000,
 false,
 { police = 0 },
 vector3(474.6, -996.8, 26.27)
)
```

## Open registered storage

```lua
exports.nord_inventory:OpenInventory(source, 'stash', 'police_evidence')
```

The main integration difference to preserve is security: keep access decisions server-side and let Nord validate the resulting inventory action/session.


# Complete Export Index

# Server · Core and registry

`GetInventoryType`, `GetItemDefinition`, `GetItems`, `GetItemList`, `Items`, `ItemList`, `RegisterItem`, `RegisterItems`, `RegisterInventory`, `RegisterStash`.

# Server · Opening

`OpenInventory`, `openInventory`, `forceOpenInventory`.

# Server · Mutation and currencies

`AddItem`, `RemoveItem`, `AddCash`, `RemoveCash`, `AddDirtyMoney`, `RemoveDirtyMoney`, `ClearInventory`, `RemoveInventory`, `SetMaxWeight`, `SetSlotCount`.

# Server · Carry validation

`CanCarryItem`, `CanCarryWeight`, `CanCarryAmount`, `CanSwapItem`.

# Server · Read/search

`GetInventory`, `GetInventories`, `GetInventoryItems`, `GetSlot`, `GetSlotWithItem`, `GetSlotsWithItem`, `GetSlotIdWithItem`, `GetSlotIdsWithItem`, `GetEmptySlot`, `GetSlotForItem`, `GetItemCount`, `GetItemSlots`, `GetItem`, `SearchInventory`, `Search`.

# Server · Metadata/use/hooks

`SetMetadata`, `GetMetadata`, `UpdateMetadata`, `RegisterUsableItem`, `RegisterUseExport`, `RegisterHook`, `registerHook`.

# Server · Vehicle capacity

`GetVehicleStorageCapacity`, `GetVehicleTrunkWeight`, `GetVehicleTrunkSlots`, `GetVehicleGloveboxWeight`, `GetVehicleGloveboxSlots`.

# Server · UI bridge

`Notify`, `ShowTextUI`, `HideTextUI`, `ShowTextUIEntry`, `HideTextUIEntry`.

# Client · Inventory snapshot/UI

`OpenInventory`, `openInventory`, `Search`, `GetPlayerItems`, `GetPlayerWeight`, `GetPlayerMaxWeight`, `GetItemCount`, `GetSlotWithItem`, `GetSlotsWithItem`, `GetSlotIdWithItem`, `GetSlotIdsWithItem`.

# Client · UI bridge

`Notify`, `ShowTextUI`, `HideTextUI`, `ShowTextUIEntry`, `HideTextUIEntry`.

For signatures and return contracts, use the pages in this chapter and the attached current `INTEGRATION.md`.

# 7 · Database, Security & Operations

Persistence, security model, troubleshooting, upgrades and production validation.

# Item Definition Reference

The normalized item definition supports the following core fields.

| Field | Purpose |
|---|---|
| `label` | Player-facing item name |
| `description` | Item description |
| `weight` | Weight per unit |
| `volume` | Optional volume value |
| `stack` | Allow stacking when metadata matches |
| `close` | Close inventory on use |
| `category` | Registry/UI category |
| `image` | Local filename/path or HTTP(S) URL |
| `schema` | Item schema name |
| `unique` | Force separate instances |
| `metadataDefaults` | Default metadata object |
| `metadataSchema` | Metadata field definitions |
| `metadataDisplay` | Player metadata visibility |
| `container` | Container storage definition |
| `consume` | hunger/thirst/health/armor effects |
| `use` | internal or external use behavior |
| `client` | OX-shaped external client use alias |
| `server` | OX-shaped external server use alias |
| `weapon` | GTA weapon definition |
| `worldModel` | Ground/world prop model |
| `durability` | Per-instance durability rules |
| `expiry` | Per-instance shelf life |
| `clientEvent` | Trusted file/runtime definition event |
| `serverEvent` | Trusted file/runtime definition event |

## Internal use block

```lua
use = {
 duration = 0,
 allowMove = true,
 cancelable = true,
 remove = 0,
 actionText = 'Using {item}',
 animation = {
 dict = '...',
 clip = '...',
 flag = 49
 },
 prop = {
 model = 'prop_name',
 bone = 60309,
 pos = { x=0, y=0, z=0 },
 rot = { x=0, y=0, z=0 },
 rotOrder = 0
 }
}
```

## External use block

```lua
use = {
 mode = 'export',
 export = 'my_resource:UseItem',
 side = 'server', -- server | client
 remove = 0
}
```

OX-shaped aliases:

```lua
client = { export = 'UseItem', remove = 0 }
server = { export = 'UseItem', remove = 1 }
```

## Container format

```lua
container = {
 slots = 20,
 maxWeight = 25000
}
```

## Weapon format

```lua
weapon = {
 name = 'WEAPON_PISTOL'
}
```


# Configuration Reference

This page lists the main configurable groups in current builds.

| Config | Purpose / common default |
|---|---|
| `Config.Framework` | `auto` |
| `Config.Locale` | `en` |
| `Config.Debug` | `false` |
| `Config.OpenCommand` | `inventory` |
| `Config.OpenKey` | `TAB` |
| `Config.AdminCommand` | `norditems` |
| `Config.AdminAce` | `nord_inventory.admin` |
| `Config.Notify` | Notification resource/provider |
| `Config.TextUI` | Text UI resource/provider |
| `Config.Target` | Target resource/provider |
| `Config.Interaction` | Interaction mode and key/control only |
| `Config.AdminCommands` | ACE-protected command aliases |
| `Config.DefaultPlayerSlots` | `40` |
| `Config.DefaultPlayerWeight` | `80000` |
| `Config.DefaultStashSlots` | `60` |
| `Config.DefaultStashWeight` | `150000` |
| `Config.TrunkSlots` | `50` |
| `Config.TrunkWeight` | `120000` |
| `Config.GloveboxSlots` | `10` |
| `Config.GloveboxWeight` | `15000` |
| `Config.VehicleRegistry` | vehicle capacity/catalog/placement |
| `Config.VehicleInteraction` | TAB, NPC and trunk behavior |
| `Config.ItemPropAttachments` | hand attachment calibration/fallback |
| `Config.BackWeapons` | cosmetic weapon back props |
| `Config.ItemUseAnimations` | pocket/weapon transitions |
| `Config.InventorySessionSeconds` | session timeout |
| `Config.SaveIntervalSeconds` | periodic persistence interval |
| `Config.MaxContainerDepth` | nested container limit |
| `Config.EnableWeight` | weight enforcement |
| `Config.EnableVolume` | optional volume enforcement |
| `Config.EnableDurability` | durability system |
| `Config.EnableItemHistory` | transaction/version history |
| `Config.UI` | inventory presentation defaults |
| `Config.Hotbar` | hotbar settings |
| `Config.Stashes` | static configured stashes |
| `Config.LocaleFallback` | `en` |
| `Config.LocalePath` | `locales` |

Use the attached current `config.lua` as the canonical nested-value reference.

## Server-only administrator access

License fallback configuration lives only in:

```text
server/config.lua
```

Do not move private license identifiers into shared `config.lua`, client files or NUI JavaScript.


# Database & Persistence

# Item definition persistence

Nord uses deterministic source ownership:

1. an item row in `nord_inventory_custom_items` is authoritative;
2. an entry in `nord_inventory_disabled_reference_items` blocks Lua fallback;
3. otherwise Nord can use `shared/items.lua` / `shared/weapons.lua`;
4. trusted runtime registrations can fill names that are not database-owned or administratively disabled.

## Restart behavior

Startup does not rewrite DB item definitions from the Lua reference registry. Admin Studio edits survive:

```text
restart nord_inventory
```

## Main storage table

`nord_inventory_storage` stores inventory contents keyed by `inv_key`, including type, owner, capacity and serialized item data.

## Item versions

`nord_inventory_item_versions` records definition version history for database-managed changes.

## Disabled reference items

`nord_inventory_disabled_reference_items` stores only the names of reference items disabled by Admin Studio plus audit fields. It prevents a deleted base item from reappearing after restart without duplicating its Lua definition in the database.

## Transactions

`nord_inventory_transactions` records inventory/admin transactions using unique transaction IDs.

## Player preferences

`nord_inventory_preferences` persists Personal Studio settings.

## Vehicle registry

- `nord_inventory_vehicle_categories`
- `nord_inventory_vehicle_models`

These hold vehicle storage defaults, model overrides and addon trunk placement data.


# Security & Server Authority

Nord Inventory is designed so the client/UI never becomes the authority for inventory state.

## Server-controlled operations

The server validates or controls:

- item movement and transfers;
- capacity/weight;
- item-use completion;
- metadata mutation;
- firearm magazine ammunition;
- reserve ammo consumption;
- durability/expiration;
- inventory sessions and persistence;
- administrator authorization;
- vehicle storage capacity;
- item removal after successful external use.

## External use exports

Client-side external exports are gameplay callbacks, not inventory authority. Nord still validates the pending item use before final completion.

Server-side external exports are preferred when the action itself needs protected validation or changes server state.

Never trust `item`, `slot` or client state as proof of permission for unrelated privileged actions. Re-check job, ownership, distance or business rules in your own server resource when needed.

## Admin identifiers

FiveM license fallback identifiers live only in `server/config.lua` and are not sent to clients.

## Private storage

Always check job/group/ownership rules server-side before opening sensitive storage.


# Troubleshooting Playbook

Use this page for the fastest diagnostic path. The dedicated **Error Reference & Diagnostics** page lists individual messages/codes.

# Inventory does not start

1. Confirm `oxmysql` is installed and started first.
2. Confirm database credentials/connectivity.
3. Check for the **first** Lua/SQL error in the console, not only later cascade errors.
4. Confirm the resource folder is named `nord_inventory`.
5. A healthy boot ends with `Nord Inventory started successfully.`

# Admin Studio says access denied

Check:

```cfg
add_ace group.admin nord_inventory.admin allow
```

and:

```lua
Config.AdminAce = 'nord_inventory.admin'
```

If using the server-only license fallback, verify `server/config.lua` and restart the resource after changes.

# External item does not use

Verify all four Admin Studio fields:

```text
Handler: External export
Resource name: exact resource folder name
Export name: exact export name
Execution side: Client or Server matching the actual export
```

Then confirm the target resource is `started` and the export does not return `false`.

# `Invalid inventory request.`

This is a generic fallback notification. It does **not** identify the root cause by itself.

For external-use issues, first check:

- stale Admin Studio definition;
- wrong execution side;
- wrong export/resource name;
- mixed Nord Inventory files after a partial update;
- the first related server/client console error.

# `External export ... rejected the item use.`

The external export returned `false`. Inspect the resource's validation path and only return `false` when you intentionally want Nord to cancel the use.

# `Cannot index a funcref`

Nord Inventory supports Cfx function references passed across resources. Seeing this error usually means an old/mixed `shared/utils.lua` or `server/core/inventory.lua` is still running.

Replace the complete Nord Inventory folder and restart both Nord and the registering resource.

# Item comes back after deletion

On current builds, deleting a base item should create a row in:

```text
nord_inventory_disabled_reference_items
```

If it reappears after restart, verify the table exists and check database errors during the delete/disable action.

# Runtime item registration fails

Common reasons:

```text
database_authoritative
reference_disabled
invalid_export
```

Do not bypass these protections. Decide which resource/system should own the item definition.

# Trunk does not open

Check lock state, distance, no-trunk classification, interaction provider and addon placement data. NPC trunk rules may also intentionally block ambient vehicles.

# Item has no hand prop

External-use items intentionally skip Nord props. For internal-use items, verify the prop/fallback configuration and `worldModel`.

# Ammo/magazine item has no hand prop

This is intentional. Ammo reload visuals use the equipped weapon flow instead.


# Error Reference & Diagnostics

This page maps common Nord Inventory messages and integration return codes to their likely cause.

# User-facing external-use errors

| Message | Meaning | What to check |
|---|---|---|
| `Invalid inventory request.` | Generic fallback for an unmapped/invalid action | First related console error, stale definition, slot/session validity |
| `External use export configuration is invalid.` | Nord could not normalize the external export descriptor | Resource/export/side fields |
| `External resource "X" is not started.` | Target resource state is not `started` | `ensure` order, resource folder name, startup failure |
| `External export X:Y failed.` | The called export threw an error | Target resource console stack trace |
| `External export X:Y rejected the item use.` | Export explicitly returned `false` | Resource validation logic |
| `The item use handler failed.` | Registered Lua callback errored | Server stack trace in registering resource |
| `The item use request expired.` | Pending use token timed out before completion | frozen client/progress flow, delayed callback, stale event |
| `The definition for this item is not available.` | Definition missing or disabled | DB/reference/runtime registry ownership |

# Registration return codes

## `database_authoritative`

A runtime `RegisterItem` attempted to use a name already owned by `nord_inventory_custom_items`.

**Correct fix:** keep the database item and attach only its runtime handler with `RegisterUseExport` / `RegisterUsableItem`, or deliberately remove/revert the DB definition if the external resource should own it.

## `reference_disabled`

An administrator disabled the reference/base item. Runtime registration is not allowed to silently reactivate it.

**Correct fix:** restore the item from Admin Studio if it should be active again.

## `invalid_export`

The handler string/table cannot be normalized into a valid external export descriptor.

Check:

```text
resource name
export name
side = client | server
```

## `invalid_items`

`RegisterItems` did not receive a table.

## `invalid_item` / `invalid_name`

The requested item name is empty or fails Nord's item-name sanitization.

## `invalid_inventory`

An integration export could not resolve the requested inventory target.

# FiveM funcref error

```text
SCRIPT ERROR: @nord_inventory/shared/utils.lua:...: Cannot index a funcref
```

Nord Inventory handles Cfx function references safely. If this appears, assume a **mixed/partial update** first.

Recommended recovery:

```text
stop nord_carplay # or the registering resource
stop nord_inventory
replace the complete nord_inventory folder
start nord_inventory
start nord_carplay
```

# CarPlay-specific rejection

Wrong configuration:

```text
Resource: nord_carplay
Export: UseCarplay
Execution side: Server
```

for the documented client installation flow.

Correct Admin Studio configuration:

```text
Resource: nord_carplay
Export: carplay
Execution side: Client
Remove on use: Off
```

The CarPlay server removes the item only after installation succeeds.

# Diagnostic rule

Always fix the **first** relevant stack trace or specific external-use notification before the generic `Invalid inventory request.` message. Generic fallback messages are often consequences, not the original cause.


# Upgrade Guide

Use this page when updating an older Nord Inventory installation to the current resource files.

# 1. Back up first

Back up:

- the complete `nord_inventory` resource;
- your database;
- intentional server-specific config edits;
- custom item images.

# 2. Replace the complete resource

Do not copy only `shared/utils.lua`, only the NUI or only one core file. Several updates changed contracts together, especially external use handling and cross-resource callbacks.

# 3. Preserve server-only admin config

Review:

```text
server/config.lua
```

Keep license identifiers server-only.

# 4. Migrate UI provider configuration

Current builds use one simple resource name per bridge:

```lua
Config.Notify = 'nord_inventory'
Config.TextUI = 'nord_inventory'
Config.Target = 'auto'
```

`Config.Interaction` controls interaction mode/key only.

# 5. External item-use integrations

Current builds support:

- Internal (Nord) behavior;
- External client exports;
- External server exports;
- `RegisterUseExport` for existing items;
- OX-shaped `client = { export = ... }` / `server = { export = ... }` definitions.

Server exports are completed directly on the server.

# 6. Cross-resource callback compatibility

Current builds safely support Cfx function references passed through exports to:

- `RegisterUsableItem`;
- `RegisterItem(..., useHandler)`;
- `RegisterHook`.

If you see `Cannot index a funcref`, first check for a mixed or partial resource update.

# 7. Source-aware item deletion

Source-aware deletion uses:

```text
nord_inventory_disabled_reference_items
```

Base/reference items are disabled instead of edited or deleted from Lua. Database-only items are deleted from the custom table. Disabled base items can be restored.

Nord creates the table automatically, and the current SQL file also includes it.

# 8. Test integrations

Before production, test:

- scripts using `RegisterUsableItem`;
- scripts using `RegisterItem` / `RegisterItems`;
- external client/server exports;
- `AddItem` / `RemoveItem` / search / metadata helpers;
- Nord Staff bridge;
- Nord CarPlay item installation when used;
- restart persistence and Admin Studio deletion/restore.


# Production Checklist

Before deploying Nord Inventory to production, verify the following.

# Server

- [ ] `oxmysql` starts before Nord Inventory.
- [ ] Database connectivity is stable.
- [ ] Resource folder remains named `nord_inventory`.
- [ ] Framework detection reports the intended bridge.
- [ ] Admin ACE/license fallback is limited to trusted staff.
- [ ] `Config.Debug` is disabled unless actively diagnosing.

# Inventory

- [ ] Player inventory persists through disconnect/reconnect.
- [ ] Inventory persists through resource/server restart.
- [ ] Add/remove/move/split operations work correctly.
- [ ] Fast Slots/hotbar and pinned-item rules work.
- [ ] Ground pickup/drop/throw behavior is correct.
- [ ] Container nesting rules match your design.

# Items

- [ ] Item images exist in the expected location.
- [ ] Internal consumable effects/removal are correct.
- [ ] External exports have the correct resource, export and execution side.
- [ ] External handlers return `false` only when use should be cancelled.
- [ ] Runtime item names do not conflict with database-authoritative items.
- [ ] Disabled base items remain disabled after restart.
- [ ] Restore returns base items to the original Lua definition.
- [ ] Metadata visibility does not expose internal/sensitive fields.

# Vehicles

- [ ] Trunks work on common front/rear-storage vehicles.
- [ ] No-trunk vehicles are rejected.
- [ ] Glovebox/trunk capacities match your economy.
- [ ] Addon vehicles have saved placement when required.
- [ ] NPC trunk loot rules are intentional and balanced.

# Integrations

- [ ] Third-party mutations use Nord server exports.
- [ ] Private stashes validate access server-side.
- [ ] `RegisterUsableItem` callbacks work after resource restart.
- [ ] External client and server item-use paths are tested separately.
- [ ] Hooks that can block actions are tested for false positives.
- [ ] Nord Staff integration is tested after Nord Inventory starts.
- [ ] Nord CarPlay uses the client `carplay` export when following the documented install flow.

# Upgrade-specific

- [ ] `Config.Notify`, `Config.TextUI`, `Config.Target` use the current simple provider format.
- [ ] `nord_inventory_disabled_reference_items` exists.
- [ ] No stale or mixed files from older installations remain.


# Downloadable Reference Files

The following reference files are attached to this BookStack import:

- Main `config.lua`.
- Server-only `server/config.lua`.
- `server.cfg.example`.
- Current `sql/nord_inventory.sql`.
- Current `README.md`.
- Full `CHANGELOG.md`.
- `docs/INTEGRATION.md`.
- `docs/EXTERNAL_ITEM_USE.md`.
- DB item persistence notes.
- Item definition precedence notes.
- Resource structure notes.
- Nord CarPlay integration notes.
- Error reference.
- Source-aware delete notes.
- Recommended scripts open-catalog template.

These attachments are documentation/reference copies. Edit the actual files in the FiveM resource when changing production configuration.

# 8 · Integrations & Ecosystem

Verified integration patterns, Nord CarPlay setup and the open recommended-scripts catalog.

# Nord Inventory + Nord CarPlay

# Objective

This integration uses the Nord Inventory `carplay` item to start the Nord CarPlay vehicle-installation flow without consuming the item before the installation is actually accepted.

# Correct architecture

CarPlay begins as a **client-side action** because the player must be checked in the current vehicle/driver context and shown the installation progress. The final install is then validated by the CarPlay server.

For this flow, do **not** configure the item to start from a server-side `UseCarplay` export.

# Admin Studio configuration

Open the `carplay` item and use:

```text
Usable: On
Handler: External export
Resource name: nord_carplay
Export name: carplay
Execution side: Client
Remove on use: Off
```

The item should not be consumed on the first click.

# Item definition

```lua
['carplay'] = {
 label = 'CarPlay',
 weight = 750,
 stack = false,
 close = true,
 useable = true,
 image = 'carplay.png',

 client = {
 export = 'carplay',
 remove = 0
 }
}
```

# Nord CarPlay client export

```lua
exports('carplay', function(item, slot)
 TriggerEvent('nord_carplay:startCarplayInstall')
 return true
end)
```

Returning `true` explicitly is recommended. Nord treats an explicit `false` as a rejection and cancels the item use.

# Installation flow

1. Player selects **Use** on `carplay`.
2. Nord validates the current item/slot and creates a pending use token.
3. Nord calls `nord_carplay:carplay` on the client.
4. CarPlay starts `nord_carplay:startCarplayInstall`.
5. Client validates vehicle/driver context and runs the progress action.
6. CarPlay requests the server-side installation (`nord_carplay:installVehicleCarplay` in the documented flow).
7. CarPlay server validates the installation and removes the item only when the install succeeds.

Because Nord uses `remove = 0`, there is no double-removal or early consumption.

# Runtime handler registration

If `carplay` already exists as a database-authoritative Nord item, `RegisterItem` may correctly return `database_authoritative`. The CarPlay resource can still register the handler:

```lua
exports['nord_inventory']:RegisterUseExport(
 'carplay',
 'carplay',
 'client'
)
```

The bare export name is automatically bound to the calling resource (`nord_carplay`).

Explicit form:

```lua
exports['nord_inventory']:RegisterUseExport(
 'carplay',
 'nord_carplay',
 'carplay',
 'client'
)
```

# Why the old server handler fails

Problem configuration:

```lua
server = {
 export = 'UseCarplay',
 remove = 0
}
```

or Admin Studio:

```text
Resource name: nord_carplay
Export name: UseCarplay
Execution side: Server
```

This asks Nord to execute a server handler as the item-use action. That is the wrong entry point for a flow that needs current client vehicle/driver/progress behavior first.

If that export returns `false`, Nord shows:

```text
External export nord_carplay:UseCarplay rejected the item use.
```

# `Invalid inventory request.`

If this appears during CarPlay use:

1. verify the item still uses `carplay` on **Client**;
2. verify Nord Inventory is a complete current build, not a partial/mixed update;
3. verify `nord_carplay` starts after `nord_inventory`;
4. restart both resources after changing the item handler;
5. inspect the first server/client console error.

# Item image

The active Nord Inventory image must resolve as:

```lua
image = 'carplay.png'
```

and the installed file should exist in Nord Inventory's item image directory:

```text
nord_inventory/web/images/items/carplay.png
```

If Nord CarPlay ships an installer payload such as:

```text
nord_carplay/install/nord_inventory/carplay.png
```

that file must be copied/installed into the Nord Inventory image directory before runtime display.

# Restart order

```text
restart nord_inventory
restart nord_carplay
```

On full server startup:

```cfg
ensure nord_inventory
ensure nord_carplay
```

# Checklist

- [ ] `carplay` item exists and is enabled.
- [ ] Image name is exactly `carplay.png`.
- [ ] Handler is **External export**.
- [ ] Resource is `nord_carplay`.
- [ ] Export is `carplay`.
- [ ] Execution side is **Client**.
- [ ] Nord removal is disabled (`remove = 0`).
- [ ] Client export starts the installation event.
- [ ] Client export does not explicitly return `false` on accepted use.
- [ ] CarPlay server owns final validation and final item removal.
- [ ] Nord Inventory starts before Nord CarPlay.


# Recommended Scripts · Open Catalog

# Recommended Scripts

> This page is intentionally kept as an **open catalog** for verified Nord Inventory integrations.

Only scripts that have been tested with the current Nord Inventory API should be added here. Avoid listing a script as compatible only because it uses generic OX/QB-style inventory calls.

## Official / verified integrations

_Add verified scripts here as they are tested._

| Script | Developer | Integration type | Status | Notes |
|---|---|---|---|---|
| _Example_ | _Developer_ | Native / Bridge / External Use | Verified | _Short note_ |

## Suggested verification checklist

Before adding a script to this page, confirm:

- item registration does not overwrite DB-authoritative definitions;
- runtime items re-register correctly after resource restart;
- `RegisterUsableItem` / `RegisterUseExport` behavior works;
- client/server execution side is correct;
- failed external use does not consume the item;
- inventory mutations happen through server exports;
- metadata survives add/remove/move/restart flows;
- private stashes perform server-side permission checks;
- script works after `restart nord_inventory` and after full server restart.

## Submission template

```text
Script name:
Developer:
Website/Discord:
Integration type:
Frameworks tested:
Features tested:
Known limitations:
Last verification date:
```

## Status labels

Use one of these statuses:

- **Verified** — tested by Nord Lab with the current Nord Inventory API.
- **Developer Verified** — integration confirmed by the script developer.
- **Community Tested** — working reports exist but Nord Lab has not fully verified it.
- **Needs Retest** — compatibility should be validated again after relevant changes to Nord Inventory or the external script.

This page should stay factual. If an integration has not been tested, leave it unlisted until verification is complete.