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
| What | Where |
|---|---|
| Job definition | jobs table (name, label, type, whitelisted) |
| Ranks and salaries | job_grades table, one row per grade |
| In-memory cache | ESX.Jobs, filled by ESX.RefreshJobs() at startup |
| Assign a job | /setjob command or xPlayer.setJob() |
| Company money and boss menu | esx_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:
| Column | Type | Description |
|---|---|---|
name | varchar(50) | Internal job name, primary key. Used in code, for example police. |
label | varchar(50) | Display name, for example LSPD. |
type | varchar(50) | Free-form category, default civ. The default data uses civ, leo, ems and mechanic. ESX.GetJobs(jobType) can filter by it. |
whitelisted | tinyint(1) | 1 hides the job from esx_joblisting (the job center). Default 0. |
The job_grades table holds the ranks:
| Column | Type | Description |
|---|---|---|
id | int | Auto-increment primary key. Leave it out of your inserts. |
job_name | varchar(50) | Must match jobs.name. |
grade | int | Rank number, starting at 0. |
name | varchar(50) | Internal grade name, for example boss. Available as xPlayer.job.grade_name. |
label | varchar(50) | Display name of the rank. |
salary | int | Paycheck amount per interval. |
skin_male / skin_female | longtext | JSON 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.
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 0The 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)| Name | Type | Description |
|---|---|---|
name | string | Internal job name. Required. |
label | string | Display name. Required. |
grades | table | List of grades. Each entry has grade, name, label, salary and optional skin_male / skin_female tables. Required. |
jobType | string? | Job type. Defaults to civ when it is not a string. |
Returns: boolean, true when the rows were inserted.
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 withESX.DoesJobExistas above to keep the console clean. - On success it fires the server event
esx:jobCreatedwith(name, job). ESX.DoesJobExistandESX.GetJobswait 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.
ESX.RefreshJob('bahamas')Change a player's job in code
local xPlayer = ESX.GetPlayerFromId(source)
xPlayer.setJob('bahamas', 1) -- on duty by default (Config.DefaultJobDuty)
xPlayer.setJob('bahamas', 1, false) -- explicitly off dutysetJob 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:
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
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
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
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
The job row exists but job_grades has no rows with that job_name. Check spelling and case; job_name must match jobs.name exactly.
Either the grade number does not exist for that job or ESX has not reloaded jobs yet. Run refreshjobs and try again.
On load, ESX validates the saved users.job and users.job_grade. If you renamed or deleted a job or grade, those players are reset to unemployed grade 0. A missing unemployed grade 0 is replaced by a built-in fallback, but keep it in the database.
esx_society must be started before your job resource, the society must be registered with the same name as the job, the society_<job> addon account must exist with shared = 1, and the player's grade name must be in Config.BossGrades.
Related
Frequently asked questions
Independent documentation
Last updated on
esx_weather
esx_weather syncs weather and time per zone (Los Santos, Sandy Shores, Paleto Bay) on ESX Legacy, with an admin panel, persistence and sync toggle exports.
Add Items
How to add items in ESX Legacy, with the items table SQL, weight, rare and can_remove, ESX.AddItems, usable items via ESX.RegisterUsableItem and ox_inventory notes.