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)
Flags the officer as dead so they can't use the radio. Used by oxide-death and similar resources to gate the radio on downed officers.
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 /padmin.
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.