NX CreativeNX CreativeDocs
Scriptsnx_employeemanagement

Configuration

Every entry in shared/config.lua. Defaults, types, and purpose.

All settings live in shared/config.lua. The file is in escrow_ignore so it remains editable after install. UI translations live in locales/<lang>.lua, one file per language, also unescrowed. See Localization below.

Webhook URLs live in a separate shared/server-config.lua file that is loaded only on the server. See Webhooks below.

Core

Config.Frameworkstringdefault: 'ESX'

Framework identifier. One of 'ESX', 'QB', 'QBX'.

Config.Inventorystringdefault: 'OX'

Inventory backend for the tablet item. One of 'OX', 'QB'.

Config.Targetstringdefault: 'OX'

Target resource identifier. Currently only 'OX' is supported.

Config.Languagestringdefault: 'en'

Active UI language. Selects the matching locales/<lang>.lua file. Shipped: 'en', 'fi', 'es', 'fr', 'it', 'de', 'sv'.

Integrations

Config.Integrations.MultiJobstringdefault: 'none'

Optional multi-job backend. One of 'none', 'core_multijob', 'nx_phone'. With 'nx_phone', a player's jobs live in nx_phone's Shift app: hires, grade changes and fires go through nx_phone, and the player switches the job they work from the phone. If nx_phone is not started, the server console warns and the tablet behaves as 'none'.

Config.Integrations.MultiJobTablestringdefault: 'user_jobs'

Database table name for the core_multijob integration.

Config.Integrations.MultiJobFallbackJobstringdefault: 'unemployed'

Job assigned when an employee has no other jobs remaining.

Custom job providers

Bring standalone job scripts (SouF UwU Cafe, BurgerShot, any self-contained job resource) into the tablet next to framework jobs. A player who manages more than one business picks one on open, then lands on that business's dashboard. Membership and grade for a custom job are read live from the external script while the player is online, through its public exports or a statebag, so nothing touches escrowed internals.

Hiring, firing, and promotion are read-only by default. Keep staffing in the external script's own boss menu, or opt in per job with a write adapter. Payroll and Society are off by default so you do not double-handle the external economy. See INSTALLATION.md, section "Custom Job Providers", for the full field guide and its limits (online-only live grade, tracked roster, opt-in writes).

Config.CustomJobs.Enabledbooleandefault: false

Master toggle for the whole custom-job system.

Config.CustomJobs.Jobstable[]default: {}

One entry per external business. Fields per entry: job (stable key this resource stores hours, payroll, society, and permissions under, need not exist in jobs.lua), label (display name), enabled (boolean), grades (table keyed by grade level, each holding name, salary, and optional isBoss), detect (how an online member and grade are resolved, mode 'export' or 'statebag' plus the export names or statebag keys), roster ('tracked' for this resource's own hours or 'table' for an external database table), actions (write-back adapter, mode 'none', 'event', 'command', or 'export', one binding per hire, fire, and setGrade), society (nil to hide the Society tab, or a { backend, account } table to bind it), payroll (boolean, enables tablet payroll for this job), trackHours (boolean, records worked hours for members).

Config.CustomJobs = {
    Enabled = true,
    Jobs = {
        {
            job     = "burgershot",
            label   = "Burger Shot",
            enabled = true,
            grades = {
                [0] = { name = "Trainee",          salary = 150 },
                [3] = { name = "Shift Supervisor", salary = 250 },
                [4] = { name = "Manager",          salary = 300, isBoss = true }
            },
            detect = {
                mode           = "export",
                resource       = "sf_burgershot",
                hasJob         = "hasJob",
                hasJobAndGrade = "hasJobAndGrade"
            },
            roster     = { mode = "tracked" },
            actions    = { mode = "none" },  -- read-only: staff from the external boss menu
            society    = nil,
            payroll    = false,
            trackHours = true
        }
    }
}

Tracking

Config.UpdateIntervalnumberdefault: 5

Minutes between working-time accumulator updates.

Config.TrackOnlyOnDutybooleandefault: true

When true, only accumulate hours for players flagged as on duty by the framework.

Config.ResetHoursOnTrackingStartbooleandefault: false

No longer used. Hours follow the player's live job, so starting to track a job never resets anything. The key is still read without error, so older configs keep loading.

Config.ResetHistoryOnHoursResetbooleandefault: true

No longer used. Reset Hours never removes hours that have not been paid yet. The key is still read without error, so older configs keep loading.

Config.SelfServicetabledefault: { Enabled = true }

Controls the non-boss self-service panel. Set Enabled = false to restrict the tablet to management.

Permissions

Config.UIKeystringdefault: 'F7'

Default keybind for /employeelist. Registered via RegisterKeyMapping so players can rebind it. The command and keybind only exist when Config.Tablet.access.mode is 'key' or 'key+item', and never while Config.LBTablet.Enabled is true.

Config.AllowedRankstabledefault: { default = { boss = true, chief = true, manager = true } }

Per-job grades treated as management. Keys are job names, values are tables of allowed grades. Each entry can be keyed by grade name (string) or grade level (number), or mix both. Values must be true to grant access. The 'default' entry is the fallback used for any job without an explicit entry.

Config.IsBossRankfunction

Resolves whether a (jobName, gradeName, gradeLevel) tuple has boss access. Signature: Config.IsBossRank(jobName, gradeName, gradeLevel). Either gradeName or gradeLevel may match an entry in Config.AllowedRanks. Falls back to Config.AllowedRanks['default'] when the job has no explicit entry. Used by both client and server permission checks.

Example: limit police to grade name boss, allow mechanic grade levels 3 and 4, mix names and levels for ambulance, and let every other job fall back to the default ranks.

Config.AllowedRanks = {
    ['default']   = { ['boss'] = true, ['chief'] = true, ['manager'] = true },
    ['police']    = { ['boss'] = true },                -- by grade name
    ['mechanic']  = { [3] = true, [4] = true },         -- by grade level
    ['ambulance'] = { ['chief'] = true, [4] = true }    -- mixed
}

Rank permissions

Boss ranks always have full access. From the Permissions tab in the menu, a boss grants each non-boss grade access to specific tabs. Grants are stored in the employee_rank_permissions table, created automatically on first start, and apply without a restart.

Nobody can escalate, bosses included. A player can only act on employees below their own grade and only offer or assign grades below their own. Only the top grade (the owner) can act on boss ranks or offer a boss rank. Nobody can change their own grade, fire themselves, approve their own invoice or pay themselves; the owner may still use Pay Now on their own hours.

Config.Permissions.Enabledbooleandefault: true

Master toggle for granular rank permissions. When false, non-boss ranks can never be granted access. Self-service keeps working.

Config.Permissions.Keysstring[]default: { 'employees', 'management', 'payroll', 'bossActions', 'society' }

Feature areas a boss can grant. Removing a key hides its column in the Permissions tab and blocks the server-side actions behind it for non-boss ranks.

Tablet

Behaviour toggles for the handheld tablet. Every option in this section is ignored while Config.LBTablet.Enabled is true, because the prop is not used in that mode.

Config.Tablet.Enabledbooleandefault: true

Master toggle for the tablet item flow. Forced off while Config.LBTablet.Enabled is true.

Config.Tablet.access.modestringdefault: 'item'

How the menu opens. One of 'key', 'item', 'key+item', 'zone'. In 'zone' mode only the locations in Config.Zones open the menu.

Config.Tablet.access.itemstringdefault: 'jobtablet'

Inventory item id for the tablet.

Config.Tablet.autoClosetabledefault: { onWeapon = true, onCombat = true, onRagdoll = true, onDeath = true }

Close the tablet automatically when the player draws a weapon, enters combat, ragdolls, or dies.

Config.Tablet.hints.enabledbooleandefault: true

Show hint prompts while the tablet is open.

Config.Tablet.camera.toggleEnabledbooleandefault: true

Allow toggling the locked tablet camera.

Config.Tablet.camera.toggleCommandstringdefault: 'tabletcam'

Command name registered for the camera toggle. Only registered when toggleEnabled is true.

Config.Tablet.camera.toggleKeystringdefault: 'F6'

Default key bound to the camera toggle command.

LB Tablet app

Run the management UI as an app inside your server's existing LB Tablet install, so bosses use the tablet they already carry instead of a second one.

Enabling this turns off every entry point this resource owns: the handheld prop, the tablet item, the Config.UIKey keybind, the tabletcam command, and Config.Zones. The app becomes the only way in. LB Tablet exposes no export for opening a specific app from Lua, so there is nothing for a key, an item, or a zone to route to. A tablet item left in your inventory config becomes inert rather than erroring. Remove it when you switch.

The UI keeps its 1024x768 layout and scales to fit whatever size the player has dragged their tablet to, so nothing re-flows.

Config.LBTablet.Enabledbooleandefault: false

Host the UI as an LB Tablet app. When true, this resource's own prop, item, keybind, camera command, and interaction zones are all disabled.

Config.LBTablet.Resourcestringdefault: 'lb-tablet'

Resource name of your LB Tablet install. Change this only if you renamed the resource folder.

Config.LBTablet.App.identifierstringdefault: 'nx_employeemanagement'

Unique app id registered with LB Tablet. Keep it stable: LB Tablet keys installed apps and player settings by this value.

Config.LBTablet.App.namestringdefault: 'Employees'

App name shown on the tablet home screen and in the app store.

Config.LBTablet.App.descriptionstringdefault: 'Roster, payroll and business management.'

Short app description shown in the LB Tablet app store.

Config.LBTablet.App.developerstringdefault: 'NX Creative'

Developer name shown in the app store listing.

Config.LBTablet.App.iconstringdefault: 'inventory-images/jobtablet.png'

App icon. Either a path inside this resource or a full https:// URL.

Config.LBTablet.App.defaultAppbooleandefault: true

When true, the app is pre-installed on every tablet. When false, players buy it from the app store at App.price.

Config.LBTablet.App.removablebooleandefault: false

When true, a player can uninstall the app from their tablet.

Config.LBTablet.App.pricenumberdefault: 0

App store price. Only used when defaultApp is false.

Config.LBTablet.App.sizenumberdefault: 24

Download size in MB shown in the app store. Cosmetic only.

Config.LBTablet.HideWhenNoAccessbooleandefault: true

Hide the app icon while the player's job grants no access, and reveal it again when it does. Re-evaluated on login, job change, and permission edits. Set false to always show the icon; a player with nothing to manage then sees the self-service view or an empty state.

Interaction zones

Fixed world locations that open the management menu as a flat window, without the tablet item or camera. The window renders at the same resolution as the tablet's screen, so both modes look identical. Access is permission-checked the same way as every other entry point. To make zones the only way to open the menu, set Config.Tablet.access.mode = 'zone'.

Config.Zones.Enabledbooleandefault: false

Master toggle for interaction zones. Zones do not register while Config.LBTablet.Enabled is true.

Config.Zones.Locationstable[]default: {}

Zone definitions. Fields per entry: label (string), coords (vec3), radius (number), interaction ('target' for an ox_target sphere zone, 'textui' for an ox_lib TextUI prompt with E), icon (Font Awesome class, used by 'target'), jobs (array of job names allowed to use the zone, omit to allow any job), bossOnly (boolean, hides the zone from non-boss employees).

Config.Zones = {
    Enabled = true,
    Locations = {
        {
            label = "Management",
            coords = vec3(449.8653, -973.8475, 30.6896),
            radius = 1.5,
            interaction = "target",
            icon = "fa-solid fa-briefcase",
            jobs = { "police" },
            bossOnly = false
        }
    }
}

Hire scope

Config.HireScope.defaultModestringdefault: 'nearby'

Initial scope of the hiring console. One of 'nearby', 'server'. 'nearby' lists only players within nearbyRadius metres. 'server' lists every online player and falls back to 'nearby' when allowServerWide is false.

Config.HireScope.nearbyRadiusnumberdefault: 10.0

Radius in GTA world units (approximately metres) used by the 'nearby' hire mode. A job offer sent from the tablet also needs the candidate within this distance.

Config.HireScope.allowServerWidebooleandefault: false

When true, exposes the 'Server-wide' tab in the hiring console so bosses can list every online player, and offers skip the distance check. When false, the tab is hidden and the server rewrites unscoped requests to the nearby radius.

Hire offers

Hiring sends a job offer. The candidate gets an Accept / Decline / Later prompt in game and is only hired once they accept, so nobody's job changes without their say. A candidate can bring back offers they put off with /joboffers. Bosses see their business's pending offers in Boss Actions and can cancel them there.

Config.HireOffers.Enabledbooleandefault: true

Allow job offers. When false, the hire button refuses with 'Job offers are disabled'.

Config.HireOffers.ExpiryHoursnumberdefault: 48

Hours before an unanswered offer expires.

Config.HireOffers.MaxPendingPerJobnumberdefault: 10

Most open offers a single business can have at once.

Config.HireOffers.MaxPendingPerCandidatenumberdefault: 3

Most open offers a single player can have waiting at once.

Config.HireOffers.PromptOnLoginbooleandefault: true

Show a candidate their pending offers again when they log in.

Time display

Config.DisplayFormatstringdefault: 'hours'

Time display format. One of 'hours', 'minutes', 'both'.

Config.TimeFormatstable

Per-language printf-style format strings. Keys are language codes ('en', 'fi'). Each entry has hours, minutes, and both fields. Falls back to the 'en' entry when the active language is missing keys.

Config.TimeFormattable

Legacy alias for the active language's TimeFormats entry. Kept for external callers. Prefer Config.TimeFormats for new code.

Payroll

Config.Payroll.Enabledbooleandefault: true

Master toggle for all payroll features. When false, invoicing, society, and auto-pay are disabled.

Config.Payroll.Modestringdefault: 'tablet'

Default payroll mode for every job. One of 'tablet' (hour-tracked payroll managed by this resource) or 'framework' (delegate to the framework's built-in paycheck thread, e.g. ESX paycheck.lua paying job_grades.salary).

Config.Payroll.PerJobModestabledefault: {}

Per-job override of Config.Payroll.Mode. Keys are job names, values are 'tablet' or 'framework'. Example: { police = 'framework', ambulance = 'framework' }.

Config.Payroll.MaxWagePerJobtabledefault: { default = 5000 }

Maximum wage allowed when editing from the tablet. The server clamps writes to this value. The 'default' entry covers any job not listed; per-job entries override it. In tablet mode the cap applies to hourly_rate, overtime_rate, and base_salary. In framework mode it caps the per-tick salary written to job_grades.salary. Use math.huge to disable the cap.

Config.Payroll.HoursPerPayCyclenumberdefault: 40

Hours per pay cycle. Used when converting a job's base salary into an hourly rate.

Config.Payroll.MoneyDecimalsnumber | stringdefault: 'auto'

Decimal places every amount is rounded to: wages, bonuses, invoices, society transfers and ledger rows. 'auto' uses 0 on ESX and 2 on QB/QBX. Accepts 0, 1 or 2.

Config.Payroll.OvertimePeriodstringdefault: 'week'

Period overtime is counted over. 'week' counts Monday to Sunday. 'payout' counts everything since the employee's last payout, the behaviour of older versions.

Config.Payroll.ResetHistoryOnPaybooleandefault: false

When true, delete an employee's fully paid past days after a payment. Off by default so the charts keep paid hours. A day inside the current overtime week is never deleted, because weekly overtime still counts it.

Config.Payroll.HistoryRetentionDaysnumberdefault: 365

Fully paid days older than this are pruned once a day. Unpaid days are never pruned, and neither is a day inside the current overtime week. 0 keeps everything.

Society funds

The Society Funds tab refreshes its balance and transactions every 10 seconds while it is open, so money paid in or out by other scripts shows up without reopening the tablet.

Config.Payroll.Society.UseSocietyFundsbooleandefault: true

When true, payroll and invoice payouts are debited from the society account. When false, wages are still paid but not debited, and bonuses and invoices are refused.

Config.Payroll.Society.Backendstringdefault: 'auto'

Society-account backend. One of 'auto', 'nx_realbanking', 'esx_addonaccount', 'database_esx', 'fd-banking', 'qb-management', 'qb-banking', 'renewed-banking', 'database'. 'auto' probes the running resources and picks the first match, nx_realbanking first.

Config.Payroll.Society.QBTableNamestringdefault: 'management_funds'

Table name used by the qb-management backend.

Limits

Hard caps the server applies to every money action, whether it comes from the tablet or from nx_computer's Employees app. Over a cap the action is refused with "Amount or count is over the limit".

Config.Payroll.Limits.MaxInvoicenumberdefault: 50000

Largest single invoice.

Config.Payroll.Limits.MaxBonusnumberdefault: 10000

Largest single bonus.

Config.Payroll.Limits.MaxPayoutnumberdefault: 100000

Largest computed payout for one employee in one payment. A payout above it is not made and the hours stay owed.

Config.Payroll.Limits.MaxSocietyTransfernumberdefault: 1000000

Largest single society deposit or withdrawal.

Config.Payroll.Limits.WithdrawPerDayNonOwnernumberdefault: 25000

Daily cap on society withdrawals for anyone below the top grade: a withdrawal is refused once the business's withdrawals for the day would pass it. The owner (top grade) is not capped. Rows posted by other scripts through AddCompanyTransaction do not count toward it.

Auto-pay

Only applies to tablet-mode jobs. Framework-mode jobs are paid by the framework's own paycheck thread.

Config.Payroll.AutoPay.Enabledbooleandefault: true

Run the auto-pay scheduler on the server.

Config.Payroll.AutoPay.Schedulestringdefault: 'weekly'

Pay cycle. One of 'daily', 'weekly', 'biweekly', 'monthly', 'interval'. Use 'interval' to fire on a fixed period instead of a calendar day.

Config.Payroll.AutoPay.PayDaynumberdefault: 1

Day of week or day of month depending on Schedule. Weekly uses 1 = Monday. Monthly accepts 1..31. Ignored when Schedule is 'interval'.

Config.Payroll.AutoPay.PayHournumberdefault: 10

Hour of day (24h) when auto-pay runs. Ignored when Schedule is 'interval'.

Config.Payroll.AutoPay.Intervaltabledefault: { Unit = 'minutes', Value = 30 }

Only used when Schedule is 'interval'. Fires every Value units regardless of PayHour or PayDay. Unit is one of 'minutes', 'hours', 'days'.

Config.Payroll.AutoPay.CatchUpHoursnumberdefault: 24

A payday missed while the server was down still runs when the server comes back, as long as it is within this many hours.

Invoices

Config.Payroll.Invoices.MaxPendingPerEmployeenumberdefault: 5

Maximum simultaneous pending invoices per employee.

Config.Payroll.Invoices.AutoApproveUndernumberdefault: 1000

Invoices with amount under this value skip review and auto-approve.

Config.Payroll.Invoices.RequireApprovalOvernumberdefault: 5000

Invoices over this amount always require boss review, even if other rules would auto-approve.

Default rates

Config.Payroll.DefaultRates.hourlynumberdefault: 25.00

Default hourly rate when no per-grade rule exists.

Config.Payroll.DefaultRates.commissionnumberdefault: 0.05

Default commission rate. 0.05 = 5 percent.

Config.Payroll.DefaultRates.overtime_multipliernumberdefault: 1.5

Overtime pay multiplier applied above the threshold.

Config.Payroll.DefaultRates.overtime_thresholdnumberdefault: 1500

Minutes worked before overtime kicks in. 1500 = 25 hours.

Reporting

Config.Payroll.Reporting.Enabledbooleandefault: true

Enables the Discord summary endpoint and boss-menu report buttons. The webhook URL itself is configured in shared/server-config.lua.

Config.Payroll.Reporting.MaxRangeDaysnumberdefault: 93

Maximum number of days that can be selected for a single report.

Config.Payroll.Reporting.IncludeTopEmployeesbooleandefault: true

Append a ranked top-employee list to the Discord summary.

Exports

Config.Exports.AllowedResourcestabledefault: { 'nx_computer' }

Resources allowed to manage staff and payroll through this resource. Keep nx_computer listed so its Employees app follows the same rules as the tablet. Does not affect AddCompanyTransaction, HasRankPermission or RunPayroll.

Config.Exports.Transactions.Enabledbooleandefault: true

Allow other resources to post rows to a company's ledger with AddCompanyTransaction. See Exports.

Config.Exports.Transactions.AllowedResourcestabledefault: {}

Empty means any server resource may post. List resource names to allow only those.

Config.Exports.Transactions.MaxAmountnumberdefault: 1000000

Largest amount a single posted row may carry.

Pay notifications

When a payment reaches a player (salary, AutoPay, Pay Now, bonus or invoice), they get a "Payroll payment received" message. With nx_phone running it lands in the phone's Wallet app, titled with the job's name. Without nx_phone, or when the player carries no phone, it shows as a normal notification. A player who muted Wallet on their phone gets no notification for it either.

Payments from the same job that land close together are summed into one message, for example "$450 (3 payments)", so a low hourly rate on a short AutoPay interval no longer fills the screen. The money itself reaches the bank right away; only the message waits.

Config.PayNotify.Enabledbooleandefault: true

Master toggle. false sends no pay messages at all.

Config.PayNotify.Phonestring | booleandefault: 'auto'

'auto' sends the message to nx_phone's Wallet when nx_phone is running. false always uses the normal notification and never the phone.

Config.PayNotify.FallbackToastbooleandefault: true

Show the normal notification when the phone could not take the message, for example because the player carries no phone. false sends nothing in that case.

Config.PayNotify.CoalesceSecondsnumberdefault: 20

Payments from the same job inside this many seconds become one message. 0 sends each payment on its own.

Config.PayNotify.MinAmountnumberdefault: 1

A total under this amount is held back and added to the next payment's message.

Config.PayNotify = {
    Enabled         = true,
    Phone           = 'auto', -- 'auto' | false (notification only)
    FallbackToast   = true,   -- notification when the player carries no phone
    CoalesceSeconds = 20,     -- payments inside this window become one message
    MinAmount       = 1       -- hold totals under this until the next payment
}

A config without Config.PayNotify keeps working with these defaults.

Rate limits

Per-player limits on actions, written as { count, seconds }. Over the limit the action is refused with "Too many requests, wait a moment". They apply to the tablet and to nx_computer's Employees app alike.

Config.RateLimits.moneytabledefault: { 6, 30 }

Pay Now, invoices, bonuses, and society deposits and withdrawals.

Config.RateLimits.stafftabledefault: { 10, 60 }

Job offers, grade changes, fires and hour resets.

Config.RateLimits.configtabledefault: { 6, 60 }

Saving rank permissions, payroll rules and grade salaries.

Config.RateLimits.runtabledefault: { 1, 60 }

Reserved for whole-business payroll runs. Nothing in this version is limited by it yet.

Config.RateLimits.ledgertabledefault: { 30, 60 }

At most 30 rows per 60 seconds posted through AddCompanyTransaction, counted per calling resource and job. Over the limit the export returns 'rate_limited'.

Debug menu

Config.DebugMenu.Enabledbooleandefault: false

Turns on the /trackdebug menu for admins. Leave it off on a live server. See Commands.

Webhooks (server-only)

These values live in shared/server-config.lua, which is loaded only on the server (declared in server_scripts) and listed in escrow_ignore so it stays editable.

Config.Webhooks.LogsWebhookUrlstringdefault: ''

Discord webhook URL for activity logs (hires, fires, grade changes, society deposits and withdrawals, payroll rule edits, salary payments, invoice approvals and rejections). Leave empty to disable activity logging.

Config.Webhooks.LogsUsernamestringdefault: 'NX Employee Management'

Username used in the activity-log Discord embeds.

Config.Webhooks.LogsAvatarUrlstringdefault: ''

Optional avatar image URL for the activity-log embeds. Leave empty for none.

Config.Webhooks.PayrollSummaryWebhookUrlstringdefault: ''

Discord webhook URL used by the manual 'Send Discord Summary' report generator in the payroll panel. Leave empty to disable summary reports.

Config.Webhooks.PayrollSummaryUsernamestringdefault: 'Company Payroll Reports'

Username used in the payroll summary embed.

Localization

Translations live in locales/<lang>.lua, one file per language. Shipped languages: en, fi, es, fr, it, de, sv. The files are listed in escrow_ignore, so they stay editable after install. To add a language, copy locales/en.lua, rename the table key, and translate the strings. New files load automatically.

Config.Localetable

String table keyed by language code. Each locales/<lang>.lua file fills one key. Non-English locales are merged with 'en' at load time so missing keys fall back to English.

Config.Translatefunction

Translate helper. Signature: Config.Translate(key, ...). Returns the formatted string for the active language, falling back to English, then to the key itself.

NX Docs