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
end

IsPlayerOfficer(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' })

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.thumbUploadUrl

UploadVideoToDiscord(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 via provide('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 from oxide-police directly)
  • police:GetCops, police:GetDutyPlayers, police:server:IsPoliceForcePresent, police:server:isPlayerDead, police:IsSilencedWeapon callbacks, plus the police:server:UpdateCurrentCops event
  • police:SetCopCount event broadcast for QB robbery scripts
  • evidence: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.

Next Steps

On this page

Duty and OfficersIsPlayerOnDuty(source)GetPlayerDepartment(source)GetPlayerDepartmentName(source)GetPlayerGrade(source)GetPlayerBadge(source)GetOnDutyOfficers()GetOnDutyForDepartment(deptId)IsPlayerOfficer(source)GetOfficerData(source)Field InteractionsIsPlayerCuffed(source)IsPlayerHandsUp(source)SearchPlayer(officerSource, targetSource)EscortPlayer(officerSource, targetSource)StopEscort(officerSource)TacklePlayer(officerSource, targetSource)PutInVehicle(officerSource, targetSource, seat)TakeOutOfVehicle(officerSource, targetSource)DepartmentsGetDepartments()GetDepartment(deptId)GetDepartmentByName(name)TreasuryGetDepartmentBalance(deptId)FundDepartment(deptId, amount, fundedBy)ChargeDepartment(deptId, amount, description)MDT and ChargesGetActiveWarrantsForChar(charId)CheckPlateForBolo(plate)GetCriminalRecords(charId)JailPlayer(source, time, opts)BOLOs and SearchCreateBolo(source, boloType, data)GetActiveBolos(source)ClearBolo(source, boloId)SearchPerson(source, query)GetPersonProfile(source, charId)SearchVehicle(source, plate)Custody and BookingOpenBookingFlow(officerSource, targetSource, stationId)ReleaseFromCustody(charId, opts)GetActiveCustody(charId)GetActiveCustodyForSource(source)MugshotCaptureMugshot(officerSource, targetSource, opts)GetMugshotsForChar(charId)GetMugshot(id)Bodycam RecordingsGetVideoUploadUrls(source, payload)UploadVideoToDiscord(source, payload)SaveVideoRecording(source, payload)EvidenceCheckPlayerGSR(targetSource)CreateFingerprintDrop(source, coords, metadata)GetVehicleFingerprints(netId)Weapon RegistryRegisterWeapon(serial, weaponName, ownerCharId, ownerName)RecordWeaponSeizure(serial, weaponName, ownerCharId, ownerName)SearchWeaponRegistry(query)GetWeaponRegistration(serial)SetWeaponStatus(serial, status, notes)RadioSetRadioDead(source, isDead)ESX CompatibilitySyncEsxJobs()QBCore Back-Compat ShimNext Steps