> For the complete documentation index, see [llms.txt](https://fantasy-scripts.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://fantasy-scripts.gitbook.io/docs/police-system/exports.md).

# Exports

## Exports & Events

Everything other scripts can use to work with fs-police: call the police from a robbery, check who is on duty, look up a warrant, block a phone while cuffed, or react when an officer signs in.

{% hint style="info" %}
fs-police answers only once it has started and its database is ready. Before that, exports that need the database return `nil` and `'err_not_ready'`. Listen for `fs-police:ready` if your script starts first.
{% endhint %}

***

### Server exports

#### CreateDispatch

Sends a call to every officer on duty, with an alert on their screen, a mark on their map and a line on the dispatch tablet.

```lua
local callId, reason = exports['fs-police']:CreateDispatch(data)
```

| Field               | Type            | Needed          | What it is                                                                                                         |
| ------------------- | --------------- | --------------- | ------------------------------------------------------------------------------------------------------------------ |
| `key`               | string          | `key` or `code` | an alert from `Config.DispatchCodes` in `config/dispatch.lua`. It brings its own code, title, map icon and colour. |
| `code`              | string          | `key` or `code` | your own code, like `'10-99'` (16 letters at most)                                                                 |
| `coords`            | vector3 / table | yes             | where it happened                                                                                                  |
| `title`             | string          | no              | what officers read (80 letters); overrides the alert's own                                                         |
| `kind`              | string          | no              | `'code'`, `'911'`, `'311'`, `'ems'` or `'manual'` (changes the colour of the alert)                                |
| `street`            | string          | no              | the street; left out, each officer's game fills it in                                                              |
| `area`              | string          | no              | the area, like `'Mission Row'`                                                                                     |
| `details`           | string          | no              | a line under the title (500 letters)                                                                               |
| `caller`            | string          | no              | who called it in (60 letters)                                                                                      |
| `radius`            | number          | no              | size of the circle on the map, 0 to 500 metres                                                                     |
| `sprite` / `colour` | number          | no              | the map icon and its colour                                                                                        |
| `departments`       | table           | no              | `{ 'lspd', 'bcso' }`; left out, every department                                                                   |

**Returns** the call number, or `nil` and a reason: `'err_bad_input'` (no `coords`, or a `key` that is not in the list), `'err_not_ready'`, `'err_unknown'`.

```lua
-- A bank robbery script, server side
exports['fs-police']:CreateDispatch({
    key = 'robbery',
    coords = GetEntityCoords(GetPlayerPed(source)),
    details = 'Two masked men, one armed',
    caller = 'Fleeca Bank',
})

-- A code of your own
exports['fs-police']:CreateDispatch({
    code = '10-99', title = 'Officer Down', kind = '911',
    coords = vector3(441.3, -979.9, 30.69),
})
```

Alerts ready to use with `key`: `shoplifting`, `silent_alarm`, `robbery`, `atm_robbery`, `breakin`, `vehicle_breakin`, `carjacking`, `gta`, `gunshots`, `fight`, `explosion`, `drug_sale`, `trespassing`, `officer_down`, `injured`, `call_311`, `call_911`, `manual`. Add your own in `config/dispatch.lua`.

{% hint style="warning" %}
Call it from the **server**. In a client script, send your own server event and call the export there, so a player cannot fake calls.
{% endhint %}

***

#### GetDispatchCalls

The calls on the dispatch tablet right now.

```lua
local calls = exports['fs-police']:GetDispatchCalls()
```

**Returns** a list of calls, newest last:

```lua
{
    id = 12, key = 'robbery', code = '10-90A', kind = 'code', title = 'Robbery In Progress',
    street = 'Vespucci Blvd', area = 'Pillbox Hill', coords = { x = 147.0, y = -1035.0, z = 29.3 },
    details = 'Two masked men', caller = 'Fleeca Bank', departments = { 'lspd' },
    createdAt = 1791240000,          -- os.time()
    status = 'active',               -- 'pending' (nobody on it) or 'active'
    units = { { identifier = '...', callsign = '214', name = 'Hugo Marsh' } },
}
```

***

#### IsPoliceOfficer

```lua
local isCop = exports['fs-police']:IsPoliceOfficer(source)
```

**Returns** `true` when the player has a police job of any department, on duty or not.

#### IsOfficerOnDuty

```lua
local onDuty = exports['fs-police']:IsOfficerOnDuty(source)
```

**Returns** `true` when the player is police and signed in.

#### GetOfficerData

```lua
local officer = exports['fs-police']:GetOfficerData(source)
```

**Returns** `nil` for somebody who is not police, or:

```lua
{
    source = 3,
    identifier = 'char1:4a87c8...',      -- the framework's id for the character
    citizenId = 12,                      -- the MDT's citizen number
    name = 'Tessa Holt',
    department = 'lspd',
    departmentLabel = 'LSPD',
    job = 'police',
    grade = 3,
    rank = 'Lieutenant',
    onDuty = true,
    callsign = '102',
    unit = nil,
    status = 'available',                -- or 'oncall' while attached to a call
    certifications = { 'swat', 'fto' },
}
```

#### GetActiveOfficers

```lua
local officers = exports['fs-police']:GetActiveOfficers()          -- every department
local lspd = exports['fs-police']:GetActiveOfficers('lspd')        -- one department
```

**Returns** a list of officers **on duty**, each like `GetOfficerData`.

#### GetOnDutyCount

```lua
local count = exports['fs-police']:GetOnDutyCount()                -- every department
local deputies = exports['fs-police']:GetOnDutyCount('bcso')
```

**Returns** a number. Use it for "4 police needed" checks:

```lua
if exports['fs-police']:GetOnDutyCount() < 4 then
    return TriggerClientEvent('ox_lib:notify', source, { description = 'Not enough police in the city', type = 'error' })
end
```

{% hint style="info" %}
Everything the officer exports return is a copy. Changing it changes nothing in fs-police.
{% endhint %}

***

#### HasWarrant

```lua
local wanted = exports['fs-police']:HasWarrant(identifier)
```

| Parameter    | Type   | What it is                                                              |
| ------------ | ------ | ----------------------------------------------------------------------- |
| `identifier` | string | the framework's id for the character (ESX identifier, QBCore citizenid) |

**Returns** `true` when the character has an active warrant that has not expired. A gun store or a city hall can use it to refuse service.

#### CreateBOLO

Puts a be-on-the-lookout notice in the MDT for every officer.

```lua
local id, reason = exports['fs-police']:CreateBOLO(data)
```

| Field         | Type   | Needed        | What it is                           |
| ------------- | ------ | ------------- | ------------------------------------ |
| `kind`        | string | yes           | `'vehicle'` or `'person'`            |
| `title`       | string | yes           | 3 to 120 letters                     |
| `plate`       | string | for a vehicle | the plate                            |
| `identifier`  | string | for a person  | the framework's id for the character |
| `description` | string | no            | up to 2000 letters                   |

**Returns** the BOLO's number, or `nil` and a reason (`'err_bad_input'`, `'err_not_found'`, `'err_not_ready'`).

```lua
-- A plate reader camera that saw a stolen car
exports['fs-police']:CreateBOLO({
    kind = 'vehicle', plate = 'AB12CD34',
    title = 'Stolen from Simeon', description = 'Black Sultan, last seen on Strawberry Ave',
})
```

***

#### IsCuffed / IsEscorted

```lua
local cuffed = exports['fs-police']:IsCuffed(source)
local escorted = exports['fs-police']:IsEscorted(source)
```

**Returns** `true` while the player is cuffed, or being walked by an officer.

#### IsRadioFrequencyAllowed

```lua
local allowed = exports['fs-police']:IsRadioFrequencyAllowed(source, 1.5)
```

**Returns** whether the player may use that frequency. Police frequencies are only for officers on duty of the right department.

#### ArmoryAccess

```lua
local mayEnter = exports['fs-police']:ArmoryAccess(source)
local mayTake = exports['fs-police']:ArmoryAccess(source, 'WEAPON_CARBINERIFLE_MK2')
```

**Returns** whether the officer may use the armory, or take that item (rank, certification, department and duty are all checked).

***

### Client exports

| Export                                   | Returns / does                                  |
| ---------------------------------------- | ----------------------------------------------- |
| `exports['fs-police']:OpenMDT()`         | opens the MDT (police only)                     |
| `exports['fs-police']:OpenDispatch()`    | opens the dispatch tablet                       |
| `exports['fs-police']:OpenRadio()`       | brings the radio up                             |
| `exports['fs-police']:ToggleCamera()`    | takes the PD camera out or puts it away         |
| `exports['fs-police']:IsPoliceOfficer()` | `true` when this player is police               |
| `exports['fs-police']:IsOfficerOnDuty()` | `true` when this player is police and signed in |
| `exports['fs-police']:IsCuffed()`        | `true` while this player is cuffed              |
| `exports['fs-police']:IsEscorted()`      | `true` while this player is being walked        |
| `exports['fs-police']:IsRadarOpen()`     | `true` while the radar is on screen             |

```lua
-- A phone script: no phone while cuffed
if exports['fs-police']:IsCuffed() then return end
```

***

### Server events

Listen with `AddEventHandler`. fs-police sends these; do not trigger them yourself.

#### fs-police:ready

fs-police has started and its database is ready.

```lua
AddEventHandler('fs-police:ready', function()
    -- safe to use every export now
end)
```

#### fs-police:dutyChanged

An officer signed in or out (or stopped being police, or left).

| Argument | Type    |                  |
| -------- | ------- | ---------------- |
| `src`    | number  | the player       |
| `onDuty` | boolean | signed in or not |

```lua
AddEventHandler('fs-police:dutyChanged', function(src, onDuty)
    print(('%s is now %s'):format(GetPlayerName(src), onDuty and 'on duty' or 'off duty'))
end)
```

#### fs-police:officerChanged

Something about an officer changed: department, rank, duty, callsign or status.

| Argument  | Type        |                                                                                                                                    |
| --------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `src`     | number      | the player                                                                                                                         |
| `officer` | table / nil | the officer now; `nil` when they are no longer police. Read it, do not change it; `GetOfficerData` gives the same in a clean form. |
| `fields`  | table       | what changed, like `{ onDuty = true }`, or `{ all = true }` when they became or stopped being police                               |

#### fs-police:cuffChanged

| Argument | Type    |                    |
| -------- | ------- | ------------------ |
| `src`    | number  | the player         |
| `cuffed` | boolean | cuffed or uncuffed |

```lua
AddEventHandler('fs-police:cuffChanged', function(src, cuffed)
    -- e.g. close their phone, stop their emote
end)
```

#### fs-police:citizenLoaded

A character has loaded and is in the MDT's citizen list.

| Argument    | Type   |                          |
| ----------- | ------ | ------------------------ |
| `src`       | number | the player               |
| `citizenId` | number | their MDT citizen number |

***

### Client events

#### fs-police:client:officerChanged

This player's own police record changed. Listen with `AddEventHandler`.

```lua
AddEventHandler('fs-police:client:officerChanged', function(officer)
    -- officer = { department, grade, rank, onDuty, callsign, unit, status }, or nil when not police
end)
```

#### fs-police:client:dutyChanged

This player signed in or out. Listen with `RegisterNetEvent`.

```lua
RegisterNetEvent('fs-police:client:dutyChanged', function(onDuty)
    -- show or hide your police-only things
end)
```

#### fs-police:client:screenClosed

One of fs-police's screens was closed (`'mdt'`, `'dispatch'`, `'radio'`, `'radarSettings'`).

```lua
AddEventHandler('fs-police:client:screenClosed', function(screen) end)
```

***

### State bags

Read them anywhere, server or client. Only fs-police writes them.

| State          | On                                        | Value                                                                                                  |
| -------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `fsPolice`     | `Player(src).state` / `LocalPlayer.state` | `{ department, grade, rank, onDuty, callsign, unit, status }`, or `nil` for somebody who is not police |
| `fsCuffed`     | `Player(src).state`                       | `true` while cuffed                                                                                    |
| `fsEscortedBy` | `Player(src).state`                       | the server id of the officer walking them                                                              |
| `fsEscorting`  | `Player(src).state`                       | the server id of the person this officer is walking                                                    |
| `fsFleet`      | `Entity(vehicle).state`                   | the fleet number of a police car out of the garage                                                     |
| `fsBarrier`    | `Entity(object).state`                    | `{ item, spikes }` on a barrier or spike strip an officer put down                                     |

```lua
-- Client: a seatbelt script that leaves cuffed players alone
if LocalPlayer.state.fsCuffed then return end

-- Server: is this car one of the department's own?
if Entity(vehicle).state.fsFleet then ... end
```

{% hint style="warning" %}
A player's game can write its own state bags. Use them to **show** things. For anything that matters (money, items, permissions) ask the server exports above, which read fs-police's own records.
{% endhint %}

***

### QBCore compatibility

Scripts written for qb-policejob that send

```lua
TriggerServerEvent('police:server:policeAlert', 'Store robbery')
```

keep working: when qb-policejob is not on the server, fs-police turns it into a 911 call at the player's position (once every 30 seconds per player).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://fantasy-scripts.gitbook.io/docs/police-system/exports.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
