# Events

Events are the lightweight way to react to the MDT. They never block an action; use [hooks](/new/mdt/api/hooks) when you need to cancel or change something.

:::hint{type="warning" title="Local events"}
All server events below are **server-local**: listen with `AddEventHandler`, not `RegisterNetEvent`. Clients cannot trigger them. Client events are local to the player's client.
:::

## Server events you can listen to

### Hook bridge

Every hook's after stage is fired as `koja-mdt:<hook>` with `(payload, src)`, for example `koja-mdt:dispatch:create`, `koja-mdt:incident:process`, `koja-mdt:citation:pay` or `koja-mdt:jail:create`. The payloads are listed on the [hooks](/new/mdt/api/hooks) page.

```lua title="server.lua"
AddEventHandler('koja-mdt:dispatch:create', function(call)
    if call.priority == 1 then exports['my-discord']:Post('Priority call: ' .. call.title) end
end)
```

### Dedicated events

| Event | Arguments | Fires when |
|---|---|---|
| `koja-mdt:server:dutyChanged` | `src, onDuty, departmentId, officer` | An officer goes on or off duty, from any source. `officer` is `{ source, identifier, name, callsign, department }`. |
| `koja-mdt:server:citationIssued` | `citation` | A citation is issued. |
| `koja-mdt:server:processed` | `target, { incidentId, identifier, fine, months }` | An incident suspect is processed. `target` is the server id or `nil`. |
| `koja-mdt:server:warrantActive` | `warrant` | A warrant becomes active (issued directly or approved). |
| `koja-mdt:server:sentenced` | `target, sentence, result` | A court sentence was applied. `sentence = { caseId, number, identifier, fine, months, applyFine, applyJail }`, `result = { fined, jailed }`. |
| `koja-mdt:server:jailPlayer` | `target, months, reason, officerSrc` | The MDT wants to jail a player and no built-in jail adapter handled it. Your prison script should jail the player. |
| `koja-mdt:server:releasePlayer` | `target, identifier, reason, system` | An early release from the MDT was not handled by a jail adapter. `target` can be `nil` when the inmate is offline. |
| `koja-mdt:server:impoundVehicle` | `plate, data, system` | An impound made in the MDT (tablet or `ImpoundVehicle`), after the impound adapters ran. `system` is the adapter that handled it, or `mdt`. `data = { reason, fee, hours, lot, officer }`. |
| `koja-mdt:server:releaseImpound` | `plate, data, system` | An impound released from the MDT (tablet or `ReleaseImpound`). `data = { feeStatus, note, officer }`. |
| `koja-mdt:server:custody` | `kind, record` | Every custody change. `kind`: `jail`, `released`, `escaped`, `transferred`, `impound`, `impoundRelease`. |
| `koja-mdt:server:medicalBillPaid` | `target, bill` | A medical bill was charged to a patient. |
| `koja-mdt:server:admission` | `admission` | A patient was admitted. |
| `koja-mdt:server:discharge` | `admission` | A patient was discharged. |

```lua title="server.lua"
AddEventHandler('koja-mdt:server:dutyChanged', function(src, onDuty, department)
    if src then TriggerClientEvent('my-radio:setChannelAccess', src, department, onDuty) end
end)

AddEventHandler('koja-mdt:server:jailPlayer', function(target, months, reason, officerSrc)
    exports['my-prison']:Jail(target, months, reason)
end)
```

## Server events you can trigger

Custody reports, the event form of `ReportJail`, `ReportRelease`, `ReportImpound` and `ReportImpoundRelease`. Trigger them with `TriggerEvent` from **server** code only; a client triggering them is flagged by the MDT security guard and ignored.

| Event | Arguments |
|---|---|
| `koja-mdt:custody:jail` | `who, months, data` |
| `koja-mdt:custody:release` | `who, reason, data` |
| `koja-mdt:custody:impound` | `plate, data` |
| `koja-mdt:custody:impoundRelease` | `plate, data` |

```lua title="server.lua"
TriggerEvent('koja-mdt:custody:jail', source, 30, { reason = 'Bank robbery', facility = 'Bolingbroke' })
```

:::hint{type="danger" title="Internal network events"}
`koja-mdt:server:*` network events (`requestOpen`, `publicCall`, `panic`, `bodycamToggle`, …) and `koja-mdt:rpc:*` are the MDT's own client-to-server channel. They are validated and rate limited for the MDT client; do not trigger them from other resources. Use the exports instead.
:::

## Client events you can listen to

| Event | Arguments | Fires when |
|---|---|---|
| `koja-mdt:client:opened` | | The tablet opened. |
| `koja-mdt:client:closed` | | The tablet closed. |
| `koja-mdt:client:dutyChanged` | `onDuty, departmentId` | The local player went on or off duty. |
| `koja-mdt:client:statusChanged` | `status, previous, callId` | The local unit's status or call changed. |
| `koja-mdt:client:newCall` | `call` | A new call reached this unit (same shape as `GetCall`). |
| `koja-mdt:client:callUpdated` | `call` | A call this unit can see changed (units, notes, status, edits). |
| `koja-mdt:client:notification` | `{ id, kind, title, body, actor, link, department, unread, createdAt }` | A notification center entry arrived. |
| `koja-mdt:client:bodycamChanged` | `state, previous, data` | The local bodycam state changed (`off`, `standby`, `recording`). |

```lua title="client.lua"
AddEventHandler('koja-mdt:client:newCall', function(call)
    if call.priority == 1 then PlaySoundFrontend(-1, 'TIMER_STOP', 'HUD_MINI_GAME_SOUNDSET', true) end
end)

AddEventHandler('koja-mdt:client:statusChanged', function(status, previous, callId)
    exports['my-hud']:SetUnitStatus(status)
end)
```

## Client events a server resource can trigger

| Event | Arguments | Effect |
|---|---|---|
| `koja-mdt:client:close` | | Closes the tablet. |
| `koja-mdt:client:waypoint` | `coords` | Sets the GPS waypoint (`{ x, y }`). |

```lua title="server.lua"
TriggerClientEvent('koja-mdt:client:close', target)
```

To open the tablet for a player, call the client export `OpenMdt()` (it goes through the access checks) instead of triggering the internal open event.
