Browse docs

Server Exports

Server-side exports provided by sky_jobs_base for wholesale orders, storage, dispatch, tablet app registration, and more.

Vehicle Registry

Read enabled features, check technical inspection and registration status by plate, or access a player's authorized certificate history with the Vehicle Registry exports and server events.

Player Interaction Whitelist

Shared player interactions are job-whitelisted. Register the jobs owned by your resource before using any player-interaction client or server export. Registration authorizes existing jobs only; it does not create framework jobs or Sky Jobs groups.

sky_policejob automatically registers its configured Police jobs with the police policy from Config.JobPlayerInteractions. sky_crimesystem does the same for its Crime jobs and the crime policy. Both refresh their registration after Job Configurator updates.

registerInteractionJobs(groupKey, jobs, permissions?)

  • groupKey (string) - unique key owned by the calling resource.
  • jobs (string[] | table[]) - job names or job definitions containing a name.
  • permissions (table?) - required for custom groups. It may be omitted when groupKey already has a policy in Config.JobPlayerInteractions.groups.
  • Returns boolean, string?.

Supported permission fields:

FieldPurpose
requireOnDutyRequires the current player to be on duty for every enabled action.
cuffsApply and remove handcuffs.
ziptiesApply, remove, and cut zipties.
legRestraintsApply and remove leg restraints.
escortStart and stop escorting a restrained player.
vehiclePlace escorted players in vehicles or remove restrained players from vehicles.
headBagApply and remove head bags.
searchOpen and transfer items through player search.
local registered, reason = exports["sky_jobs_base"]:registerInteractionJobs(
    "security",
    {
        "security",
        { name = "security_night" }
    },
    {
        requireOnDuty = true,
        cuffs = true,
        zipties = false,
        legRestraints = true,
        escort = true,
        vehicle = true,
        headBag = false,
        search = true
    }
)

if not registered then
    print(("Unable to register security interactions: %s"):format(reason))
end
Call this export from a server script during resource startup, after the final job list is known. Call it again when your resource changes that list at runtime. Group keys are resource-owned, registrations are removed when the owner resource stops, and another resource cannot overwrite the same key.

Registering jobs only grants the selected interaction capabilities to players whose current job matches the registration. It does not execute an action, bypass item requirements, or trust client input.

unregisterInteractionJobs(groupKey)

Removes a group registered by the calling resource.

exports["sky_jobs_base"]:unregisterInteractionJobs("security")

The export returns false, "not_owner" if another resource owns the group.

hasInteractionAccess(sourceId, permission)

Checks the same server-authoritative job and duty policy used by the interaction actions.

local allowed, reason = exports["sky_jobs_base"]:hasInteractionAccess(source, "search")

Possible failure reasons include not_authorized, not_on_duty, and invalid_permission.

Player Interaction Actions

Every action export below validates the acting player's current whitelist access. Target existence, distance, vehicle state, restraint state, items, and inventory operations are also validated where applicable.

These are trusted server-side integration points. Never forward an options table, target ID, network ID, seat, item name, amount, or metadata directly from an unvalidated client event.

Handcuffs and zipties

ExportReturnsPurpose
cuffPlayer(sourceId, targetId, cuffType, options?)boolean, string?Applies cuffs or zipties.
uncuffPlayer(sourceId, targetId, options?)boolean, string?Removes the target's current cuffs or zipties.
cutZipties(sourceId, targetId, options?)boolean, string?Cuts the target's zipties with the configured cutter.
isPlayerCuffed(targetId)booleanReads the shared cuff state.
getCuffConfig()tableReturns a read-only snapshot of Config.PoliceCuffs.

Trusted options for cuffPlayer are ignoreItemCheck, allowSelf, and forceNormal. uncuffPlayer and cutZipties accept allowSelf and forceNormal. Whitelist and distance validation cannot be disabled.

local success, reason = exports["sky_jobs_base"]:cuffPlayer(
    source,
    targetId,
    "cuffs"
)

Escape opportunities are not exposed as a forceable integration API. The server rolls and validates them from Config.PoliceCuffs.escape, including timing, expected keys, round progression, cooldowns, and final restraint removal.

Leg restraints

ExportReturnsPurpose
applyLegRestraints(sourceId, targetId)boolean, string?Applies leg restraints after all access and target checks.
removeLegRestraints(sourceId, targetId)boolean, string?Removes the target's leg restraints.
isPlayerLegRestrained(targetId)booleanReads the shared leg-restraint state.
getLegRestraintsTargetState(targetId)tableReturns the target's validated cuff and leg-restraint state.
getLegRestraintsConfig()tableReturns a read-only snapshot of Config.LegRestraints.

Escort and vehicles

ExportReturnsPurpose
escortToggle(sourceId, targetId)boolean, string?Starts or stops escorting a cuffed target.
escortPutInVehicle(sourceId, targetId, netId, seat)boolean, string?Places an escorted target into a nearby networked vehicle.
escortTakeOutVehicle(sourceId, targetId)boolean, string?Removes a nearby cuffed target from a vehicle.

escortToggle requires escort; the two vehicle exports require vehicle.

Head bag

ExportReturnsPurpose
useHeadBag(sourceId, targetId, action)boolean, string?Uses apply, remove, or toggle.
applyHeadBag(sourceId, targetId)boolean, string?Applies a head bag.
removeHeadBag(sourceId, targetId)boolean, string?Removes a head bag.
isPlayerHeadBagged(targetId)booleanReads the shared head-bag state.
getHeadBagTargetState(targetId)tableReturns success, hooded, and restrained.
getHeadBagConfig()tableReturns a read-only snapshot of Config.HeadBag.
ExportReturnsPurpose
getSearchTargetInventory(sourceId, targetId)tableReturns the validated target inventory and weapons.
searchTransfer(sourceId, targetId, transferType, name, amount, metadata?)tableTransfers a validated item from the target to the acting player.

searchTransfer currently accepts only transferType = "storage". The target must remain nearby and restrained for the initial read and every transfer.


Dispatch

The public Dispatch App integration is documented separately under Emergency Dispatch.


All gallery exports use the media provider selected in sky_jobs_base/config/webhooks.lua. See Media Providers for FiveManage, Qbox CDN, and custom adapter setup.

takeServerImage(sourceId, metadata?, timeoutMs?, uploadConfig?)

  • Purpose: Requests a client capture from a player and uploads it through the configured provider.
  • Arguments:
    • sourceId (number) — server ID of the player whose client performs the capture.
    • metadata (table?) — optional capture metadata forwarded to the client.
    • timeoutMs (number?) — optional capture timeout in milliseconds.
    • uploadConfig (table?) — optional trusted server-side media options, including provider.
  • Returns:
    • booleantrue on success, false on failure.
    • table | string — on success: { data = { url, id, image_id } }. On failure: error message.
local ok, result = exports["sky_jobs_base"]:takeServerImage(source, {
    type = "bodycam",
    timestamp = os.time()
})

if ok then
    print("Uploaded:", result.data.url)
end

getMediaUploadUrl()

  • Purpose: Requests a temporary browser-safe upload URL from the selected provider.
  • Returns: { success = true, data = { presignedUrl, expiresIn?, provider, timeoutMs } } or { success = false, error }.
local result = exports["sky_jobs_base"]:getMediaUploadUrl()

if result.success then
    print("Provider:", result.data.provider)
end

uploadMediaDataUrl(dataUrl, options?)

  • Purpose: Uploads a base64 data URL from a trusted server resource.
  • Arguments:
    • dataUrl (string) — complete base64 data URL.
    • options (table?) — optional provider override and provider-specific fields such as filename or path.
  • Returns: { success = true, data = { url, id, image_id, provider } } or { success = false, error }.
local result = exports["sky_jobs_base"]:uploadMediaDataUrl(dataUrl, {
    filename = "evidence.png",
    path = "cases/42"
})

deleteMediaFile(fileId, options?)

  • Purpose: Deletes a remote media object using its provider ID or storage key.
  • Returns: { success = true } or { success = false, error }.
local result = exports["sky_jobs_base"]:deleteMediaFile("provider-file-id")

registerMediaProvider(name, adapter)

  • Purpose: Registers or replaces a custom server-side media provider adapter.
  • Returns: { success = true } or { success = false, error }.

The adapter must implement requestUploadUrl, uploadDataUrl, and deleteFile. See the complete custom provider contract.

Creator

openCreatorByJob(source, jobKey)

  • Purpose: Opens the registered station creator for a player by job key after validating the creator permission.
  • Arguments:
    • source (number) - server ID of the player who should receive the creator UI.
    • jobKey (string) - registered job key.
  • Returns:
    • boolean - true on success.
    • string? - failure reason such as "creator_not_found" or "no_permission".
local ok, reason = exports["sky_jobs_base"]:openCreatorByJob(source, "mechanic")

openCreatorByJobWithoutPermission(source, jobKey)

  • Purpose: Opens the registered station creator for a player by job key without running the creator permission check. Use only from trusted server-side integrations that already authorized the player.
  • Arguments:
    • source (number) - server ID of the player who should receive the creator UI.
    • jobKey (string) - registered job key.
  • Returns:
    • boolean - true on success.
    • string? - failure reason such as "creator_not_found" or "invalid_source".
local ok, reason = exports["sky_jobs_base"]:openCreatorByJobWithoutPermission(source, "mechanic")

Wholesale Orders

External delivery scripts can receive new orders with their items, job, and ordering character's in-game name, then accept, reject, or complete them through server exports.

ExportReturnsPurpose
AcceptWholesaleOrder(orderId, civilianName, civilianPhone)true, order or false, errorCodeAccepts an order and saves the civilian's contact details.
RejectWholesaleOrder(orderId, reason?)true, order or false, errorCodeRejects a pending or accepted order.
CompleteWholesaleOrder(orderId)true, order or false, errorCodeMarks an accepted order as completed.
GetWholesaleOrder(orderId)order or nil, errorCodeReads one saved order.
GetWholesaleOrders(jobName?, status?, limit?, offset?)orders or nil, errorCodeLists saved orders with optional filters and pagination.

The local server events are sky_jobs_base:wholesale:orderCreated(order) and sky_jobs_base:wholesale:orderStatusChanged(order, previousStatus).

See Wholesale Orders for configuration, both events, the order payload, status rules, error codes, and a separate copyable example for each order and storage export. Payment and delivery are handled by the external script; completing an order changes its status only.

Storage

Use the actual target storage station ID. The stationId on a wholesale order identifies the ordering location and can differ from the delivery storage. These exports use the supplied job and grade context; the calling server script must authorize its own player-facing requests.

GetStorageItemCount(stationId, jobName, grade, name)

  • Purpose: Counts how many items with the given name are currently available in one station storage. Metadata does not split the result; matching stacks are added together.
  • Arguments:
    • stationId (string | number) - target station ID.
    • jobName (string) - job name used for storage restrictions.
    • grade (number?) - job grade context.
    • name (string) - item name to count.
  • Returns: number - total available amount, or 0 when the station or item is missing.
local wheels = exports["sky_jobs_base"]:GetStorageItemCount("station_1", "mechanic", 2, "wheels")

if wheels > 0 then
    print(("Storage has %d wheel kits"):format(wheels))
end

RemoveStorageItem(stationId, jobName, grade, name, amount)

  • Purpose: Removes a trusted amount of an item directly from station storage. This is intended for server-side job resources that already validated the player, station, and workflow.
  • Arguments:
    • stationId (string | number) - target station ID.
    • jobName (string) - job name used for storage restrictions.
    • grade (number?) - job grade context.
    • name (string) - item name to remove.
    • amount (number) - positive item count.
  • Returns: boolean - true when the requested amount was removed.
local removed = exports["sky_jobs_base"]:RemoveStorageItem("station_1", "mechanic", 2, "wheels", 1)

if not removed then
    print("Not enough wheel kits in storage")
end

AddStorageItem(stationId, jobName, grade, name, amount, metadata?)

  • Purpose: Adds an item or weapon stack directly to station storage. The export validates input, job and station availability, and storage capacity before writing.
  • Arguments:
    • stationId (string | number) - target station ID.
    • jobName (string) - job name used for storage restrictions.
    • grade (number?) - job grade context.
    • name (string) - item name.
    • amount (number) - positive whole number, at most 2147483647.
    • metadata (table?) - optional item metadata.
  • Returns: boolean - true when the database write succeeds; false on rejected input, insufficient capacity, or a failed write.
local ok = exports["sky_jobs_base"]:AddStorageItem(
    "station_1",
    "mechanic",
    2,
    "repair_crate",
    1,
    { quality = 100 }
)

AddStorageItems(stationId, jobName, grade, items)

  • Purpose: Adds multiple item or weapon stacks to station storage with one database statement. All entries and the total capacity are checked before writing; an invalid entry rejects the entire batch.
  • Arguments:
    • stationId (string | number) - target station ID.
    • jobName (string) - job name used for storage restrictions.
    • grade (number?) - job grade context.
    • items (table[]) - dense array of 1–200 entries with name, a positive whole-number amount or quantity (at most 2147483647), and optional metadata.
  • Returns: boolean - true when the complete batch is written; false when validation or the database write fails.
local ok = exports["sky_jobs_base"]:AddStorageItems("station_1", "mechanic", 2, {
    { name = "repair_crate", amount = 1, metadata = { quality = 100 } },
    { name = "cleaning_kit", quantity = 2 }
})

Weapons use the same exports. For unique serial numbers, provide one entry per weapon with amount = 1 and its own metadata.serial:

local ok = exports["sky_jobs_base"]:AddStorageItems("police_storage", "police", 4, {
    { name = "weapon_pistol", amount = 1, metadata = { serial = "DELIVERY-001" } },
    { name = "weapon_pistol", amount = 1, metadata = { serial = "DELIVERY-002" } }
})

Neither storage export completes a wholesale order. Call CompleteWholesaleOrder after successful delivery and keep a persistent delivery record in the external script to prevent duplicate deposits. Storage writes and order status updates are separate operations, without a shared transaction.


External Garage Integration

These exports allow third-party garage systems to register and unregister vehicles so the trunk inventory and prop system works for externally-spawned job vehicles.

registerExternalVehicle(plate, jobName, model, netId, ownerSource, trunkCapacity)

  • Purpose: Registers an externally-spawned vehicle in the sky_jobs_base vehicle tracking system. This enables trunk inventory, prop placement, and all trunk-related server callbacks for the vehicle.
  • Arguments:
    • plate (string) — the vehicle license plate. Required.
    • jobName (string) — the job this vehicle belongs to (e.g. "ambulance", "police"). Required.
    • model (string?) — vehicle model name (e.g. "ambulance"). Defaults to "unknown".
    • netId (number?) — network ID of the spawned vehicle entity.
    • ownerSource (number?) — server ID of the player who spawned the vehicle.
    • trunkCapacity (number?) — trunk weight capacity. Defaults to 50.
  • Returns: booleantrue on success, false if plate or jobName is missing.
-- After spawning the vehicle on the server:
local plate = GetVehicleNumberPlateText(vehicle)
local netId = NetworkGetNetworkIdFromEntity(vehicle)

exports["sky_jobs_base"]:registerExternalVehicle(
    plate,
    "ambulance",
    "ambulance",
    netId,
    source,
    80
)
External vehicles are not stored in the database. They only exist in memory and will be cleaned up on resource restart. Your garage system is responsible for its own persistence.

unregisterExternalVehicle(plate)

  • Purpose: Removes an externally-registered vehicle from the tracking system. Call this when the vehicle is despawned or parked by the external garage.
  • Arguments:
    • plate (string) — the vehicle license plate. Required.
  • Returns: booleantrue on success, false if plate is invalid.
exports["sky_jobs_base"]:unregisterExternalVehicle(plate)

Full Usage Example

-- When spawning the vehicle
RegisterNetEvent("mygarage:vehicleSpawned", function(plate, model, netId)
    exports["sky_jobs_base"]:registerExternalVehicle(
        plate, "ambulance", model, netId, source, 80
    )
end)

-- When despawning / parking the vehicle
RegisterNetEvent("mygarage:vehicleDespawned", function(plate)
    exports["sky_jobs_base"]:unregisterExternalVehicle(plate)
end)

Salary

These exports allow external resources to temporarily pause and resume salary payouts for a player.

pausePlayerSalary(playerId)

  • Purpose: Pauses salary payouts for the given player. While paused, the player will not receive any automatic salary payments.
  • Arguments:
    • playerId (number) — the server ID of the player. Required.
  • Returns: booleantrue on success, false if the player ID is invalid.
exports["sky_jobs_base"]:pausePlayerSalary(playerId)

resumePlayerSalary(playerId)

  • Purpose: Resumes salary payouts for the given player after they were paused.
  • Arguments:
    • playerId (number) — the server ID of the player. Required.
  • Returns: booleantrue on success, false if the player ID is invalid.
exports["sky_jobs_base"]:resumePlayerSalary(playerId)

isPlayerSalaryPaused(playerId)

  • Purpose: Checks whether salary payouts are currently paused for the given player.
  • Arguments:
    • playerId (number) — the server ID of the player.
  • Returns: booleantrue if paused, false otherwise.
local paused = exports["sky_jobs_base"]:isPlayerSalaryPaused(playerId)

getGradeSalary(jobName, grade)

  • Purpose: Reads the salary configuration of a job grade as configured in the boss menu.
  • Arguments:
    • jobName (string) — the job name. Required.
    • grade (number) — the job grade. Required.
  • Returns:
    • number — salary amount per interval (0 if none configured).
    • number — payout interval in minutes.
local amount, intervalMinutes = exports["sky_jobs_base"]:getGradeSalary("ambulance", 3)
print(("Grade 3 earns $%d every %d minutes"):format(amount, intervalMinutes))
The pause state is stored in memory and automatically cleared when the player disconnects. No database changes are needed.

Duty

These exports allow external resources (e.g. admin menus, multijob scripts) to read and change a player's duty state.

setPlayerDuty(playerId, onDuty)

  • Purpose: Sets a player's duty state externally. Runs the full duty pipeline: on/off-duty job switch, duty event for all job scripts, and salary work-time session handling — exactly as if the player toggled duty themselves.
  • Arguments:
    • playerId (number) — the server ID of the player. Required.
    • onDuty (boolean) — true to set on duty, false to set off duty.
  • Returns: booleantrue when the resulting duty state matches the request, false when the player is invalid or their job cannot go on duty.
-- Force a player off duty (e.g. from an admin menu)
local ok = exports["sky_jobs_base"]:setPlayerDuty(playerId, false)

if not ok then
    print("Player is not on a duty-capable job")
end

isPlayerOnDuty(playerId)

  • Purpose: Checks whether a player is currently on duty.
  • Arguments:
    • playerId (number) — the server ID of the player.
  • Returns: booleantrue if on duty.
local onDuty = exports["sky_jobs_base"]:isPlayerOnDuty(playerId)

getOnDutyPlayers(jobName)

  • Purpose: Returns all currently on-duty players of a job.
  • Arguments:
    • jobName (string) — the job name. Required.
  • Returns: number[] — array of server IDs.
local medics = exports["sky_jobs_base"]:getOnDutyPlayers("ambulance")
print(("%d medics on duty"):format(#medics))

Players & Members

These exports let external resources (admin menus, whitelist systems, other job scripts) manage job members without going through the boss menu.

getPlayerJobInfo(playerId)

  • Purpose: Returns the cached job state of an online player in one call.
  • Arguments:
    • playerId (number) — the server ID of the player. Required.
  • Returns: table | nil{ job, grade, gradeLabel, onDuty }, or nil if the player is not loaded.
local info = exports["sky_jobs_base"]:getPlayerJobInfo(playerId)

if info then
    print(("%s — grade %d (%s), on duty: %s"):format(info.job, info.grade, info.gradeLabel, tostring(info.onDuty)))
end

setJobMember(jobName, playerOrIdentifier, grade)

  • Purpose: Hires a player into a job or changes the grade of an existing member. Works for online players (server ID) and offline players (identifier). Updates the framework job, the player cache, and notifies open boss menus.
  • Arguments:
    • jobName (string) — the job name. Required.
    • playerOrIdentifier (number | string) — server ID or framework identifier. Required.
    • grade (number?) — target grade. Defaults to the job's lowest grade (hire).
  • Returns:
    • booleantrue on success.
    • string? — error message on failure (e.g. "Invalid grade").
-- Hire an online player at the default grade
local ok, err = exports["sky_jobs_base"]:setJobMember("ambulance", playerId)

-- Promote an offline player to grade 2
local ok, err = exports["sky_jobs_base"]:setJobMember("ambulance", "license:abc123", 2)

removeJobMember(jobName, playerOrIdentifier)

  • Purpose: Fires a member from a job. Sets the player to unemployed, ends their duty, and removes their member stats. Works for online and offline players.
  • Arguments:
    • jobName (string) — the job name. Required.
    • playerOrIdentifier (number | string) — server ID or framework identifier. Required.
  • Returns:
    • booleantrue on success.
    • string? — error message on failure.
local ok, err = exports["sky_jobs_base"]:removeJobMember("ambulance", "license:abc123")

getJobMembers(jobName)

  • Purpose: Returns all members of a job (online and offline) with their boss-menu stats.
  • Arguments:
    • jobName (string) — the job name. Required.
  • Returns: table[] — array of member entries.

Member Entry Structure

FieldTypeDescription
identifierstringFramework identifier
licensestringStored license identifier
namestringCharacter name
gradenumberJob grade
grade_labelstringGrade label
ondutybooleanCurrent duty state
last_online_timestampnumberUnix timestamp of last online
total_work_time_secondsnumberAccumulated on-duty time
actions_donenumberJob action counter
image_urlstring?Member image if set
local members = exports["sky_jobs_base"]:getJobMembers("ambulance")

for _, member in ipairs(members) do
    print(member.name, member.grade_label, member.onduty)
end

Society Money

Direct access to the job's shared account (the same account used by the boss menu finance tab).

getJobMoney(jobName)

  • Purpose: Returns the current shared account balance of a job.
  • Arguments:
    • jobName (string) — the job name. Required.
  • Returns: number — balance (0 on invalid job).
local balance = exports["sky_jobs_base"]:getJobMoney("ambulance")

addJobMoney(jobName, amount)

  • Purpose: Adds money to the job's shared account.
  • Arguments:
    • jobName (string) — the job name. Required.
    • amount (number) — positive amount. Required.
  • Returns: booleantrue on success.
exports["sky_jobs_base"]:addJobMoney("ambulance", 5000)

removeJobMoney(jobName, amount)

  • Purpose: Removes money from the job's shared account.
  • Arguments:
    • jobName (string) — the job name. Required.
    • amount (number) — positive amount. Required.
  • Returns: booleantrue on success, false when the account is invalid or the withdrawal fails.
if exports["sky_jobs_base"]:removeJobMoney("ambulance", 2500) then
    print("Invoice paid from society funds")
end

Permissions

hasJobPermission(playerId, permission, jobName)

  • Purpose: Checks whether a player's grade has a boss-menu permission. Useful for external resources that gate features behind management permissions.
  • Arguments:
    • playerId (number) — the server ID of the player. Required.
    • permission (number | string) — Permission enum value or key name (e.g. 3 or "MANAGE_MEMBERS"). Required.
    • jobName (string?) — when set, the check additionally requires the player to be on this job.
  • Returns: booleantrue if the grade has the permission (or the ALL permission).

Permission Keys

VIEW_LOGS, MANAGE_ROLES, MANAGE_MEMBERS, MANAGE_WAREHOUSE, MANAGE_MONEY, EDIT_OUTFITS, CREATE_OUTFITS, DELETE_OUTFITS, PURCHASE_SUPPLIES, PURCHASE_VEHICLES, GARAGE_VEHICLES, SELL_VEHICLES, TABLET_APPS, DOCUMENT_CLASSIFICATIONS, ALL

if exports["sky_jobs_base"]:hasJobPermission(playerId, "MANAGE_MONEY", "ambulance") then
    -- allow external payout action
end

Support

Need help? Our support team is always ready to assist

Join Discord