Installation
Requirements
ox_libox_targetorqb-target(optional: without one, interactions use on-screen prompts)oxmysql(staff, shifts, reports, inspections, certificates and finances; without it those features switch off)- A framework:
qbx_core,qb-coreores_extended ox_inventory(qb-inventory, qs-inventory, codem-inventory, tgiann-inventory and origen_inventory also work)- Optional:
screenshot-basic(photos, see Photo uploads),kartik-mdt(MDT V2),kartik-ambulancejob,kartik-evidence,kartik-ers,pma-voice,illenium-appearance,qbx_management, a vehicle keys resource and a banking resource (see Works with)
Steps
- Download
kartik-firefighterjobfrom your Cfx portal. - Put it in your resources folder.
- Start it after your framework, inventory and MDT in
server.cfg:
ensure ox_lib
ensure oxmysql
ensure ox_target
ensure qbx_core
ensure ox_inventory
ensure kartik-mdt
ensure kartik-firefighterjob- Add the job, the items and the server.cfg lines below.
- Restart the server once so the job and items load. The database tables are created by themselves.
- Join as an admin and follow First Setup.
The fire job
On Qbox, paste into qbx_core/shared/jobs.lua, inside the jobs table (on QBCore, qb-core/shared/jobs.lua takes the same block). On ESX, use the SQL below. Grade 4 (Chief) and above can use station setup, incident commands and the boss pages of the tablet (Config.BossGrade).
['fire'] = {
label = 'Fire Department',
type = 'fire',
defaultDuty = false,
offDutyPay = false,
grades = {
[0] = { name = 'Probationary Firefighter', payment = 50 },
[1] = { name = 'Firefighter', payment = 75 },
[2] = { name = 'Engineer', payment = 100 },
[3] = { name = 'Lieutenant', payment = 125 },
[4] = { name = 'Chief', isboss = true, bankAuth = true, payment = 150 },
},
},Already have a fire job under another name? Add its name to Config.Jobs in shared/config.lua (default { 'fire', 'safd' }).
Items
| Image | Item | What it is |
|---|---|---|
![]() | hydraulic_spreaders | Hydraulic Spreaders: Forces vehicle doors open |
![]() | rescue_saw | Rescue Saw: Cuts through door hinges |
![]() | hydraulic_cutter | Hydraulic Cutter: Cuts door hinges and roof pillars |
![]() | rescue_ram | Rescue Ram: Rolls the dashboard off trapped legs |
![]() | lifting_airbags | Lifting Airbags: Lifts a vehicle or load off a casualty |
![]() | hose_pack | Wildland Hose Pack: 30 m of hose to extend an attack line across rough ground |
![]() | portable_reservoir | Portable Reservoir: Fold-a-tank for tanker shuttles and drafting |
![]() | halligan | Halligan Bar: Smashes windows and forces locks |
![]() | stabiliser | Vehicle Stabiliser: Stops a crashed vehicle from moving |
![]() | ppv_fan | Ventilation Fan: Clears smoke from a fire scene |
![]() | portable_pump | Portable Pump: Draws water from rivers and lakes |
![]() | drip_torch | Drip Torch: Lays controlled burn lines |
![]() | fire_rake | Fire Rake: Clears firebreaks a wildfire cannot cross |
![]() | fire_cone | Fire Dept. Cone: Closes off lanes around a scene |
![]() | fire_barrier | Fire Dept. Barrier: Blocks a road |
![]() | portable_ladder | Portable Ladder: Reach roofs and ledges |
![]() | air_cushion | Rescue Air Cushion: Catches someone falling from height |
![]() | fire_diving_gear | Rescue Diving Gear: Scuba set and dive torch for water rescues |
![]() | fire_axe | Fire Axe: Forces doors and breaks windows |
![]() | pike_pole | Pike Pole: Pulls ceilings to find hidden fire and vent smoke |
![]() | fire_chainsaw | Rescue Chainsaw: Cuts ventilation holes and forces doors |
![]() | thermal_camera | Thermal Imaging Camera: See heat and people through smoke |
![]() | gas_detector | 4-Gas Detector: Reads oxygen, flammable gas, CO and H2S |
![]() | scba_kit | SCBA Mask Kit: Harness, air cylinder and facepiece. Use it to put it on or take it off |
![]() | scba_cylinder | SCBA Cylinder: Full spare air cylinder |
![]() | burn_dressing | Burns Dressing: Sterile dressing for burns |
![]() | spill_kit | Hazmat Spill Kit: Absorbent booms and pads to contain a chemical spill |
![]() | fire_tablet | Fire Department Tablet: Calls, scene map, teams and apparatus |
ground_monitor | Ground Monitor: Placed nozzle for heavy, unmanned water flow | |
oil_absorbent | Oil Absorbent: Granules that soak up oil on the road | |
push_broom | Push Broom: Sweeps up soaked absorbent | |
wildland_mask | Wildland Mask: Cuts smoke on grass fires. No air supply | |
glass_hammer | Glass Hammer: Breaks car windows cleanly before extrication | |
extinguisher_refill | Extinguisher Refill: Refills an empty wall extinguisher | |
extinguisher_mount | Extinguisher Mount: Hangs a new extinguisher on a wall or floor |
The item images are in images/inventory/ of the resource. Copy every PNG to ox_inventory/web/images/.
The fire tools use their own names (fire_cone, fire_barrier, fire_diving_gear) so they don't clash with a police job's cones or qbx_divegear.
Paste into ox_inventory/data/items.lua, inside the returned table:
['hydraulic_spreaders'] = { label = 'Hydraulic Spreaders', weight = 9000, stack = false, close = true, description = 'Forces vehicle doors open', client = { export = 'kartik-firefighterjob.useItem' } },
['rescue_saw'] = { label = 'Rescue Saw', weight = 7000, stack = false, close = true, description = 'Cuts through door hinges', client = { export = 'kartik-firefighterjob.useItem' } },
['hydraulic_cutter'] = { label = 'Hydraulic Cutter', weight = 9000, stack = false, close = true, description = 'Cuts door hinges and roof pillars', client = { export = 'kartik-firefighterjob.useItem' } },
['rescue_ram'] = { label = 'Rescue Ram', weight = 8000, stack = false, close = true, description = 'Rolls the dashboard off trapped legs', client = { export = 'kartik-firefighterjob.useItem' } },
['lifting_airbags'] = { label = 'Lifting Airbags', weight = 12000, stack = false, close = true, description = 'Lifts a vehicle or load off a casualty', client = { export = 'kartik-firefighterjob.useItem' } },
['hose_pack'] = { label = 'Wildland Hose Pack', weight = 6000, stack = true, close = true, description = '30 m of hose to extend an attack line across rough ground', client = { export = 'kartik-firefighterjob.useItem' } },
['portable_reservoir'] = { label = 'Portable Reservoir', weight = 20000, stack = false, close = true, description = 'Fold-a-tank for tanker shuttles and drafting', client = { export = 'kartik-firefighterjob.useItem' } },
['halligan'] = { label = 'Halligan Bar', weight = 4000, stack = false, close = true, description = 'Smashes windows and forces locks', client = { export = 'kartik-firefighterjob.useItem' } },
['stabiliser'] = { label = 'Vehicle Stabiliser', weight = 3000, stack = true, close = true, description = 'Stops a crashed vehicle from moving', client = { export = 'kartik-firefighterjob.useItem' } },
['ppv_fan'] = { label = 'Ventilation Fan', weight = 12000, stack = false, close = true, description = 'Clears smoke from a fire scene', client = { export = 'kartik-firefighterjob.useItem' } },
['portable_pump'] = { label = 'Portable Pump', weight = 15000, stack = false, close = true, description = 'Draws water from rivers and lakes', client = { export = 'kartik-firefighterjob.useItem' } },
['drip_torch'] = { label = 'Drip Torch', weight = 3000, stack = false, close = true, description = 'Lays controlled burn lines', client = { export = 'kartik-firefighterjob.useItem' } },
['fire_rake'] = { label = 'Fire Rake', weight = 2500, stack = false, close = true, description = 'Clears firebreaks a wildfire cannot cross', client = { export = 'kartik-firefighterjob.useItem' } },
['fire_cone'] = { label = 'Fire Dept. Cone', weight = 1500, stack = true, close = true, description = 'Closes off lanes around a scene', client = { export = 'kartik-firefighterjob.useItem' } },
['fire_barrier'] = { label = 'Fire Dept. Barrier', weight = 4000, stack = true, close = true, description = 'Blocks a road', client = { export = 'kartik-firefighterjob.useItem' } },
['portable_ladder'] = { label = 'Portable Ladder', weight = 12000, stack = false, close = true, description = 'Reach roofs and ledges', client = { export = 'kartik-firefighterjob.useItem' } },
['air_cushion'] = { label = 'Rescue Air Cushion', weight = 15000, stack = false, close = true, description = 'Catches someone falling from height', client = { export = 'kartik-firefighterjob.useItem' } },
['fire_diving_gear'] = { label = 'Rescue Diving Gear', weight = 9000, stack = false, close = true, description = 'Scuba set and dive torch for water rescues', client = { export = 'kartik-firefighterjob.useItem' } },
['fire_axe'] = { label = 'Fire Axe', weight = 3500, stack = false, close = true, description = 'Forces doors and breaks windows', client = { export = 'kartik-firefighterjob.useItem' } },
['pike_pole'] = { label = 'Pike Pole', weight = 3000, stack = false, close = true, description = 'Pulls ceilings to find hidden fire and vent smoke', client = { export = 'kartik-firefighterjob.useItem' } },
['fire_chainsaw'] = { label = 'Rescue Chainsaw', weight = 8000, stack = false, close = true, description = 'Cuts ventilation holes and forces doors', client = { export = 'kartik-firefighterjob.useItem' } },
['thermal_camera'] = { label = 'Thermal Imaging Camera', weight = 1500, stack = false, close = true, description = 'See heat and people through smoke', client = { export = 'kartik-firefighterjob.useItem' } },
['gas_detector'] = { label = '4-Gas Detector', weight = 400, stack = false, close = true, description = 'Reads oxygen, flammable gas, CO and H2S', client = { export = 'kartik-firefighterjob.useItem' } },
['scba_kit'] = { label = 'SCBA Mask Kit', weight = 12000, stack = false, close = true, description = 'Harness, air cylinder and facepiece. Use it to put it on or take it off', client = { export = 'kartik-firefighterjob.useItem' } },
['scba_cylinder'] = { label = 'SCBA Cylinder', weight = 6000, stack = true, close = true, description = 'Full spare air cylinder', client = { export = 'kartik-firefighterjob.useItem' } },
['burn_dressing'] = { label = 'Burns Dressing', weight = 200, stack = true, close = true, description = 'Sterile dressing for burns' },
['spill_kit'] = { label = 'Hazmat Spill Kit', weight = 7000, stack = true, close = true, description = 'Absorbent booms and pads to contain a chemical spill' },
['fire_tablet'] = { label = 'Fire Department Tablet', weight = 600, stack = false, close = true, description = 'Calls, scene map, teams and apparatus', client = { export = 'kartik-firefighterjob.useTablet' } },
['ground_monitor'] = { label = 'Ground Monitor', weight = 18000, stack = false, close = true, description = 'Placed nozzle for heavy, unmanned water flow', client = { export = 'kartik-firefighterjob.useItem' } },
['oil_absorbent'] = { label = 'Oil Absorbent', weight = 5000, stack = true, close = true, description = 'Granules that soak up oil on the road' },
['push_broom'] = { label = 'Push Broom', weight = 1500, stack = false, close = true, description = 'Sweeps up soaked absorbent' },
['wildland_mask'] = { label = 'Wildland Mask', weight = 200, stack = false, close = true, description = 'Cuts smoke on grass fires. No air supply', client = { export = 'kartik-firefighterjob.useItem' } },
['glass_hammer'] = { label = 'Glass Hammer', weight = 500, stack = false, close = true, description = 'Breaks car windows cleanly before extrication', client = { export = 'kartik-firefighterjob.useItem' } },
['extinguisher_refill'] = { label = 'Extinguisher Refill', weight = 3000, stack = true, close = true, description = 'Refills an empty wall extinguisher' },
['extinguisher_mount'] = { label = 'Extinguisher Mount', weight = 4000, stack = true, close = true, description = 'Hangs a new extinguisher on a wall or floor', client = { export = 'kartik-firefighterjob.useItem' } },Paste into qb-core/shared/items.lua (or your inventory's items file), inside the items table. Copy the PNGs to your inventory's image folder.
hydraulic_spreaders = { name = 'hydraulic_spreaders', label = 'Hydraulic Spreaders', weight = 9000, type = 'item', image = 'hydraulic_spreaders.png', unique = true, useable = true, shouldClose = true, description = 'Forces vehicle doors open' },
rescue_saw = { name = 'rescue_saw', label = 'Rescue Saw', weight = 7000, type = 'item', image = 'rescue_saw.png', unique = true, useable = true, shouldClose = true, description = 'Cuts through door hinges' },
hydraulic_cutter = { name = 'hydraulic_cutter', label = 'Hydraulic Cutter', weight = 9000, type = 'item', image = 'hydraulic_cutter.png', unique = true, useable = true, shouldClose = true, description = 'Cuts door hinges and roof pillars' },
rescue_ram = { name = 'rescue_ram', label = 'Rescue Ram', weight = 8000, type = 'item', image = 'rescue_ram.png', unique = true, useable = true, shouldClose = true, description = 'Rolls the dashboard off trapped legs' },
lifting_airbags = { name = 'lifting_airbags', label = 'Lifting Airbags', weight = 12000, type = 'item', image = 'lifting_airbags.png', unique = true, useable = true, shouldClose = true, description = 'Lifts a vehicle or load off a casualty' },
hose_pack = { name = 'hose_pack', label = 'Wildland Hose Pack', weight = 6000, type = 'item', image = 'hose_pack.png', unique = false, useable = true, shouldClose = true, description = '30 m of hose to extend an attack line across rough ground' },
portable_reservoir = { name = 'portable_reservoir', label = 'Portable Reservoir', weight = 20000, type = 'item', image = 'portable_reservoir.png', unique = true, useable = true, shouldClose = true, description = 'Fold-a-tank for tanker shuttles and drafting' },
halligan = { name = 'halligan', label = 'Halligan Bar', weight = 4000, type = 'item', image = 'halligan.png', unique = true, useable = true, shouldClose = true, description = 'Smashes windows and forces locks' },
stabiliser = { name = 'stabiliser', label = 'Vehicle Stabiliser', weight = 3000, type = 'item', image = 'stabiliser.png', unique = false, useable = true, shouldClose = true, description = 'Stops a crashed vehicle from moving' },
ppv_fan = { name = 'ppv_fan', label = 'Ventilation Fan', weight = 12000, type = 'item', image = 'ppv_fan.png', unique = true, useable = true, shouldClose = true, description = 'Clears smoke from a fire scene' },
portable_pump = { name = 'portable_pump', label = 'Portable Pump', weight = 15000, type = 'item', image = 'portable_pump.png', unique = true, useable = true, shouldClose = true, description = 'Draws water from rivers and lakes' },
drip_torch = { name = 'drip_torch', label = 'Drip Torch', weight = 3000, type = 'item', image = 'drip_torch.png', unique = true, useable = true, shouldClose = true, description = 'Lays controlled burn lines' },
fire_rake = { name = 'fire_rake', label = 'Fire Rake', weight = 2500, type = 'item', image = 'fire_rake.png', unique = true, useable = true, shouldClose = true, description = 'Clears firebreaks a wildfire cannot cross' },
fire_cone = { name = 'fire_cone', label = 'Fire Dept. Cone', weight = 1500, type = 'item', image = 'fire_cone.png', unique = false, useable = true, shouldClose = true, description = 'Closes off lanes around a scene' },
fire_barrier = { name = 'fire_barrier', label = 'Fire Dept. Barrier', weight = 4000, type = 'item', image = 'fire_barrier.png', unique = false, useable = true, shouldClose = true, description = 'Blocks a road' },
portable_ladder = { name = 'portable_ladder', label = 'Portable Ladder', weight = 12000, type = 'item', image = 'portable_ladder.png', unique = true, useable = true, shouldClose = true, description = 'Reach roofs and ledges' },
air_cushion = { name = 'air_cushion', label = 'Rescue Air Cushion', weight = 15000, type = 'item', image = 'air_cushion.png', unique = true, useable = true, shouldClose = true, description = 'Catches someone falling from height' },
fire_diving_gear = { name = 'fire_diving_gear', label = 'Rescue Diving Gear', weight = 9000, type = 'item', image = 'fire_diving_gear.png', unique = true, useable = true, shouldClose = true, description = 'Scuba set and dive torch for water rescues' },
fire_axe = { name = 'fire_axe', label = 'Fire Axe', weight = 3500, type = 'item', image = 'fire_axe.png', unique = true, useable = true, shouldClose = true, description = 'Forces doors and breaks windows' },
pike_pole = { name = 'pike_pole', label = 'Pike Pole', weight = 3000, type = 'item', image = 'pike_pole.png', unique = true, useable = true, shouldClose = true, description = 'Pulls ceilings to find hidden fire and vent smoke' },
fire_chainsaw = { name = 'fire_chainsaw', label = 'Rescue Chainsaw', weight = 8000, type = 'item', image = 'fire_chainsaw.png', unique = true, useable = true, shouldClose = true, description = 'Cuts ventilation holes and forces doors' },
thermal_camera = { name = 'thermal_camera', label = 'Thermal Imaging Camera', weight = 1500, type = 'item', image = 'thermal_camera.png', unique = true, useable = true, shouldClose = true, description = 'See heat and people through smoke' },
gas_detector = { name = 'gas_detector', label = '4-Gas Detector', weight = 400, type = 'item', image = 'gas_detector.png', unique = true, useable = true, shouldClose = true, description = 'Reads oxygen, flammable gas, CO and H2S' },
scba_kit = { name = 'scba_kit', label = 'SCBA Mask Kit', weight = 12000, type = 'item', image = 'scba_kit.png', unique = true, useable = true, shouldClose = true, description = 'Harness, air cylinder and facepiece. Use it to put it on or take it off' },
scba_cylinder = { name = 'scba_cylinder', label = 'SCBA Cylinder', weight = 6000, type = 'item', image = 'scba_cylinder.png', unique = false, useable = true, shouldClose = true, description = 'Full spare air cylinder' },
burn_dressing = { name = 'burn_dressing', label = 'Burns Dressing', weight = 200, type = 'item', image = 'burn_dressing.png', unique = false, useable = false, shouldClose = true, description = 'Sterile dressing for burns' },
spill_kit = { name = 'spill_kit', label = 'Hazmat Spill Kit', weight = 7000, type = 'item', image = 'spill_kit.png', unique = false, useable = false, shouldClose = true, description = 'Absorbent booms and pads to contain a chemical spill' },
fire_tablet = { name = 'fire_tablet', label = 'Fire Department Tablet', weight = 600, type = 'item', image = 'fire_tablet.png', unique = true, useable = true, shouldClose = true, description = 'Calls, scene map, teams and apparatus' },
ground_monitor = { name = 'ground_monitor', label = 'Ground Monitor', weight = 18000, type = 'item', image = 'ground_monitor.png', unique = true, useable = true, shouldClose = true, description = 'Placed nozzle for heavy, unmanned water flow' },
oil_absorbent = { name = 'oil_absorbent', label = 'Oil Absorbent', weight = 5000, type = 'item', image = 'oil_absorbent.png', unique = false, useable = false, shouldClose = true, description = 'Granules that soak up oil on the road' },
push_broom = { name = 'push_broom', label = 'Push Broom', weight = 1500, type = 'item', image = 'push_broom.png', unique = true, useable = false, shouldClose = true, description = 'Sweeps up soaked absorbent' },
wildland_mask = { name = 'wildland_mask', label = 'Wildland Mask', weight = 200, type = 'item', image = 'wildland_mask.png', unique = true, useable = true, shouldClose = true, description = 'Cuts smoke on grass fires. No air supply' },
glass_hammer = { name = 'glass_hammer', label = 'Glass Hammer', weight = 500, type = 'item', image = 'glass_hammer.png', unique = true, useable = true, shouldClose = true, description = 'Breaks car windows cleanly before extrication' },
extinguisher_refill = { name = 'extinguisher_refill', label = 'Extinguisher Refill', weight = 3000, type = 'item', image = 'extinguisher_refill.png', unique = false, useable = false, shouldClose = true, description = 'Refills an empty wall extinguisher' },
extinguisher_mount = { name = 'extinguisher_mount', label = 'Extinguisher Mount', weight = 4000, type = 'item', image = 'extinguisher_mount.png', unique = false, useable = true, shouldClose = true, description = 'Hangs a new extinguisher on a wall or floor' },Every item above must exist in your inventory. The truck compartment only hands out items the inventory knows, so a missing entry means that tool silently doesn't come out of the truck.
Lighter (world fires): anyone with a lighter can set cars and bins alight through the target eye. To light dry grass from the inventory, give your existing lighter item the export on ox_inventory: client = { export = 'kartik-firefighterjob.useLighter' }. Other inventories: set Config.WorldFires.arson.lighter.registerUsable = true if no other resource uses the lighter.
Firefighters don't need to carry these all the time: every fire truck has an equipment compartment to take them from and put them back.
server.cfg lines
# The hose uses the fire extinguisher for the nozzle animation and the halligan uses the crowbar.
# ox_inventory must not strip them (already have this convar? add both names to your list):
setr inventory:ignoreweapons ["WEAPON_FIREEXTINGUISHER", "WEAPON_CROWBAR"]
# Admin access to /fireadmin
add_ace group.admin kfj.admin allow
# Optional: token for photo uploads (Config.Media.upload)
set kfj_media_token "your-token"ox_inventory wears the fire extinguisher out quickly (durability = 0.006 per use by default). For a firefighter that empties it in seconds, so lower it in ox_inventory/data/weapons.lua:
['WEAPON_FIREEXTINGUISHER'] = {
label = 'Fire Extinguisher',
weight = 8616,
durability = 0.0015, -- lasts about 4 times as long
},Firefighters can also refill it at any fire truck (target the truck → Refill extinguisher).
The ace name is Config.AdminAce in shared/config.lua. Players with the ace can always use the admin and Chief commands.
Fires can throw real game explosions (car cook-off, gas cans, propane, the gas station call). If your anti-cheat blocks script explosions, whitelist explosion types 7, 9, 23, 28 and 34, or turn the hazards off in Config.FireHazards.
Database
Everything is created on first start. oxmysql is required: everything the job saves goes into these tables.
| Table | What it holds |
|---|---|
kfj_shifts | Clocked shifts (hours this week, shift history) |
kfj_reports | Incident reports, draft and filed |
kfj_report_extra | Call timeline and fire investigation for each report |
kfj_inspections | Fire inspections and their result |
kfj_certs, kfj_cert_theory | Certificates and passed theory tests |
kfj_transactions | Department account movements |
kfj_audit | Hires, promotions, dismissals, certificates, money and deleted reports |
kfj_store | The setup: station hardware placed in /fireadmin (lights, speakers, TVs, lockers, garage), turnout outfits, station readiness (truck and equipment condition) and scenes. One row per station, gender or scene |
Player XP and stats are kept in the resource KVP.
Language
Set Config.Locale in shared/config.lua: en, es, fr, de, ar or zh. The interface uses the same file. Locale files are in locales/ (config text in locales/config/). Copy en.json to your language code and translate the values.
ESX job
INSERT INTO jobs (name, label) VALUES ('fire', 'Fire Department');
INSERT INTO job_grades (job_name, grade, name, label, salary, skin_male, skin_female) VALUES
('fire', 0, 'recruit', 'Probationary Firefighter', 50, '{}', '{}'),
('fire', 1, 'firefighter', 'Firefighter', 75, '{}', '{}'),
('fire', 2, 'engineer', 'Engineer', 100, '{}', '{}'),
('fire', 3, 'lieutenant', 'Lieutenant', 125, '{}', '{}'),
('fire', 4, 'boss', 'Chief', 150, '{}', '{}');Changing the interface
The interface is React + Tailwind in source/ and ships built in web/. To change it:
cd source && npm install && npm run buildnpm run dev previews every screen in a browser with fake data (?view=hud, ?view=tablet&page=calls, ?view=panel, ?view=negotiation, ?view=debrief). source/node_modules is only needed for building, you can delete it.



























