How Integrations Work#
Everything that depends on your framework sits behind two small, unencrypted bridge files — one for the client, one for the server — and the resource loads the one that matches what is running. The rest is optional and found on its own:
Layer | Supported | Without it |
|---|---|---|
Framework |
| Required. There is no standalone mode. |
Database |
| Required. |
Library |
| Required: callbacks, salary and hiring notifications, scheduled resets. |
3D prompt |
| A drawn text label over the boss zone. |
Job levels | Eleven Quasar job resources | Cards and employee rows show no level or XP. |
Job payouts | Any resource that calls | The job still works; its treasury and earnings statistics stay empty. |
Notifications | Any system, through | Some messages are silent until you pick one. |
Character names |
| Names read Unknown in menus and logs. |
Discord | One webhook URL | No Discord logs; the HR and server logs still record everything. |
Every optional integration degrades silently. A missing resource makes its feature stand down — it never stops the job menu from opening.
Frameworks#
The framework is detected from the started resources the moment Job Management starts. Run exactly one.
Players are tracked by their ESX identifier, and a job change is caught from esx:setJob. Ranks are read from and written to job_grades, jobs to jobs, and ESX reloads them at once — every change made in game is permanent.
Names come from
esx_identity's first and last name.Admins are the
adminandsuperadmingroups.Money is paid into the
money(cash) account.
Players are tracked by their citizenid, and a job change is caught from QBCore:Client:OnJobUpdate. Ranks are read from QBCore.Shared.Jobs and written through the core's own job functions.
Names come from the character's
charinfo.Admins are whoever the core allows the
adminpermission.Money is paid as
cash.Jobs and ranks created or edited in game last until the next restart — copy them into
qb-core/shared/jobs.luato keep them.
Qbox is detected from qbx_core and runs the QBCore bridge: players are tracked by citizenid, jobs are read from and written to qbx_core through its exports, and job updates made by other resources are picked up as they happen.
Names, admin check and cash work as on QBCore.
Jobs and ranks created or edited in game last until the next restart — copy them into Qbox's shared jobs file to keep them.
Notifications#
SendTextMessage(msg, type) in config/config.lua shows the maximum-jobs warning, the license refusal, a boss being told an employee quit, a player being told they received a treasury payment, and the result of a hire. It ships with every line commented out, so those messages are silent until you choose a system. type is info, success or error.
-- config/config.lua
function SendTextMessage(msg, type)
lib.notify({
description = msg,
type = type == 'info' and 'inform' or type,
})
end-- config/config.lua
function SendTextMessage(msg, type)
SetNotificationTextEntry('STRING')
AddTextComponentString(msg)
DrawNotification(0, 1)
end-- config/config.lua
function SendTextMessage(msg, type)
exports['your-notify']:Alert(msg, type)
endSalary payments and hiring results also use ox_lib's notifications directly, so those appear whatever you choose here.
The 3D Prompt#
When qs-textui is started, the [E] Manage prompt over a boss zone is drawn by it, with its key hint. Without it, a plain label with a dark backdrop is drawn instead. There is nothing to configure.
Quasar Job Resources#
These resources keep each worker's level and experience in their own table, and Job Management reads it — only while the resource is started — to show levels in the job menu and the boss menu:
Job name | Resource | Table |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The job name must be the one those resources give their workers. Config.JobStatsTables holds the same pairs for the boss menu, and Config.JobProgress the XP curve of each — keep both in step with the job scripts' own config.
Any job resource — Quasar's or your own — that pays its workers through AddJobMoney feeds the job's treasury and statistics. See Commands and Exports.
Discord Logs#
Paste a channel webhook into config/config.lua and job events are posted there as embeds, under the name QS MultiJob. Leave it empty to send nothing.
Config.DiscordWebhook = "https://discord.com/api/webhooks/0000000000/xxxxxxxxxxxx"Embed | Sent when |
|---|---|
Employee Hired | A job offer is accepted, or an admin adds a job to a player. Marked |
Employee Fired | A boss, a rank with Fire, an admin or a rescinded contract removes someone. |
Grade Changed | An employee's rank changes, with the old and new grade. |
Penalty Applied | A penalty is added, with its reason. |
Treasury | A boss or an admin pays an employee from the treasury. |
Boss Assigned · Boss Removed · Boss Resigned | Any change of boss. |
Announcement | A boss or an admin sends an announcement. |
Job Created · Job Deleted | From the admin panel's Manage Jobs. |
Hires through a signed contract, cut and tax changes, added funds and permission changes are kept in the HR and server logs rather than sent to Discord.
Custom Frameworks and Checks#
The bridges in client/framework/ and server/framework/ are open to edit. The server bridge provides a handful of functions the rest of the resource relies on:
Function | Returns |
|---|---|
| The framework's player object, or |
| The character identifier stored in |
| The server ids of loaded players. |
| The character's name, used in menus and logs. |
| Whether the player may use the admin panel. |
| Write a job and its ranks to the core, or remove it. |
A fork of ESX or QBCore that keeps their player object works by adjusting these functions. If your core announces job changes with a different event, forward it from the client bridge so the job still reaches the player's list:
-- client/framework/esx.lua or qb.lua
RegisterNetEvent('mycore:jobChanged', function(job)
PlayerJob = job
TriggerServerEvent('qs-job-management:add', job.name, job.grade)
end)Languages#
Nineteen translations ship in locales/: English (en), Spanish (es), French (fr), German (de), Portuguese (pt), Italian (it), Dutch (nl), Danish (da), Hungarian (hu), Romanian (ro), Russian (ru), Bulgarian (bg), Turkish (tr), Arabic (ar), Persian (fa), Hindi (hi), Japanese (ja), Korean (ko) and Simplified Chinese (zh-CN). Set Config.Locale to the file name.
Near misses are accepted:
DEloads German, a regional code such aspt-BRfalls back topt, andzhloadszh-CN.Any text missing from a translation is shown in English instead of disappearing.
A file saved by Notepad or PowerShell with a byte-order mark still loads.
To add a language, copy en.json, translate the values, save it as locales/<code>.json and set Config.Locale to that code.



