Docs

kyoJobCreator

Configuration

FileContents
config/shared.luaLanguage, debug, framework, integrations, job injection, multijob, accounts, audit log, Discord, roleplay lines, laundering
config/server.luaACE permissions, rate limiting, spatial checks, payroll, society, actions, billing, custom billing, limits
config/client.luaInteraction, world streaming, markers, actions menu, admin command
config/items.luaWeapon attachments and skins for weapon upgraders

Restart the resource after editing.

config/shared.lua

Basics

locale = 'en',        -- en es fr de pt it pl ro
debug = false,        -- verbose logging; OFF on a live server
framework = 'auto',   -- 'auto' | 'qbox' | 'qbcore' | 'esx'

To add a language, copy locales/en.json and translate it. Anything left out falls back to English.

Integrations

adapters = {
    menu = 'auto',       -- native | ox_lib | esx_default
    notify = 'auto',     -- kyo_notify | ox_lib | okok | vms_notifyv2 | origen | brutal | t_notify | mythic | framework
    inventory = 'auto',  -- ox_inventory | qb_inventory | qs_inventory | origen_inventory | core_inventory | codem_inventory | esx_native
    target = 'auto',     -- ox_target | qb_target
    clothing = 'auto',   -- illenium | fivem_appearance | qb_clothing | esx_skin
    keys = 'auto',       -- qbx_vehiclekeys | qb_vehiclekeys
    society = 'auto',    -- qbx_management | qb_management | esx_addonaccount
    billing = 'auto',    -- origen | qs_billing | okok | ox_billing | qb_phone | esx_billing | custom
    medical = 'auto',    -- qbx_medical | brutal_ambulancejob | qb_ambulancejob | esx_ambulancejob
    identity = 'auto',
},

auto is almost always right. Pin one when two are installed. The adapter id and the resource name both work (qs_inventory = qs-inventory). An unknown name stops the server at boot and lists the valid ones.

  • menu = 'native' uses kyoJobCreator’s own menus (icons, prices, keyboard).
  • notify = 'kyo_notify' uses kyoNotify, the only one that also draws the world prompt.

How jobs reach the framework

injectionStrategy = 'auto', -- 'auto' | 'runtime' | 'persisted' | 'hybrid' | 'none'
unemployedJob = 'unemployed',
unemployedGrade = 0,
ValueMeaning
autoWhatever your framework’s adapter recommends
runtimeRegistered in memory, applied instantly
persistedWritten to the framework’s own tables
hybridBoth
noneNever injected; only works with jobs that already exist

Multijob

multijob = {
    enabled = true,
    maxJobs = 3,
    command = 'jobs',
},

Accounts

accounts = { cash = 'cash', bank = 'bank', dirty = 'dirty' },

Each adapter maps these to its own names. Change only the values, never the keys.

Audit log

audit = {
    database = {
        jobs = true, locations = true, employment = true, wages = true,
        society = true, trade = true, production = true, vehicles = true,
        storage = true,   -- the big one: one row per item moved
        actions = true, panel = true, security = true,
    },
    retentionDays = 90,   -- 0 = no limit
    maxRows = 500000,     -- 0 = no limit (~1 KB per row)
    pruneHours = 6,
    pruneBatch = 5000,
    securityBatchSeconds = 30,
    storagePollSeconds = 3,
},

Turning a category off stops it being stored and searchable. locations is what the undo button uses, and security is the only record of refused requests.

Discord

discord = {
    enabled = false,
    general = '',          -- catch-all channel
    channels = {
        jobs = '', locations = '', employment = '', wages = '', society = '', trade = '',
        production = '', vehicles = '', storage = '', actions = '', panel = '', security = '',
    },
},

A category with its own webhook goes there and nowhere else; one without goes to general; one with neither is not sent.

Roleplay lines

roleplay = { enabled = true, meCommand = 'me', doCommand = 'do' },

The texts are in locales/ as action.<id>.me and action.<id>.do. Leave doCommand empty if your server has no /do.

Money laundering

laundering = {
    enabled = true,
    returnRate = 85,          -- % the player gets back
    maxPerCycle = 100000,
    durationSeconds = 300,
},

It also has to be switched on in the boss point itself.

config/server.lua

Permissions

aceRoot = 'kyo',
aceAdmin = 'kyo.admin',          -- bypasses every individual check
trustFrameworkAdmins = false,    -- treat your framework's admins as kyo.admin
showSetupHelp = true,            -- show the add_ace line to people without access

Rate limiting

Token bucket per player and action group: burst calls allowed back to back, refill tokens per second. Reviving and invoicing are limited the hardest.

Spatial checks

spatial = {
    tolerance = 3.0,           -- added to each point's radius, for lag
    suspiciousDistance = 25.0, -- further than this is logged as suspicious
},

Every interaction is re-checked against the server’s coordinates, never the client’s.

Payroll

payroll = {
    enabled = true,
    interval = 30,           -- minutes between payments
    onDutyOnly = true,
    source = 'society',      -- 'society' (job account) | 'server' (creates the money)
    account = 'bank',
    suppressFramework = false, -- true = only kyoJobCreator pays wages
},

With source = 'society', an empty company account pays nobody. Each job can override the source in its Economy tab.

Society, actions and billing

society = { mirror = true },     -- copy balances into your society resource

actions = {
    requireDuty = true,
    reach = 3.0,
    vehicleReach = 6.0,
    invoice = { min = 1, max = 25000 },
},

billing = {
    enabled = true,
    societyCut = 0.10,
    maxAmount = 50000,
},

Custom billing

For a billing resource with no adapter, set adapters.billing = 'custom' and describe it:

customBilling = {
    resource = 'some_billing',
    kind = 'server-event',       -- 'server-event' | 'client-event' | 'export'
    name = 'some_billing:create',
    args = { 'target', 'amount', 'reason', 'societyKey' },
},

Names you can use in args: target, targetId, identifier, amount, reason, society, societyKey, societyLabel, author, authorSource, resourceName. Anything else is passed as a literal.

Limits

limits = {
    maxJobs = 200,
    maxGradesPerJob = 20,
    maxLocationsPerJob = 100,
    maxJobNameLength = 50,
    maxLabelLength = 64,
    maxRosterPage = 500,
},

config/client.lua

interaction = {
    provider = 'auto',     -- 'auto' | 'target' | 'marker'
    key = 38,              -- E, for markers
    promptDistance = 1.5,
},

world = {
    cellSize = 100.0,      -- grid cell size for the proximity loop
    scanInterval = 1000,
    entityDistance = 50.0, -- distance at which peds and props are created
    modelTimeout = 10000,
},

marker = { type = 1, scale = { x = 1.5, y = 1.5, z = 0.5 }, colour = { r = 46, g = 204, b = 176, a = 160 }, drawDistance = 20.0 },

actions = {
    command = 'jobactions',
    key = 'F6',            -- default only; players rebind it in FiveM's settings
    duration = { repair = 12, wash = 6, lockpick = 8, search = 5, heal = 8, revive = 12 },
    animation = { ... },   -- animation and prop per action
},

command = 'jobcreator',    -- opens the admin panel

config/items.lua

blockIncompatiblePurchase = false, -- refuse parts the weapon in hand cannot take
attachments = {                    -- weapon parts, one list per inventory
    ox_inventory = { ... },
    qb_inventory = { ... },
    default = { ... },
},
skins = {                          -- weapon skins
    tintPrice = 750,                 -- price of a tint, on inventories where skins are tints
    ox_inventory = { ... },
    qb_inventory = { ... },
    default = { ... },
},

Each entry has item, price and label. Your inventory uses its own list, or default if it has none. On inventories where skins are weapon tints (no item behind them), every tint costs tintPrice. Unknown item names are reported in the console at boot.

See Locations.