FiveM Docs
Guides

Create a Job

How to create a job in ESX Legacy step by step, with jobs and job_grades SQL, ESX.CreateJob at runtime, /refreshjobs, /setjob and an esx_society boss menu.

To create a job in ESX Legacy, add one row to the jobs table and one row per rank to the job_grades table, then reload the jobs with /refreshjobs (or restart the server). From a script you can do the same at runtime with ESX.CreateJob. A job only becomes a "company" with a bank account and boss menu when you also register it with esx_society.

Overview

WhatWhere
Job definitionjobs table (name, label, type, whitelisted)
Ranks and salariesjob_grades table, one row per grade
In-memory cacheESX.Jobs, filled by ESX.RefreshJobs() at startup
Assign a job/setjob command or xPlayer.setJob()
Company money and boss menuesx_society + esx_addonaccount (optional)

ESX loads all jobs once when the database is ready. A job that has no rows in job_grades is skipped with a warning, and a player whose saved job or grade does not exist is moved to unemployed grade 0 when they load.

How ESX stores jobs

The jobs table holds one row per job:

ColumnTypeDescription
namevarchar(50)Internal job name, primary key. Used in code, for example police.
labelvarchar(50)Display name, for example LSPD.
typevarchar(50)Free-form category, default civ. The default data uses civ, leo, ems and mechanic. ESX.GetJobs(jobType) can filter by it.
whitelistedtinyint(1)1 hides the job from esx_joblisting (the job center). Default 0.

The job_grades table holds the ranks:

ColumnTypeDescription
idintAuto-increment primary key. Leave it out of your inserts.
job_namevarchar(50)Must match jobs.name.
gradeintRank number, starting at 0.
namevarchar(50)Internal grade name, for example boss. Available as xPlayer.job.grade_name.
labelvarchar(50)Display name of the rank.
salaryintPaycheck amount per interval.
skin_male / skin_femalelongtextJSON uniform data. NOT NULL, so insert '{}' if you have no uniform.

See Database schema for every table ESX uses.

Create the job with SQL

This is the most common way and works on any ESX Legacy server.

Insert the job and its grades

Run this in HeidiSQL, phpMyAdmin or your MySQL client against the ESX database. The example creates a bahamas nightclub job with four ranks.

bahamas.sql
INSERT INTO `jobs` (`name`, `label`, `type`, `whitelisted`) VALUES
  ('bahamas', 'Bahama Mamas', 'civ', 1);

INSERT INTO `job_grades` (`job_name`, `grade`, `name`, `label`, `salary`, `skin_male`, `skin_female`) VALUES
  ('bahamas', 0, 'recruit',  'Recruit',  20,  '{}', '{}'),
  ('bahamas', 1, 'barman',   'Barman',   40,  '{}', '{}'),
  ('bahamas', 2, 'manager',  'Manager',  60,  '{}', '{}'),
  ('bahamas', 3, 'boss',     'Owner',    100, '{}', '{}');

Keep name lowercase without spaces. jobs.name is varchar(50), but users.job is only varchar(20), so job names longer than 20 characters cannot be saved on the player.

Reload the jobs

ESX keeps jobs in memory, so new rows are not visible until you reload them. In the server console (or in game as an admin) run:

refreshjobs

/refreshjobs calls ESX.RefreshJobs(), which reloads both tables and fires the esx:jobsRefreshed server event. A server restart does the same.

Assign the job to a player

setjob 1 bahamas 0

The arguments are [playerId] [job] [grade]. In game you can use me as the player ID: /setjob me bahamas 3. ESX checks the job and grade with ESX.DoesJobExist and rejects unknown combinations. Who can run /setjob is controlled by Config.CommandPermissions (owner and admin by default), see Admin permissions.

Check the result

The /job command prints the player's job, grade label and duty status. It runs on the server, so the output appears in the server console, not in the player's F8 console. To check from a script, read xPlayer.getJob() on the server or ESX.PlayerData.job on the client.

Create a job from a script with ESX.CreateJob

If your resource ships its own job, create it at runtime instead of asking server owners to run SQL. ESX.CreateJob inserts the job and its grades in one transaction and adds them to ESX.Jobs immediately, so no /refreshjobs is needed.

ESX.CreateJob(name, label, grades, jobType)
NameTypeDescription
namestringInternal job name. Required.
labelstringDisplay name. Required.
gradestableList of grades. Each entry has grade, name, label, salary and optional skin_male / skin_female tables. Required.
jobTypestring?Job type. Defaults to civ when it is not a string.

Returns: boolean, true when the rows were inserted.

server/main.lua
ESX = exports['es_extended']:getSharedObject()

CreateThread(function()
    if ESX.DoesJobExist('bahamas', 0) then
        return
    end

    local ok = ESX.CreateJob('bahamas', 'Bahama Mamas', {
        { grade = 0, name = 'recruit', label = 'Recruit', salary = 20 },
        { grade = 1, name = 'barman',  label = 'Barman',  salary = 40 },
        { grade = 2, name = 'manager', label = 'Manager', salary = 60 },
        { grade = 3, name = 'boss',    label = 'Owner',   salary = 100 },
    }, 'civ')

    if not ok then
        print('[bahamas] could not create the job, see the es_extended log above')
    end
end)

Behavior worth knowing:

  • If the job already exists, only grades that do not exist yet are inserted. The existing label and type are kept.
  • If the job and all grades already exist, it prints an error and returns false. Guard the call with ESX.DoesJobExist as above to keep the console clean.
  • On success it fires the server event esx:jobCreated with (name, job).
  • ESX.DoesJobExist and ESX.GetJobs wait until jobs are loaded, so call them from a thread, not at the top level of a file.

Reload a single job

After changing a salary or label directly in the database, ESX.RefreshJob(jobName) reloads just that job and updates every online player who has it (xPlayer.refreshJob()). If the job no longer exists or has no grades, online players with that job are set to unemployed.

server/main.lua
ESX.RefreshJob('bahamas')

Change a player's job in code

server/main.lua
local xPlayer = ESX.GetPlayerFromId(source)
xPlayer.setJob('bahamas', 1)          -- on duty by default (Config.DefaultJobDuty)
xPlayer.setJob('bahamas', 1, false)   -- explicitly off duty

setJob triggers the server event esx:setJob with (source, job, lastJob) and the client event esx:setJob with (job, lastJob). It also writes the job to the player's state bag (Player(source).state.job). React to job changes on the client like this:

client/main.lua
RegisterNetEvent('esx:setJob', function(job, lastJob)
    if job.name == 'bahamas' then
        print(('Now working as %s (%s)'):format(job.grade_label, job.label))
    end
end)

The job table contains name, label, type, onDuty, grade, grade_name, grade_label, grade_salary, skin_male and skin_female. Full method list: xPlayer.

Add a society (company account and boss menu)

A society gives the job a shared bank account, employee management (hire, fire, promote) and optionally money washing. It needs esx_society and esx_addonaccount, started before your job resource.

Create the shared account

bahamas_society.sql
INSERT INTO `addon_account` (`name`, `label`, `shared`) VALUES
  ('society_bahamas', 'Bahama Mamas', 1);

You do not need to insert into addon_account_data: esx_addonaccount creates the missing row for shared accounts (with 0 money) when it starts. Official jobs also add rows to datastore and addon_inventory with the same name; you only need those if your script uses a society stash or datastore.

Register the society on the server

server/main.lua
TriggerEvent('esx_society:registerSociety', 'bahamas', 'Bahama Mamas',
    'society_bahamas', 'society_bahamas', 'society_bahamas', { type = 'private' })

The arguments are name, label, account, datastore, inventory, data. name must equal the job name. The same function is exported: exports.esx_society:registerSociety(...).

Open the boss menu from the client

client/main.lua
TriggerEvent('esx_society:openBossMenu', 'bahamas', function()
    ESX.CloseContext()
end, { wash = false })

Only grades whose name is listed in esx_society's Config.BossGrades can use the menu. The default is ['boss'] = true, which is why the top grade above is named boss.

With Config.EnableSocietyPayouts = true in es_extended, salaries are paid from the society account instead of being created out of thin air. See Money and accounts.

Common issues

Frequently asked questions

Independent documentation

esx-fivem.com is a community project and is not affiliated with the ESX Framework team. Facts on this page were checked against the upstream source. Official resources: docs.esx-framework.org and github.com/esx-framework.
Edit on GitHub

Last updated on

On this page