Skip to content

DutyKit

DutyKit turns approved duty vehicles into configurable gear access points for your server-defined items, weapons, outfits, kits, and refill flows. Use it for police, EMS, fire, DOT, mechanics, roadside support, or custom jobs that should collect duty equipment from vehicles instead of carrying everything at all times.

  • ox_lib v3.30.0 or newer.

  • One supported framework running on the server:

    • ox_core
    • qbx_core
    • qb-core
    • es_extended
  • One supported inventory adapter set in Config.inventory:

    • ox_inventory
    • codem-inventory
    • qb-inventory
    • origen_inventory
  • One supported target adapter set in Config.target:

    • ox_target
    • qb-target
    • sleepless_interact
  1. Download the latest granted asset from the Cfx.re Portal.

  2. Install and start your selected framework, inventory, target resource, and ox_lib.

  3. Place the kf-dutykit folder in your server resources directory.

  4. Start dependencies before DutyKit.

    server.cfg
    ensure ox_lib
    # Start your framework.
    ensure qbx_core
    # ensure qb-core
    # ensure es_extended
    # ensure ox_core
    # Start your inventory.
    ensure ox_inventory
    # Start your target resource.
    ensure ox_target
    ensure kf-dutykit
  5. Open config.lua and set the inventory, target, and notification adapters for your server.

    config.lua
    Config.inventory = 'ox_inventory'
    Config.target = 'ox_target'
    Config.notifications = true
    Config.notify = 'ox_lib'
  6. Add or edit loadout files in loadouts/, then register those files in loadouts/index.lua.

DutyKit adds vehicle interactions for loadouts and refills through your configured target resource. Players open the interaction from an approved vehicle, then use the menu to browse Quick Access, all loadouts, or the custom categories defined in Config.categories.

A loadout can contain either inventory items or clothing. DutyKit checks vehicle access, ped model access, group access, distance, and item limits server-side before giving equipment or applying an outfit.

Quick Access is controlled per item or clothing entry with quick = true. allowEquipAll = true enables the bulk equip option for a loadout.

config.lua
Config.inventory = 'ox_inventory'
Config.target = 'ox_target'
Config.notifications = true
Config.notify = 'ox_lib'
Config.categories = { 'Items', 'Outfits' }
Config.debug = false
Config.loadoutMenuThemeDefaultPreset = 'police'
Config.autoEquipWeapon = true
Config.checkVehicleLocked = true
Config.refillTime = '1 hour'
OptionPurpose
Config.inventorySelects the inventory adapter.
Config.targetSelects the target adapter used for vehicle interactions.
Config.notificationsEnables or disables DutyKit notifications.
Config.notifySelects the notification adapter. Supported values are ox_lib, esx_notify, qb-core, qbx_core, okokNotify, and wasabi_notify.
Config.categoriesAdds custom menu categories after Quick Access and Loadouts. Loadout category values should match these labels.
Config.debugEnables debug logging and the /dumpappearance helper command. Disable this in production.
Config.loadoutMenuThemeDefaultPresetDefault UI theme preset used by the custom DutyKit menu.
Config.autoEquipWeaponAuto-equips retrieved weapon items when the inventory adapter supports slot use.
Config.checkVehicleLockedBlocks access when the target vehicle is locked.
Config.refillTimeCooldown text parsed by the refill flow, for example 10 minutes, 1 hour, or 2 days.

DutyKit includes configurable menu theme presets in Config.loadoutMenuThemePresets. The default config includes presets such as default, police, ems, and fire.

Set a loadout theme by name:

theme = 'police'

Or add your own preset in Config.loadoutMenuThemePresets:

config.lua
Config.loadoutMenuThemePresets.custom = {
selectedColor = 'rgba(10, 10, 10, 0.7)',
selectedBorderColor = 'rgba(255, 255, 255, 0.7)',
selectedColorLine = 'rgba(255, 255, 255, 1.0)',
buttonGradientFrom = 'rgba(30, 30, 30, 0.7)',
buttonGradientTo = 'rgba(50, 50, 50, 0.5)',
textColor = 'rgba(240, 240, 240, 1.0)',
disabledTextColor = 'rgba(150, 150, 150, 1.0)',
indicatorColor = 'rgba(240, 240, 240, 1.0)',
}

Use groups to restrict a loadout or refill station to jobs, gangs, or framework groups.

-- Any listed group can access.
groups = { 'police', 'sheriff' }
-- Minimum grade requirements.
groups = { police = 2, sheriff = 1 }
-- Mixed plain and graded access.
groups = { police = 2, sheriff = true }
-- ESX also supports grade name checks.
groups = { police = 'boss' }

Framework behavior:

FrameworkGroup source
ox_coreOx player groups.
qbx_corePlayer job, gang, or native Qbox groups.
qb-corePlayer job and gang.
es_extendedPlayer job, including numeric grade or grade name.

DutyKit includes an in-game loadout editor for creating and maintaining loadouts. This is the expected workflow for most server owners because it avoids manually writing each loadout table from scratch.

Use the /dutykit command in-game to open the editor. From there, you can build loadouts, assign categories, items, outfits, vehicles, groups, limits, Quick Access entries, and themes, then save the result. DutyKit stores those saved loadouts as Lua files inside loadouts/.

DutyKit stores loadouts as individual Lua files in loadouts/. The in-game editor can create and update these files for you, but understanding the structure is useful for developers, Git review, and advanced manual edits.

DutyKit loads loadouts from loadouts/index.lua. Each index entry points to a Lua file in loadouts/, and each loadout file returns one table.

When manually adding, renaming, or removing loadout files, run /dutykit rebuild in-game to rebuild loadouts/index.lua so DutyKit knows which loadout files should be loaded.

loadouts/index.lua
return {
{
file = 'ld_police_patrol-kit.lua',
id = 'ld_police_patrol',
},
}
loadouts/ld_police_patrol-kit.lua
return {
category = 'Items',
label = 'Patrol Weapon Kit',
type = 'police',
theme = 'police',
groups = {
'police',
'sheriff',
},
allowedVehicles = {
'police',
'police2',
'police3',
'sheriff',
'sheriff2',
},
allowEquipAll = true,
items = {
{
itemName = 'WEAPON_COMBATPISTOL',
ammo = 72,
componentItemNames = {
'at_flashlight',
},
tint = 0,
max = 1,
quick = true,
},
{
itemName = 'WEAPON_STUNGUN',
max = 1,
quick = true,
},
{
itemName = 'ammo-9',
max = 8,
},
},
}
loadouts/ld_police_patrol-uniform.lua
return {
category = 'Outfits',
label = 'Patrol Uniform',
type = 'police',
theme = 'police',
groups = {
'police',
'sheriff',
},
allowedVehicles = {
'police',
'police2',
'police3',
'sheriff',
'sheriff2',
},
allowedPeds = {
'mp_m_freemode_01',
'mp_f_freemode_01',
},
allowEquipAll = true,
clothing = {
{
label = 'Short Sleeve Arms',
componentId = 3,
drawable = 19,
texture = 0,
quick = true,
},
{
label = 'Duty Pants',
componentId = 4,
drawable = 35,
texture = 0,
quick = true,
},
{
label = 'Duty Cap',
propId = 0,
drawable = 46,
texture = 0,
quick = true,
},
},
}
FieldDescription
categoryMenu category. Should match a value in Config.categories.
labelDisplay name shown in the DutyKit menu.
typeOptional family key used to pair loadouts with refill stations.
themeTheme preset name or inline theme table.
groupsOptional access filter. Omit it for unrestricted access.
allowedVehiclesVehicle spawn names allowed to open this loadout. Omit or leave empty to allow any vehicle.
allowedPedsOptional ped model allowlist for outfit or role-specific loadouts.
allowEquipAllEnables Equip All or Equip Quick menu actions.
itemsInventory items, weapons, ammo, supplies, or tools.
clothingOutfit components and props.
resetMissingClothingFor outfit loadouts, set false to avoid clearing missing clothing slots during Equip All.
FieldDescription
itemNameInventory item name. Use the exact item key from your inventory.
maxMaximum number of this item that can be retrieved from the same vehicle state before it is returned or refilled.
ammoWeapon ammo metadata.
componentItemNamesWeapon component item names.
tintWeapon tint metadata.
quickShows the item in Quick Access.
autoEquipWeaponSet false to prevent auto-equip for this item when Config.autoEquipWeapon is enabled.
FieldDescription
labelDisplay label in the menu.
componentIdGTA clothing component ID.
propIdGTA prop ID. Use propId = -1 to clear all props for that entry.
drawableDrawable variation.
textureTexture variation.
quickShows the clothing entry in Quick Access.

Configure static refill points in Config.refillStations. Use matching type values when a station should only refill a specific loadout family.

config.lua
Config.refillStations = {
{
label = 'Police Armory',
type = { 'police' },
groups = { 'police', 'sheriff' },
coords = vector3(-1833.0654, -3150.6802, 13.0),
distance = 3.0,
marker = {
enabled = true,
scale = vector3(3.0, 3.0, 3.0),
type = 1,
color = { r = 255, g = 0, b = 0, a = 70 },
},
drawText = true,
blip = {
enabled = true,
sprite = 110,
color = 3,
scale = 0.8,
},
},
}
FieldDescription
labelMenu, marker, or blip label.
typeOptional station type. Pair it with loadout type values.
groupsOptional access filter for the station.
coordsWorld position for the refill station.
distanceInteraction distance. Defaults to 10 if omitted.
drawTextEnables station draw text when supported by the client flow.
markerOptional FiveM marker configuration. See Marker fields.
blipOptional map blip configuration. See Blip fields.

Use marker to draw a FiveM marker at the refill station.

marker = {
enabled = true,
scale = vector3(3.0, 3.0, 3.0),
type = 1,
direction = vector3(0.0, 0.0, 0.0),
rot = vector3(0.0, 0.0, 0.0),
color = { r = 255, g = 0, b = 0, a = 70 },
bobUpAndDown = false,
faceCamera = false,
rotate = false,
textureDict = nil,
textureName = nil,
}

Use blip to create a map blip for the refill station.

blip = {
enabled = true,
sprite = 110,
color = 3,
scale = 0.8,
label = 'Police Armory',
}
FieldDescription
enabledEnables or disables the map blip.
spriteBlip sprite ID. Defaults to 110 if omitted.
colorBlip color ID. Defaults to 3 if omitted.
scaleBlip size. Defaults to 0.8 if omitted.
labelOptional custom blip label. If omitted, DutyKit uses the refill station label.
  • DutyKit adds target options to vehicle boot/trunk access for loadouts and refills.
  • Vehicle access checks are performed again on the server before giving items or applying loadouts.
  • The server rejects access when the player is too far from the target vehicle.
  • If Config.checkVehicleLocked is enabled, locked vehicles cannot be opened for DutyKit access.
  • If the configured trunk door is unavailable, DutyKit falls back to rear doors where possible.

The following files are escrow-ignored so you can adapt DutyKit to custom server stacks without editing locked code:

  • config.lua
  • loadouts/*.lua
  • server/framework/*.lua
  • server/inventory/*.lua
  • client/framework/*.lua
  • client/inventory/*.lua
  • client/interface/*.lua
  • client/target/*.lua

Inventory adapters are expected to provide:

-- Client
ClientFramework.getItem(itemName)
ClientFramework.getItems()
ClientFramework.useSlot(slot)
-- Server
ServerFramework.getItem(itemName)
ServerFramework.getItems()
ServerFramework.addInventoryItem(player, itemName, amount, metadata)
ServerFramework.removeInventoryItem(player, itemName, amount, metadata, slot)
ServerFramework.getInventoryItemBySlot(player, slot)
ServerFramework.getSlotIdWithItem(player, itemName, metadata)

Framework adapters are expected to provide:

GetPlayer(source)
ServerFramework.IsPlayerInGroup(player, groups)
ClientFramework.IsPlayerInGroup(groups)

Check that:

  • Config.target matches the target resource you are running.
  • The target resource starts before kf-dutykit.
  • The vehicle spawn name is listed in allowedVehicles.
  • The player is in one of the configured groups.
  • The player’s ped model is allowed by allowedPeds, if set.
  • The vehicle is not locked when Config.checkVehicleLocked = true.

Check that:

  • Config.inventory matches your inventory resource.
  • The inventory resource starts before kf-dutykit.
  • Every itemName exactly matches an item registered in your inventory.
  • Weapon component item names exist in your inventory when using componentItemNames.

Enable debug mode, put on the desired outfit, run /dumpappearance, then copy the printed component and prop rows into your outfit loadout file.

Players can see a loadout but cannot retrieve items

Section titled “Players can see a loadout but cannot retrieve items”

Check server logs for access-denied or inventory adapter errors. The server validates the entity, distance, group filter, vehicle allowlist, ped allowlist, and inventory add result before completing the request.