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

# Dispatch (cc\_dispatch)

**CrankChaos Dispatch**: emergency calls, automatic alerts, alert cards and a dispatch desk per job, plus a citizen tablet for emergency calls.

Supported frameworks: **ESX Legacy**, **QBCore** and **QBox** (see framework bridge).

{% hint style="success" %}
No database needed. Alerts live in memory and expire.
{% endhint %}

{% hint style="info" %}
The script is protected by Cfx asset escrow. `config/`, `locales/` and `shared/cc_framework.lua` stay editable.
{% endhint %}

## Features

* **Any job via the config**: police, EMS and mechanics by default; a new job (taxi, fire department, sheriff) needs only one config entry
* **Emergency calls** via chat commands (`/911`, `/112`, `/mechaniker`, `/abschlepp` by default), the citizen tablet or the `Call` export (e.g. for a phone)
* **Alert cards** on screen: accept (`Y`) or dismiss (`J`) the top card; alert list on `F9` / `/dispatch`
* **Panic button** (hold `Page Up`) for police and EMS on duty
* **Automatic alerts**: shots fired, traffic accidents and person down (from cc\_ambulance/00%20Overview\.md)); a downed officer is additionally sent to the police with priority 1
* Similar alerts nearby are **merged** into one; shooting locations are fuzzed
* **Dispatch desk per job**: alert list, location sketch, notes, close alerts, assign units, broadcast; multiple dispatchers at once, all actions validated server-side
* **Citizen tablet** (`F6` for non-responders, `/notruf` for everyone): tiles per callable job, send a report (reason, text, share or blur location, callback), **text-based live calls**, "my reports"
* **Unit tracking** for live maps (used by the cc\_police/00%20Overview\.md) MDT and the cc\_ambulance tablet), with configurable visibility between jobs
* Blips, routes and sounds per priority

## Pages

* Installation
* Configuration
* API (exports, events, commands)
* FAQ & troubleshooting

Need help? See Support.

## Installation

General steps are described in Getting started: Installation.

### Requirements

**Required**

* ESX Legacy, QBCore or QBox
* **No database** and no oxmysql needed

**Optional**

| Resource                                                        | Used for                               | Without it                                                                        |
| --------------------------------------------------------------- | -------------------------------------- | --------------------------------------------------------------------------------- |
| cc\_police/00%20Overview\.md), cc\_ambulance/00%20Overview\.md) | set the duty state bag `ccDuty`        | jobs with `dutyRequired = true` receive no alerts (testable with `/dispatchtest`) |
| cc\_ambulance/00%20Overview\.md)                                | sets `ccDown` for "person down" alerts | no person-down alerts                                                             |
| cc\_jobs/00%20Overview\.md)                                     | place desk locations in-game           | `Config.Jobs[job].desk.coords`                                                    |
| cc\_lib/00%20Overview\.md)                                      | shared notifications / hints           | built-in display                                                                  |

{% hint style="info" %}
cc\_dispatch **never** sets `ccDuty` itself; the job resources do. Jobs with `dutyRequired = false` (e.g. mechanic) work with just the framework job.
{% endhint %}

See also Dependencies.

### Steps

1. Copy the folder to `resources/[crankchaos]/cc_dispatch`.
2. Add to `server.cfg` **after** the framework (and after cc\_jobs, if used). Start it before cc\_police / cc\_ambulance so their integrations are available:

```cfg
ensure es_extended          # or qb-core / qbx_core
ensure cc_lib               # optional
ensure cc_jobs              # optional
ensure cc_dispatch
ensure cc_ambulance         # optional
ensure cc_police            # optional
```

### Database

None. There is no `sql/` folder; alerts, conversations and "my reports" are kept in memory and are cleared on restart.

### ACE permissions

```cfg
add_ace group.admin command.dispatchtest allow
```

### Framework notes

Works the same on ESX Legacy, QBCore and QBox. The server checks the framework job every `Config.JobCheck` seconds; on a job change the unit is unassigned, signed off and its conversation ends. See Framework bridge.

## Configuration

| File                                 | Contents                                                                          |
| ------------------------------------ | --------------------------------------------------------------------------------- |
| `config/config.lua`                  | jobs, keys, cards, alerts, calls, desk, tablet, panic, tracking, automatic alerts |
| `config/ui.lua`                      | look and language                                                                 |
| `locales/de.json`, `locales/en.json` | texts                                                                             |

All times are in seconds, distances in meters, speeds in km/h (unless noted).

### Jobs (`Config.Jobs`)

A new job needs **only one entry**.

```lua
Config.Jobs = {
    police = {
        label = 'dispatch.job.police',
        color = '#3B82F6',
        icon = 'shield',
        dutyRequired = true,
        callable = true,
        callCommand = '911',
        aliases = {},
        callCode = '911',
        callTitle = 'dispatch.title.call_police',
        blip = { sprite = 280, colour = 3, scale = 1.0 },
        live = true,
        categories = {
            { id = 'theft', label = 'dispatch.cat.theft' },
            { id = 'robbery', label = 'dispatch.cat.robbery', priority = 1 },
            -- ...
        },
        desk = { coords = vector3(458.9, -985.9, 30.69), minGrade = 0, remoteGrade = 2 },
    },
    -- ambulance ('/112'), mechanic ('/mechaniker', alias 'abschlepp', dutyRequired = false)
}
```

| Field                           | Meaning                                                                                                          |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `label`                         | locale key or plain text                                                                                         |
| `color`                         | accent color (hex) for cards, list, desk, tablet                                                                 |
| `icon`                          | icon name (`shield`, `heart`, `wrench`, `truck`, `siren`, ...)                                                   |
| `dutyRequired`                  | `true`: only on duty (`ccDuty.job == job`); `false`: anyone with the framework job                               |
| `callable`                      | citizens can call this job                                                                                       |
| `callCommand`, `aliases`        | chat command and extra names                                                                                     |
| `callCode`, `callTitle`, `blip` | code, title and blip of the call alert                                                                           |
| `live`                          | allow text-based live calls                                                                                      |
| `categories`                    | reasons in the tablet: `{ id, label, priority? }`                                                                |
| `desk`                          | `coords`, `minGrade` (at the desk), `remoteGrade` (from anywhere, `false` = desk only); `desk = false` = no desk |

{% hint style="warning" %}
Default desk coordinates are approximate. If a desk is inside a wall, enter your own coordinates or set it with cc\_jobs.
{% endhint %}

### Keys and list

```lua
Config.Keys = { list = 'F9', accept = 'Y', dismiss = 'J', panic = 'PAGEUP' }
Config.ListCommand = 'dispatch'
```

Players can rebind all keys in GTA settings → Key Bindings → FiveM.

### Cards, alerts, merging

```lua
Config.Cards  = { max = 4, duration = 15 }
Config.Alerts = { max = 60, lifetime = 600, pruneInterval = 15 }
Config.Merge  = { radius = 80.0, window = 90 }
```

### Blips and sounds

```lua
Config.Blips = { enabled = true, duration = 180, flashPriority = 1, route = true }
Config.Sounds = {
    enabled = true,
    [1] = { name = 'Beep_Red', set = 'DLC_HEIST_HACKING_SNAKE_SOUNDS' },
    [2] = { name = 'Event_Start_Text', set = 'GTAO_FM_Events_Soundset' },
    [3] = { name = 'SELECT', set = 'HUD_FRONTEND_DEFAULT_SOUNDSET' },
}
```

### Calls

Applies to chat commands, the citizen tablet and the `Call` export.

```lua
Config.Calls = {
    cooldown = 45, liveCooldown = 60,
    minLength = 3, maxLength = 200,
    livePriority = 1,
    ringTimeout = 90,
    hiddenLocation = 300.0,  -- blur radius when location is not shared
    history = 10,            -- "my reports" per player
    chat = { maxLength = 200, keep = 60, interval = 1, linger = 300 },
}
```

### Unit tracking (live map)

```lua
Config.UnitTracking = {
    enabled = true,
    updateInterval = 1000,   -- ms
    minMoveDistance = 4.0,
    minHeadingChange = 12.0,
    vehicle = true,
    subscribeCooldown = 750, -- ms
    jobs = {
        police = { enabled = true, visibleJobs = { 'police' } },
        ambulance = { enabled = true, visibleJobs = { 'ambulance' } },
        mechanic = { enabled = true, visibleJobs = { 'mechanic' } },
    },
}
Config.JobCheck = 5
```

A unit only appears when on duty (`ccDuty`); the framework job alone is never enough for the map. Cross-visibility example: `police = { enabled = true, visibleJobs = { 'police', 'ambulance' } }`.

### Dispatch desk

```lua
Config.Desk = {
    command = 'leitstelle', key = 'F10',
    distance = 1.6, refresh = 3,
    broadcastCooldown = 10, broadcastMax = 200, broadcastKeep = 8,
    noteMax = 300,
    marker = { enabled = true, type = 1, ... },
}
```

### Citizen tablet

```lua
Config.CitizenTablet = { enabled = true, key = 'F6', command = 'notruf', refresh = 5 }
```

F6 opens the tablet only for players who are **not** responders; the command works for everyone.

### Panic button

```lua
Config.Panic = {
    enabled = true,
    allowed = { 'police', 'ambulance' },  -- who may press it (ccDuty.job)
    jobs = { 'police', 'ambulance' },     -- who receives it
    holdMs = 600,
    cooldown = 60,
}
```

### Test mode

```lua
Config.Test = { duration = 900, defaultJob = 'police' }
```

`/dispatchtest` makes the admin a receiver for `duration` seconds without setting `ccDuty`.

### Automatic alerts (`Config.Auto`)

```lua
shooting = {
    enabled = true, cooldown = 20, ignoreSilenced = false,
    ignoreJobs = { 'police' },
    exceptions = { 'WEAPON_STUNGUN', 'WEAPON_FIREEXTINGUISHER', ... },
    ignoreZones = { { coords = vector3(13.6, -1097.2, 29.8), radius = 25.0 }, ... },
},
accident = { enabled = true, cooldown = 90, minSpeed = 60, speedDrop = 40, damage = 60, interval = 150, ignoreClasses = { 13 } },
player_down = { enabled = true, delay = 5, cooldown = 60, officerJobs = { 'police', 'ambulance' }, officerAlso = { 'police' } },
```

### Alert kinds (`Config.Kinds`)

Defaults per kind; every field can be overridden by `exports.cc_dispatch:Alert`. Kinds: `shooting`, `accident`, `player_down`, `call`, `panic`, `robbery`, `custom`.

```lua
shooting = {
    jobs = { 'police' }, code = '10-71', title = 'dispatch.title.shooting', priority = 2,
    blip = { sprite = 110, colour = 1, scale = 1.0 }, radius = 70.0, fuzz = 35.0, merge = true,
    lifetime = 300,
},
```

`radius` = area blip instead of a point, `fuzz` = random offset of the center, `merge` = merge similar alerts.

### UI (`config/ui.lua`)

```lua
Config.Debug = false
Config.UI = {
    theme = 'default', primary = '#8B5CF6', radius = 'md', scale = 1.0,
    locale = 'de', animations = true, blur = false, sounds = true,
}
```

Card accent colors follow `Config.Jobs[job].color`; `primary` only colors buttons and selection.

## Exports, Events & Commands

### Commands and keys

| What               | Default                                                          | Who                          |
| ------------------ | ---------------------------------------------------------------- | ---------------------------- |
| Alert list         | `F9` (`cc_dispatch_list`), `/dispatch`                           | units                        |
| Accept top card    | `Y` (`cc_dispatch_accept`)                                       | units                        |
| Dismiss top card   | `J` (`cc_dispatch_dismiss`)                                      | units                        |
| Panic (hold)       | `Page Up` (`+cc_dispatch_panic`)                                 | `Config.Panic.allowed`       |
| Dispatch desk      | `E` at the desk                                                  | job, from `desk.minGrade`    |
| Desk from anywhere | `F10` (`cc_dispatch_desk`), `/leitstelle`                        | job, from `desk.remoteGrade` |
| Citizen tablet     | `F6` (`cc_dispatch_tablet`)                                      | non-responders only          |
| Citizen tablet     | `/notruf`                                                        | everyone                     |
| Chat call          | `/911`, `/112`, `/mechaniker`, `/abschlepp` (from `Config.Jobs`) | everyone                     |
| Test alert         | `/dispatchtest <kind> [job]`                                     | ACE `command.dispatchtest`   |

### Server exports

| Export                                | Returns                                                                     |
| ------------------------------------- | --------------------------------------------------------------------------- |
| `Alert(data)`                         | alert ID                                                                    |
| `Call(data)`                          | `{ ok, id, number, conversation? }` or `{ ok = false, error, vars }`        |
| `Jobs()`                              | callable jobs `{ { name, label, color, icon, command, live, categories } }` |
| `GetAlerts(job?)`                     | active alerts, newest first (copies)                                        |
| `OnDutyUnits(job?)`                   | `{ { source, name, job, callsign, status, alert } }`                        |
| `RemoveAlert(id)`                     | removes an alert                                                            |
| `AcceptAlert(src, id)`                | `{ ok, id?, coords?, message? }`, server checks the job                     |
| `ChatSend(src, conversationId, text)` | `{ ok, message? }`                                                          |
| `ChatHangup(src, conversationId)`     | `{ ok, message? }`                                                          |
| `Conversations(src)`                  | all conversations of the player                                             |
| `GetUnitsFor(src)`                    | units this player may see                                                   |
| `GetUnitsForJob(job)`                 | all active units of a job (server code only)                                |
| `IsTrackingJob(job)`                  | `boolean`                                                                   |
| `CanSeeUnits(src)`                    | `boolean`                                                                   |

Unit (live map): `{ serverId, job, name, callsign, x, y, heading, status, vehicle = { plate, hash }? }`.

```lua
local id = exports.cc_dispatch:Alert({
    kind = 'robbery',                 -- shooting|player_down|accident|call|panic|robbery|custom
    jobs = { 'police' },              -- otherwise Config.Kinds[kind].jobs
    coords = vector3(x, y, z),        -- if missing, the caller's position is used
    street = 'Vinewood Blvd',
    title = 'Jewelry store robbery',
    message = 'Register broken open',
    priority = 1,                     -- 1..3
    code = '10-90',
    caller = src,
    callerName = 'Anna Smith',
    blip = { sprite = 500, colour = 1, scale = 1.0 },
    radius = 60.0,
    expires = 600,
    merge = true,
})

-- Phone integration: same checks as command and tablet
local result = exports.cc_dispatch:Call({
    job = 'mechanic', caller = src, message = 'Flat tire',
    coords = nil, anonymous = false, category = 'tire',
    callback = true, shareLocation = true, live = false,
})
```

### Client exports

| Export                | Returns                                                                            |
| --------------------- | ---------------------------------------------------------------------------------- |
| `IsResponder()`       | `true` = responder (on duty, or job without duty requirement). Decides who owns F6 |
| `SubscribeUnits(on)`  | subscribe/unsubscribe own map, `true`                                              |
| `IsUnitsSubscribed()` | `boolean`                                                                          |
| `GetMapLayers()`      | `{ { id, type, label, url, bounds, ... } }`, first entry is the default            |
| `SetWaypoint(x, y)`   | `boolean`                                                                          |

```lua
if exports.cc_dispatch:IsResponder() then
    -- open your own job menu
end
```

### Events

| Event                      | Side           | Payload                                                                         |
| -------------------------- | -------------- | ------------------------------------------------------------------------------- |
| `cc_dispatch:alert`        | client (net)   | new alert                                                                       |
| `cc_dispatch:update`       | client (net)   | alert changed                                                                   |
| `cc_dispatch:remove`       | client (net)   | `id`                                                                            |
| `cc_dispatch:assigned`     | client (net)   | `{ alert, by }`                                                                 |
| `cc_dispatch:broadcast`    | client (net)   | `{ job, from, text }`                                                           |
| `cc_dispatch:desk`         | client (net)   | `{ job, count }`                                                                |
| `cc_dispatch:callerUpdate` | client (net)   | `{ id, number, job, jobs, status, unit?, reason? }`                             |
| `cc_dispatch:chat`         | client (net)   | conversation `{ id, alert, number, job, state, side, peer, endedBy, messages }` |
| `cc_dispatch:units`        | client (local) | live map changes (only with `SubscribeUnits(true)`)                             |
| `cc_dispatch:units:ready`  | client (local) | live map ready                                                                  |
| `cc_dispatch:created`      | server (local) | new alert (for logs, MDT, Discord)                                              |

Subscribe to client net events with `RegisterNetEvent`.

### State bags (read only)

* `ccDuty = { job, callsign, ... }`, set by job resources (cc\_police, cc\_ambulance)
* `ccDown`, set by cc\_ambulance

## FAQ & Troubleshooting

#### Police / EMS receive no alerts

Jobs with `dutyRequired = true` only receive alerts while `ccDuty` is set, which is done by cc\_police/00%20Overview\.md) / cc\_ambulance/00%20Overview\.md) when going on duty. cc\_dispatch never sets it. For testing use `/dispatchtest <kind> [job]` (ACE `command.dispatchtest`).

#### Alerts are gone after a restart

By design: there is no database. Alerts, conversations and "my reports" are lost on restart.

#### F6 does not open the citizen tablet

F6 only opens the tablet for non-responders. On-duty police/EMS and mechanics have their own job menu on F6. `/notruf` opens the tablet for everyone.

#### My job has a desk but I cannot open it

Check `desk.minGrade` (at the desk) and `desk.remoteGrade` (F10 / `/leitstelle`; `false` = desk only). A duty job named differently in the framework than in `ccDuty` (e.g. `police` vs `lspd`) has no rank for the desk.

#### A unit does not appear on the live map

Units only appear while on duty (`ccDuty`) and when their job is enabled in `Config.UnitTracking.jobs`. Visibility is controlled per job with `visibleJobs`.

#### Too many shooting alerts / shooting ranges trigger alerts

Adjust `Config.Auto.shooting` (`cooldown`, `exceptions`, `ignoreZones`, `ignoreJobs`, `ignoreSilenced`) and `Config.Merge`.

#### Add a new job (taxi, fire department)

Add one entry to `Config.Jobs` and, for the live map, one entry to `Config.UnitTracking.jobs`.

#### Known limitations

* No alerts for melee or explosions; no NPC witnesses.
* Accidents are only reported by a driver with a client.
* Street names come from the client; coordinates are measured by the server.
* No voice: a "call" is a text conversation.
* The desk sketch map is not a street map.
* Desk locations in the config are approximate; set them via cc\_jobs/00%20Overview\.md).

Still stuck? See Support.


---

# 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://ccfivem.gitbook.io/ccfivem-docs/police-ems-and-justice/dispatch-cc_dispatch.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.
