Exports & API Reference
Server exports provided by oxide-police for duty, officers, departments, treasury, MDT, custody, mugshot, evidence, radio, and framework compatibility.
Server exports provided by oxide-police.
Any server resource can call these exports after oxide-police has started.
Duty and Officers
IsPlayerOnDuty(source)
Returns true if the player is currently clocked in.
local onDuty = exports['oxide-police']:IsPlayerOnDuty(source)GetPlayerDepartment(source)
Returns the on-duty department ID, or nil if not on duty.
local deptId = exports['oxide-police']:GetPlayerDepartment(source)GetPlayerDepartmentName(source)
Returns the on-duty department name (e.g. 'lspd'), or nil if not on duty.
local deptName = exports['oxide-police']:GetPlayerDepartmentName(source)GetPlayerGrade(source)
Returns the on-duty grade rank, or nil if not on duty.
local grade = exports['oxide-police']:GetPlayerGrade(source)GetPlayerBadge(source)
Returns the officer's badge number, or nil if not an officer.
local badge = exports['oxide-police']:GetPlayerBadge(source)GetOnDutyOfficers()
Returns a table of all on-duty officers keyed by source.
local officers = exports['oxide-police']:GetOnDutyOfficers()GetOnDutyForDepartment(deptId)
Returns an array of the server IDs (number[]) of the on-duty officers in the given department. Unlike GetOnDutyOfficers, this returns plain source IDs, not officer records.
local sources = exports['oxide-police']:GetOnDutyForDepartment(1)
for _, src in ipairs(sources) do
-- src is a player server ID
endIsPlayerOfficer(source)
Returns true if the player is hired as an officer in any department (regardless of duty).
local isOfficer = exports['oxide-police']:IsPlayerOfficer(source)GetOfficerData(source)
Returns the officer's record (department_id, grade_rank, badge_number, hire timestamps), or nil.
local data = exports['oxide-police']:GetOfficerData(source)Field Interactions
IsPlayerCuffed(source)
Returns true if the player is currently cuffed.
local cuffed = exports['oxide-police']:IsPlayerCuffed(source)IsPlayerHandsUp(source)
Returns true if the player has hands up. Reads the oxide:handsup statebag.
local handsUp = exports['oxide-police']:IsPlayerHandsUp(source)SearchPlayer(officerSource, targetSource)
Triggers the search interaction.
exports['oxide-police']:SearchPlayer(officerSource, targetSource)EscortPlayer(officerSource, targetSource)
Starts escorting the target.
exports['oxide-police']:EscortPlayer(officerSource, targetSource)StopEscort(officerSource)
Stops the officer's current escort.
exports['oxide-police']:StopEscort(officerSource)TacklePlayer(officerSource, targetSource)
Tackles the target.
exports['oxide-police']:TacklePlayer(officerSource, targetSource)PutInVehicle(officerSource, targetSource, seat)
Forces the target into the nearest vehicle in the given seat.
exports['oxide-police']:PutInVehicle(officerSource, targetSource, 2)TakeOutOfVehicle(officerSource, targetSource)
Forces the target out of their current vehicle.
exports['oxide-police']:TakeOutOfVehicle(officerSource, targetSource)Departments
GetDepartments()
Returns all departments as a list.
local depts = exports['oxide-police']:GetDepartments()GetDepartment(deptId)
Returns a single department by ID.
local dept = exports['oxide-police']:GetDepartment(1)GetDepartmentByName(name)
Returns a single department by name (e.g. 'lspd').
local dept = exports['oxide-police']:GetDepartmentByName('lspd')Treasury
GetDepartmentBalance(deptId)
Returns the current treasury balance for a department.
local balance = exports['oxide-police']:GetDepartmentBalance(1)FundDepartment(deptId, amount, fundedBy)
Adds money to a department's treasury and logs a transaction.
exports['oxide-police']:FundDepartment(1, 50000, 'grant')fundedBy defaults to 'export' if omitted.
ChargeDepartment(deptId, amount, description)
Debits a department's treasury and logs a transaction. Returns success, newBalance.
local ok, balance = exports['oxide-police']:ChargeDepartment(1, 1000, 'Equipment purchase')MDT and Charges
GetActiveWarrantsForChar(charId)
Returns active warrants for the given character ID.
local warrants = exports['oxide-police']:GetActiveWarrantsForChar('CHAR_ABC123')CheckPlateForBolo(plate)
Returns the matching active BOLO record for a plate, or nil.
local bolo = exports['oxide-police']:CheckPlateForBolo('OXIDE001')GetCriminalRecords(charId)
Returns the criminal record list for a character.
local records = exports['oxide-police']:GetCriminalRecords('CHAR_ABC123')JailPlayer(source, time, opts)
Applies a jail sentence directly. time is in RP-months, converted to real seconds with Config.Custody.SecondsPerMonth (default 60). Returns true on success, false on failure.
opts must provide department context — either opts.departmentId or opts.officerSource (an on-duty officer whose department is used). Without one of these the call fails and logs a warning. Other accepted keys: stationId, mugshotId, criminalRecordId, reason.
exports['oxide-police']:JailPlayer(source, 30, { departmentId = 1, reason = 'admin' })BOLOs and Search
The following exports require an on-duty source for permission gating; pass the calling officer's source as the first argument.
CreateBolo(source, boloType, data)
Creates an active BOLO. boloType is 'person' or 'vehicle'. data.title is required. 'person' BOLOs require data.charId; 'vehicle' BOLOs require data.plate. Returns true, bolo (the created BOLO record) on success or false, errorMessage on failure.
-- Vehicle BOLO
local ok, bolo = exports['oxide-police']:CreateBolo(source, 'vehicle', {
title = 'Stolen Sentinel XS',
plate = 'OXIDE001',
description = 'Wanted in connection with armed robbery',
})
-- Person BOLO
local ok, bolo = exports['oxide-police']:CreateBolo(source, 'person', {
title = 'Wanted suspect',
charId = 'CHAR_ABC123',
description = 'Last seen near Vespucci Beach',
})GetActiveBolos(source)
Returns the current list of active BOLOs visible to the calling officer.
local bolos = exports['oxide-police']:GetActiveBolos(source)ClearBolo(source, boloId)
Clears an active BOLO by ID.
exports['oxide-police']:ClearBolo(source, boloId)SearchPerson(source, query)
Runs the MDT person search via olink.character.Search and returns matching profiles. Query length is bound by Config.MDT.search.minQueryLength (default 2); results are capped at Config.MDT.search.maxResults (default 20). Characters listed in Config.MDT.hidden.charIds are filtered out unless the caller's grade rank meets bypassMinGrade.
local results = exports['oxide-police']:SearchPerson(source, 'Doe')GetPersonProfile(source, charId)
Returns the full MDT person profile (warrants, charges, vehicles, licenses, mugshots) for a character.
local profile = exports['oxide-police']:GetPersonProfile(source, 'CHAR_ABC123')SearchVehicle(source, plate)
Returns a list of vehicles whose plate matches the query (up to 20 results), via olink.vehicles.SearchByPlate. Each result includes the plate, model, vehicle type, state, owner fields, and a has_bolo flag. Owner fields are stripped when the owner is in Config.MDT.hidden.charIds and the caller is below bypassMinGrade.
local vehicles = exports['oxide-police']:SearchVehicle(source, 'OXIDE')Custody and Booking
OpenBookingFlow(officerSource, targetSource, stationId)
Opens the booking flow at the given station.
exports['oxide-police']:OpenBookingFlow(officerSource, targetSource, 1)ReleaseFromCustody(charId, opts)
Releases an inmate from custody. opts defaults to { reason = 'admin' }.
exports['oxide-police']:ReleaseFromCustody('CHAR_ABC123', { reason = 'time_served' })GetActiveCustody(charId)
Returns the active custody record for the character, or nil.
local custody = exports['oxide-police']:GetActiveCustody('CHAR_ABC123')GetActiveCustodyForSource(source)
Returns the active custody record for the player, or nil.
local custody = exports['oxide-police']:GetActiveCustodyForSource(source)Mugshot
CaptureMugshot(officerSource, targetSource, opts)
Runs the full mugshot capture and upload pipeline.
local mugshot, err = exports['oxide-police']:CaptureMugshot(officerSource, targetSource, {
stationId = 1,
description = 'Booking photo',
dob = '1990-01-01',
})opts keys: stationId, description, dob, criminal_record_id, case_id, bypassRange, bypassGrade, teleportSuspect, internal.
GetMugshotsForChar(charId)
Returns all mugshots for a character.
local mugshots = exports['oxide-police']:GetMugshotsForChar('CHAR_ABC123')GetMugshot(id)
Returns a single mugshot by ID.
local mugshot = exports['oxide-police']:GetMugshot(42)Bodycam Recordings
These exports power the bodycam recording pipeline (the watcher-side clip capture documented in Features → Bodycam → Recording). External integrations rarely need them; the recorder NUI calls them directly.
GetVideoUploadUrls(source, payload)
Requests Fivemanage presigned upload URLs from oxide-police. Returns { videoUploadUrl, thumbUploadUrl? } (the thumb URL is only included when payload.thumbFilename is set), or nil on failure. Used by external recorders (e.g. oxide-baitcar) to upload clips into the MDT Video Storage tab when Config.Bodycam.recording.provider = 'fivemanage'. The export skips the "officer is watching" check that the in-resource bodycam recorder requires, so the caller must still pass the recording officer's source.
local urls = exports['oxide-police']:GetVideoUploadUrls(source, { thumbFilename = 'clip_001_thumb.jpg' })
-- urls.videoUploadUrl, urls.thumbUploadUrlUploadVideoToDiscord(source, payload)
Uploads base64-encoded video (and optional thumbnail) to the Discord webhook configured in Config.Bodycam.recording.discordWebhook and returns { videoUrl, thumbUrl? }. payload: { videoBase64, thumbBase64?, filename }.
local result = exports['oxide-police']:UploadVideoToDiscord(source, {
videoBase64 = '...',
filename = 'clip_001',
})SaveVideoRecording(source, payload)
Persists a finalised recording row to police_bodycam_recordings. payload: { source?, wearerCharId?, deptId?, trigger, startedAt, durationS, provider, videoUrl, thumbUrl?, fileBytes?, metadata? }. Set source = 'baitcar' to mark the clip as a baitcar dashcam capture (the wearer / officer-watching checks are skipped for non-bodycam sources). Returns row, nil on success or nil, errorKey on failure.
exports['oxide-police']:SaveVideoRecording(source, {
source = 'baitcar',
trigger = 'auto',
startedAt = os.date('%Y-%m-%d %H:%M:%S'),
durationS = 32,
provider = 'fivemanage',
videoUrl = uploadedUrl,
metadata = { plate = 'OXIDE001' },
})Evidence
CheckPlayerGSR(targetSource)
Returns true if the target currently has GSR. Bypasses the duty check (safe for external resources to call).
local hasGsr = exports['oxide-police']:CheckPlayerGSR(targetSource)CreateFingerprintDrop(source, coords, metadata)
Spawns a fingerprint evidence drop at the given coords with metadata attached.
exports['oxide-police']:CreateFingerprintDrop(source, vector3(123.4, 567.8, 90.1), {
suspect = 'CHAR_ABC123',
note = 'Found on door handle',
})GetVehicleFingerprints(netId)
Returns the list of fingerprints associated with a vehicle's net ID. Bypasses the duty check.
local prints = exports['oxide-police']:GetVehicleFingerprints(netId)Weapon Registry
The statewide firearm registry keyed by weapon serial — the durable owner-of-record that survives the item being destroyed, sold, or carried offline. It backs the MDT Weapons tab and is populated in-game by the civilian firearm registration desk and by evidence submission of weapon_* items. These exports are server-to-server (no source / duty gate) so other resources can register, seize, or look up weapons directly.
RegisterWeapon(serial, weaponName, ownerCharId, ownerName)
Registers (or re-registers) a weapon to an owner. Sets status to registered and clears the flagged marker. This is the same operation the PD registration desk performs. ownerCharId / ownerName may be omitted for an owner-less registration. No return value.
exports['oxide-police']:RegisterWeapon('SN-4F2A9C', 'weapon_pistol', 'CHAR_ABC123', 'John Doe')RecordWeaponSeizure(serial, weaponName, ownerCharId, ownerName)
Records a weapon as seized without overwriting a known registration's owner (an existing owner on record is preserved). A weapon recorded with no owner lands as unregistered — exactly what flags a ghost gun in the MDT. No return value.
exports['oxide-police']:RecordWeaponSeizure('SN-4F2A9C', 'weapon_pistol')SearchWeaponRegistry(query)
Returns up to 25 registry rows matching the serial or owner name. query must be at least 2 characters; shorter queries return an empty table.
local results = exports['oxide-police']:SearchWeaponRegistry('SN-4F2A')GetWeaponRegistration(serial)
Returns the full registry record for one serial (serial, weapon_name, owner_char_id, owner_name, status, flagged, notes, registered_at, updated_at) with the owner name refreshed from the character store, or nil if the serial is not in the registry.
local record = exports['oxide-police']:GetWeaponRegistration('SN-4F2A9C')SetWeaponStatus(serial, status, notes)
Sets a weapon's status. Valid values: registered, stolen, seized, revoked (any other value is rejected). stolen and revoked automatically set the flagged marker; the others clear it. notes is optional free text. Returns true on success, false if the serial doesn't exist or the status is invalid.
exports['oxide-police']:SetWeaponStatus('SN-4F2A9C', 'stolen', 'Reported stolen 06/06')Radio
SetRadioDead(source, isDead)
Marks the officer as down on the radio channel roster, forwarded to the bundled radio resource. Provided for death resources that do not already announce downs through the bridge; a no-op when the radio resource is not installed.
exports['oxide-police']:SetRadioDead(source, true)ESX Compatibility
SyncEsxJobs()
Manually re-runs the ESX job sync (mirrors Oxide departments into ESX jobs table). Available on ESX servers when Config.Compatibility['es_extended'].enabled = true. Note that the sync itself only runs when jobSync.enabled = true as well (off by default) — with it off, the export exists but does nothing.
exports['oxide-police']:SyncEsxJobs()The sync also runs automatically:
- on resource start (when
jobSync.syncOnStart = true) - on department change (when
jobSync.syncOnChange = true)
Use this export after manually editing the police_departments table outside of /police admin.
QBCore Back-Compat Shim
oxide-police also re-publishes a small set of legacy QBCore surfaces so existing third-party scripts keep working. These are not intended for new integrations — use the exports above.
Covered legacy surfaces:
exports['qb-policejob']:IsHandcuffed()(client-side proxy viaprovide('qb-policejob')) (the legacy export was client-side too)exports['oxide-police']:IsHandcuffed()(client-side wrapper around the local cuff state, for QBCore client scripts that read the cuff state fromoxide-policedirectly)police:GetCops,police:GetDutyPlayers,police:server:IsPoliceForcePresent,police:server:isPlayerDead,police:IsSilencedWeaponcallbacks, plus thepolice:server:UpdateCurrentCopseventpolice:SetCopCountevent broadcast for QB robbery scriptsevidence:server:CreateFingerDrop— legacy fingerprint drops from QB robbery scripts are routed into the Oxide evidence system
Full coverage list and limitations: see QBCore install guide.