FiveM & QBCore · Practical guide

QBCore Mechanic Job Script — Practical Guide

Build a server-authoritative QBCore mechanic job with repairs, tows, and impound actions. Implementation plan, complete destination-labelled files, installation, validation and security.

Stellar AI · Updated 8 September 2026 · 5 min read

This guide walks you through creating a server-authoritative QBCore mechanic job resource: repair, tow, and impound actions, qb-target interactions, and safe server-side validation. You'll get a short plan, assumptions, complete files to drop into a resource, installation steps, testing checklist, and security considerations.

Implementation plan

1) Define config and permissions. 2) Register qb-target interactions (repair/tow/impound). 3) Use server callbacks to check job, charge money, and record impounds. 4) Client performs the local vehicle actions only after server approval. 5) Provide a simple persistent impound hook placeholder (oxmysql or your vehicle ownership system).

Assumptions and dependencies

  • Server uses QBCore (v1/v2 style exports). Adjust GetCoreObject line to match your core.
  • qb-target or ox_target installed for targeting interactions.
  • Optional: oxmysql or your own persistence if you want permanent impound storage — otherwise impounds will be ephemeral.
  • The mechanic is a job named "mechanic" in your jobs config. Change job name in config.lua if different.

Destination-labelled files (complete)

Create a resource folder called qb-mechanic. Place these files exactly as named.

fxmanifest.lua

fx_version 'cerulean'
game 'gta5'

author 'YourName'
description 'QBCore mechanic job (repair, tow, impound)'
version '1.0.0'

shared_script 'config.lua'

client_script 'client/main.lua'
server_script 'server/main.lua'

dependencies {
  'qb-core',
  'qb-target'
}

config.lua

Config = {}
Config.JobName = 'mechanic'
Config.RepairCost = 150 -- cash amount
Config.TowCost = 250
Config.ImpoundCost = 500
Config.ImpoundLocation = vector3(412.0, -1634.0, 29.3) -- example
Config.TargetDistance = 3.0

client/main.lua

local QBCore = exports['qb-core']:GetCoreObject()

-- qb-target registration for nearby vehicles (run on client start)
CreateThread(function()
    while not NetworkIsSessionStarted() do
        Wait(100)
    end

    -- register an entity target for vehicles the player is near
    exports['qb-target']:AddTargetModel(GetHashKey('a_m_m_business_01'), {
        options = {
            {
                type = 'client',
                event = 'qb-mechanic:client:OpenMechanicMenu',
                icon = 'fas fa-wrench',
                label = 'Mechanic Actions',
            }
        },
        distance = 3.0
    })
end)

-- Client event opens actions and coordinates server validation
RegisterNetEvent('qb-mechanic:client:OpenMechanicMenu', function()
    local playerPed = PlayerPedId()
    local veh = GetVehiclePedIsIn(playerPed, true)
    local plate = veh ~= 0 and QBCore.Functions.GetPlate(veh) or nil

    local elements = {}
    table.insert(elements, {label = 'Repair Vehicle ($' .. Config.RepairCost .. ')', value = 'repair'})
    table.insert(elements, {label = 'Tow Vehicle ($' .. Config.TowCost .. ')', value = 'tow'})
    table.insert(elements, {label = 'Impound Vehicle ($' .. Config.ImpoundCost .. ')', value = 'impound'})

    -- simple menu using ox_lib or your UI. Here we call server directly per choice.
    -- Replace this menu with your preferred UI. For example purposes we'll use a native input.

    local choice = nil
    -- (Replace the following placeholder with your UI selection implementation.)
    -- For demo, we will always pick repair if in a vehicle, otherwise impound is skipped.
    if veh ~= 0 then
        choice = 'repair'
    else
        QBCore.Functions.Notify('Stand near a vehicle to use mechanic actions', 'error')
        return
    end

    if choice == 'repair' then
        -- ask server to charge and approve
        QBCore.Functions.TriggerCallback('qb-mechanic:server:ChargeForService', function(success)
            if success then
                -- local repair action: server authorised
                SetVehicleFixed(veh)
                SetVehicleDirtLevel(veh, 0.0)
                QBCore.Functions.Notify('Vehicle repaired', 'success')
            else
                QBCore.Functions.Notify('You cannot perform this action', 'error')
            end
        end, 'repair')
    end
end)

server/main.lua

local QBCore = exports['qb-core']:GetCoreObject()
local Impounded = {} -- in-memory store; replace with DB for persistence

QBCore.Functions.CreateCallback('qb-mechanic:server:ChargeForService', function(source, cb, serviceType)
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then cb(false); return end

    -- job validation
    if Player.PlayerData.job.name ~= Config.JobName then
        cb(false)
        return
    end

    local cost = 0
    if serviceType == 'repair' then cost = Config.RepairCost
    elseif serviceType == 'tow' then cost = Config.TowCost
    elseif serviceType == 'impound' then cost = Config.ImpoundCost
    else cb(false); return end

    -- check money and remove
    if Player.Functions.RemoveMoney('cash', cost) then
        cb(true)
    else
        cb(false)
    end
end)

-- Impound example: record an impound and notify
RegisterNetEvent('qb-mechanic:server:ImpoundVehicle', function(plate, model, owner)
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end
    if Player.PlayerData.job.name ~= Config.JobName then return end

    -- place into in-memory table; replace with oxmysql insert for persistence
    table.insert(Impounded, {plate = plate, model = model, impoundedBy = Player.PlayerData.citizenid, time = os.time()})
    TriggerClientEvent('QBCore:Notify', src, 'Vehicle impounded: ' .. plate, 'success')

    -- TODO: save to DB via exports.oxmysql:insert(...) if you want persistence across restarts
end)

Where files belong and install

  1. Create a folder resources/[your-folder]/qb-mechanic and drop fxmanifest.lua, config.lua, client/main.lua, server/main.lua inside.
  2. Add start qb-mechanic to your server.cfg (ensure qb-core and qb-target are started first).
  3. Restart server or resource and test.

Testing & validation checklist

StepWhat to expect
Start resourceNo startup errors in server console
Open mechanic menu near a vehicleMenu opens and repair option available
Repair actionMoney removed server-side and vehicle fixed client-side
Impound actionEntry added to server Impounded table (or DB if integrated)
Unauthorized job tryAction rejected and no money removed

Security considerations

  • Never let the client remove or add money. Server callbacks must do all monetary operations (as shown).
  • Validate job and permissions on the server for every paid action.
  • If you need to validate vehicle ownership before impound, query your ownership DB server-side rather than trusting client-sent plate strings.
  • For persistence use oxmysql or your preferred DB with proper prepared statements to avoid injection.
  • Remember that the client still performs local vehicle fixes. For strict enforcement, set a server-side state that flags repaired vehicles and reject inconsistent updates from anti-cheat logic.

Next steps and useful integrations

  • Integrate a full UI (ox_lib or your custom NUI) for selecting actions and showing receipts.
  • Hook impound storage into your owned_vehicles or a dedicated impound table in the DB for persistent retrieval.
  • Add job blips, ranks, and a garage for professional tools and upgradeable equipment.

If you'd like help designing the full feature set or producing destination-labelled files for an extended mechanic resource (NUI, persistent DB, tow-trailer handling), try building the plan in the Stellar AI app and iterate quickly. The app helps translate plain-English requirements into complete resource files you can drop into your server. After that, check our walkthroughs on the blog for integration patterns: Stellar AI blog.

When you're ready to prototype multi-role flows or export a staged resource, open the generator in the Stellar AI app to prototype file sets and installation instructions you can copy directly into your FiveM server.

Build your next system with Stellar AI

Describe one feature, get organized project files, then bring back your errors to keep improving. Start free with no card required. Test generated code in a private development environment before release.

Create your first script free →