Events
| Group | Side | Direction |
|---|---|---|
| Dispatcher events | server | koja-dispatcher → your resource |
| Events you can trigger | server / client | your resource → koja-dispatcher |
| koja-mdt events | server | koja-mdt → koja-dispatcher |
| Client events | client | koja-dispatcher → the player's client |
| Internal events | both | do not use |
Dispatcher events
Every after hook is also fired as a server-local event koja-dispatcher:<hook>, with the same payload, right after the hook functions ran.
AddEventHandler('koja-dispatcher:incidentEnded', function(incident)
if incident.status == 'resolved' then
print(('incident #%d closed: %s'):format(incident.id, incident.outcome))
end
end)| Event | Payload |
|---|---|
koja-dispatcher:callCreated | public call |
koja-dispatcher:callAnswered | { id, kind, handler } |
koja-dispatcher:callEnded | { id, kind, incident, handler } |
koja-dispatcher:callMissed | { id, caller, scenario, label, waited } |
koja-dispatcher:callDropped | { id, caller, scenario, label, handler } |
koja-dispatcher:incidentCreated | public incident |
koja-dispatcher:incidentUpdated | public incident |
koja-dispatcher:incidentEnded | public incident (status, outcome, endedAt) |
koja-dispatcher:unitAssigned | { unit, src, callsign, incident } |
koja-dispatcher:unitStatus | { unit, src, callsign, status, previous } |
koja-dispatcher:unitAdded | public unit |
koja-dispatcher:unitRemoved | { unit, src, callsign } |
koja-dispatcher:shiftStarted | { source, name, console } |
koja-dispatcher:shiftEnded | { source, name, minutes, stats, earned, rating } |
koja-dispatcher:settingChanged | { key, value, previous } |
koja-dispatcher:adminAction | { source, name, text } |
koja-dispatcher:mciStarted | { incident, patients, x, y } |
koja-dispatcher:mciEnded | { incident, transported, treated, seconds, status } |
koja-dispatcher:pursuitStarted | { incident, x, y, category } |
koja-dispatcher:pursuitEnded | { incident, outcome, seconds } |
koja-dispatcher:voiceConnected | { call, channel, provider, caller, handler } |
koja-dispatcher:voiceEnded | { call, channel, caller, handler, handlerKind, reason, duration } |
koja-dispatcher:boloCreated | internal BOLO (event only, no hook) |
Payload details are on the Hooks page.
koja-dispatcher:boloCreated
Fires when a BOLO is created in the dispatcher's own list (mdt_tools.boloSource = 'internal', or auto while koja-mdt is not running), from the CAD or the CreateBolo export. BOLOs created in koja-mdt fire koja-mdt's own events instead.
{
id = 'i12', source = 'internal', type = 'vehicle',
title = 'Black Sultan, armed', description = nil,
plate = 'ABC123', model = 'Sultan', colour = 'Black', priority = 1, image = nil,
x = 215.8, y = -810.1, label = 'Legion Square', incident = 1004,
createdBy = 'Jane Doe', createdAt = 1730000000,
}
AddEventHandler('koja-dispatcher:boloCreated', function(bolo)
if bolo.plate then exports['my-alpr']:Watch(bolo.plate) end
end)Events you can trigger
koja-dispatcher:phone:call (net, client → server)
Generic 911 entry point for any phone resource. The text is handled like a call to the service number: it goes through /911 logic, follow-up texts join the open call and dispatcher replies come back to the player (as phone messages when a supported phone is running, otherwise as normal notifications).
| Argument | Type | Description |
|---|---|---|
text | string | Message (control characters removed, max 250). |
TriggerServerEvent('koja-dispatcher:phone:call', 'My neighbour is screaming for help')Only phones.enabled must be on; no supported phone is needed. The same player can call once every 2 seconds (Config.Phones.cooldown). When nobody can take the call, the player gets a "no dispatchers" message.
koja-dispatcher:compat:alert (net, client → server)
Feeds a client-side alert in the format of another dispatch script.
| Argument | Type | Description |
|---|---|---|
name | string | 'ps-dispatch', 'cd_dispatch', 'rcore_dispatch', 'qs-dispatch', 'core_dispatch' or 'linden_outlawalert'. |
data | table | The alert in that script's format (see CompatAlert). |
Rate limited to 8 alerts per 10 seconds per player (Config.Compat.rate), coordinates more than 600 m from the player's ped are replaced by the ped position (Config.Compat.maxDistance), and identical alerts within 20 s are dropped.
TriggerServerEvent('koja-dispatcher:compat:alert', 'ps-dispatch', {
message = 'Store robbery', code = '10-90', priority = 1,
coords = GetEntityCoords(PlayerPedId()), jobs = { 'police' },
})Dispatch-script events
While compat.enabled is on, koja-dispatcher answers the events of other dispatch scripts only when that script is not running (a shim from compat/ counts as not running). Existing robbery and crime scripts keep working without edits.
| Event | Side | Script | Arguments |
|---|---|---|---|
ps-dispatch:server:notify | server (net) | ps-dispatch | data table |
cd_dispatch:AddNotification | server (net) | cd_dispatch | data table |
rcore_dispatch:server:sendAlert | server (net) | rcore_dispatch | data table |
qs-dispatch:server:CreateDispatchCall | server (net) | qs-dispatch | data table |
core_dispatch:addCall | server (net) | core_dispatch | code, title, extraInfo, coords, job, cooldown, sprite, color, priority |
wf-alerts:svNotify | server (net) | linden_outlawalert | data table |
wf-alerts:svNotify911 | server (net) | linden_outlawalert | message, caller, coords |
ps-dispatch:client:notify | client | ps-dispatch | data table, forwarded to the server |
cd_dispatch:AddNotification | client | cd_dispatch | data table, forwarded |
rcore_dispatch:server:sendAlert | client | rcore_dispatch | data table, forwarded |
qs-dispatch:server:CreateDispatchCall | client | qs-dispatch | data table, forwarded |
Server-side TriggerEvent (without a player) works too. Events sent by players use the same rate limit and 600 m clamp as koja-dispatcher:compat:alert.
Phone events
With phones.enabled on, the dispatcher listens to the running phone:
| Phone | Listened to |
|---|---|
| lb-phone | lb-phone:newCall (calls to the service number or to a company in Config.Phones.companies), lb-phone:newCompanyMessage, lb-phone:messages:messageSent (texts to the service number) |
| npwd | exports.npwd:onCall(number) and exports.npwd:onMessage(number) for the service number |
| qs-smartphone-pro | net events qs-smartphone:server:sendJobAlert and qs-smartphone-pro:server:sendJobAlert |
These are the phones' own events. You do not trigger them yourself.
koja-mdt events
With koja-mdt started and mdt.enabled on, koja-dispatcher listens to these server events. They are accepted only when koja-mdt fires them; the same events from other resources are ignored.
| Event | What koja-dispatcher does |
|---|---|
koja-mdt:dispatch:create | Rings the MDT call at the console (mdt.pullCalls = 'calls') or opens an incident ('incidents'), only while a dispatcher is on shift and mode.calls is not generated. Calls created by koja-dispatcher itself are skipped. |
koja-mdt:dispatch:close | Ends the linked call and resolves the linked incident (outcome = 'mdt'). |
koja-mdt:dispatch:assign | Assigns the officer's player unit to the linked incident (mdt.syncStatus). |
koja-mdt:dispatch:detach | Releases the officer's unit (mdt.syncStatus). |
koja-mdt:unit:status | Mirrors the MDT status (available, busy, traffic, break) to the player unit (mdt.syncStatus). |
koja-mdt:officer:duty | Re-checks which players are field units. |
koja-mdt:bolo:create, koja-mdt:bolo:update, koja-mdt:bolo:close | Refreshes the CAD BOLO list. |
koja-mdt:anpr:hit | Alerts dispatchers when the read plate has a BOLO (mdt_tools.alpr). |
To create MDT calls or status changes from your own resource, use the koja-mdt exports. koja-dispatcher picks the result up through these events.
Client events
koja-dispatcher sends these net events to the player's client. Other client scripts may listen to them, for example to show their own HUD. Register the name in your resource and add a handler; never trigger them yourself.
RegisterNetEvent('koja-dispatcher:client:fieldAssign', function(incident)
print(('assigned to #%d: %s at %s'):format(incident.id, incident.title, incident.label or '?'))
end)| Event | Sent to | Payload |
|---|---|---|
koja-dispatcher:client:fieldAssign | field unit | { id, category, title, priority, x, y, z, label, note, scene, located }. Sent on assignment and again when the incident moves. |
koja-dispatcher:client:fieldMove | field unit | { x, y } staging point from the CAD. |
koja-dispatcher:client:fieldOnScene | field unit | { id } the unit arrived. |
koja-dispatcher:client:fieldDone | field unit | { id, outcome } the incident ended. |
koja-dispatcher:client:fieldClear | field unit | {} the unit was released or recalled. |
koja-dispatcher:client:fieldStatus | field unit | the new status string ('available', 'enroute', ...). |
koja-dispatcher:client:fieldCode | field unit | { code, status, units, mdt } after a field code; { status = 'offline' } when the unit goes off duty. |
koja-dispatcher:client:boloNotice | on-duty police units | { title, plate, priority, label } new internal BOLO (mdt_tools.notifyUnits). |
koja-dispatcher:client:voiceRole | caller, dispatcher or unit in a voice call | role table { call, role, provider, elapsed, held, ... }, or false when the voice call ended. |
koja-dispatcher:client:sync | all players | shared client settings: { modules, consoles, cinematic, remote, fieldScenes, fieldWaypoint, clientAlerts }. modules holds every setting registered with client = true. |
koja-dispatcher:client:modules | local (client TriggerEvent) | the modules table after each sync. |
Dispatcher-only events (koja-dispatcher:client:call, incident, unit, radio, roster, ...) only reach players on shift and carry CAD data; their format can change between versions, so read the server exports instead.
Internal events
These are part of koja-dispatcher's own client/server protocol. Never trigger them from other resources. Server handlers check the sender and rate limits, and their arguments change between versions.
| Event | Why |
|---|---|
koja-dispatcher:rpc:request, koja-dispatcher:rpc:response, koja-dispatcher:rpc:clientRequest, koja-dispatcher:rpc:clientResponse | The CAD's RPC channel. Use the exports. |
koja-dispatcher:server:sync | Requests the client settings. |
koja-dispatcher:server:voice | AI caller speech upload from the dispatcher's microphone. |
koja-dispatcher:server:voiceTalking | Voice call talking indicator. |
koja-dispatcher:server:shots, koja-dispatcher:server:crash | Shots and crash sensors. Use CreateCall / CreateIncident for your own alerts. |
koja-dispatcher:server:avatar, koja-dispatcher:server:stationState, koja-dispatcher:server:terrainRows | Avatar capture, station screens, 3D terrain scan. |
koja-dispatcher:client:* | Sent by the server only. Triggering them locally only confuses that player's UI. |