> ## Documentation Index
> Fetch the complete documentation index at: https://docs.redutzu.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Events

> Events the MDT triggers and listens to.

Events fire **after** an action has happened, so they only tell you about it. To block or change an action before it happens, use a [hook](https://docs.redutzu.com/redutzu-mdt/api-reference/hooks.md).

Server events are local (`TriggerEvent`), so listen with `AddEventHandler`, not `RegisterNetEvent`. That way clients can't fake them.

```lua theme={null}
AddEventHandler('redutzu-mdt:incident:created', function(id, source)
    print(('Incident #%s filed by %s'):format(id, source))
end)
```

* Arguments are positional, in the order listed in the tables.
* `source` is the player who did the action. It's `nil` for automated actions (exports, timers, expiry). Read it from the arguments, not from the global `source`.
* `identifier` is a citizen or officer identifier, `job` is the department job name, and chat `messageId`s are strings.

## Examples

### Relay logs

`log:created` fires for every MDT log entry. Use it to forward logs to Discord, Fivemanage or your own logging. The built-in Discord relay in `server/custom/logs/discord.lua` listens to this event too.

```lua theme={null}
AddEventHandler('redutzu-mdt:log:created', function(entry)
    -- entry = { action, author, message, metadata }
    exports.my_logs:Send('mdt', entry.action, entry.message, entry.metadata)
end)
```

### Bill charges

`charge:issued` fires once per charged citizen, after the MDT has applied the charges.

```lua theme={null}
AddEventHandler('redutzu-mdt:charge:issued', function(entityType, entityId, identifier, charges, source)
    local total = 0

    for _, charge in ipairs(charges) do
        total = total + (charge.fine or 0) * (charge.qty or 1)
    end

    print(('%s was fined $%s on %s #%s'):format(identifier, total, entityType, entityId))
end)
```

### Pay officers per shift

```lua theme={null}
AddEventHandler('redutzu-mdt:shift:ended', function(identifier, job, duration, reason)
    local minutes = math.floor(duration / 60)
    exports.my_payroll:AddHours(identifier, job, minutes)
end)
```

### Mirror dispatch calls

`call` is the full dispatch call: `id`, `type`, `code`, `priority`, `title`, `description`, `coords`, `details`, `jobs`, `units`, `createdBy`, `createdAt` and more.

```lua theme={null}
AddEventHandler('redutzu-mdt:dispatch-call:created', function(call)
    exports.my_tablet:PushAlert(call.jobs, call.title, call.coords)
end)
```

### Send an MDT notification

`redutzu-mdt:client:notification` shows a toast and adds it to the notification bell, whether the MDT is open or not.

```lua theme={null}
TriggerClientEvent('redutzu-mdt:client:notification', source, {
    title = 'Court date',
    description = 'You are summoned to court tomorrow at 18:00',
    route = '/incidents', -- optional, MDT page opened when clicked, must start with /
})
```

## Server events

### Incidents & charges

| Name | Arguments | Description |
| - | - | - |
| `redutzu-mdt:incident:created` | `id`, `source?` | An incident was filed |
| `redutzu-mdt:incident:updated` | `id`, `data`, `source?` | An incident was saved, `data` holds the saved fields |
| `redutzu-mdt:incident:deleted` | `id`, `source?` | An incident was deleted |
| `redutzu-mdt:charge:issued` | `entityType`, `entityId`, `identifier`, `charges`, `source?` | Charges were applied to a citizen. `entityType` is `penalty`, `incident` or `warrant` |
| `redutzu-mdt:note:added` | `id`, `entityType`, `entityId`, `source?` | A note was added to a record |
| `redutzu-mdt:note:removed` | `id`, `entityType`, `entityId`, `source?` | A note was removed from a record |

### Warrants

| Name | Arguments | Description |
| - | - | - |
| `redutzu-mdt:warrant:created` | `id`, `source?` | A warrant was filed |
| `redutzu-mdt:warrant:updated` | `id`, `status`, `source?` | A warrant's status changed, `source` is `nil` when it expired |
| `redutzu-mdt:warrant:served` | `id`, `citizen`, `source` | A warrant was served on `citizen` |
| `redutzu-mdt:warrant:deleted` | `id`, `source?` | A warrant was deleted |
| `redutzu-mdt:operation-plan:created` | `id`, `source?` | An operation plan was created |
| `redutzu-mdt:operation-plan:updated` | `id`, `source?` | An operation plan was saved |
| `redutzu-mdt:operation-plan:deleted` | `id`, `source?` | An operation plan was deleted |
| `redutzu-mdt:operation-plan:mission-set` | `id`, `action`, `source?` | The mission was started or ended, `action` is `start` or `end` |
| `redutzu-mdt:operation-plan:mission-changed` | `id`, `action` | The mission state changed automatically |
| `redutzu-mdt:operation-plan:acknowledged` | `id`, `source` | An officer acknowledged the plan |

### BOLOs

| Name | Arguments | Description |
| - | - | - |
| `redutzu-mdt:bolo:created` | `id`, `source?` | A BOLO was created |
| `redutzu-mdt:bolo:updated` | `id`, `status`, `source?` | A BOLO's status changed, `source` is `nil` when it expired |
| `redutzu-mdt:bolo:located` | `id`, `source` | The BOLO subject was located |
| `redutzu-mdt:bolo:deleted` | `id`, `source?` | A BOLO was deleted |

### Evidence

| Name | Arguments | Description |
| - | - | - |
| `redutzu-mdt:evidence:created` | `id`, `source`, `stashId?` | Evidence was logged |
| `redutzu-mdt:evidence:updated` | `id`, `data`, `source` | Evidence was saved |
| `redutzu-mdt:evidence:deleted` | `id`, `source` | Evidence was deleted |
| `redutzu-mdt:evidence:custody-transferred` | `id`, `transfer`, `source` | Custody was transferred, `transfer` is the transfer record |
| `redutzu-mdt:evidence:analysis-requested` | `id`, `analysisId`, `source` | A lab analysis was requested |
| `redutzu-mdt:evidence:analysis-completed` | `evidenceId`, `analysisId`, `result`, `source?` | A lab analysis finished |

### Citizens & prisoners

| Name | Arguments | Description |
| - | - | - |
| `redutzu-mdt:citizen:registered` | `identifier`, `source?` | A citizen was registered |
| `redutzu-mdt:citizen:profile-updated` | `identifier`, `data`, `source?` | A citizen profile was saved |
| `redutzu-mdt:citizen:image-changed` | `identifier`, `url`, `source?` | A citizen's photo changed |
| `redutzu-mdt:citizen:license-changed` | `identifier`, `licenseType`, `active`, `source?` | A license was granted or revoked |
| `redutzu-mdt:citizen:license-points-changed` | `identifier`, `points`, `total`, `threshold`, `source?` | License points were added or removed |
| `redutzu-mdt:prisoner:released` | `identifier`, `reason?`, `source?` | A prisoner was released early |
| `redutzu-mdt:prisoner:sentence-changed` | `identifier`, `time`, `reason?`, `source?` | A sentence was changed, `time` uses the penal code's unit |

### Vehicles & weapons

| Name | Arguments | Description |
| - | - | - |
| `redutzu-mdt:vehicle:registered` | `plate`, `source?` | A vehicle was registered |
| `redutzu-mdt:vehicle:updated` | `plate`, `patch`, `source?` | A vehicle record changed, `patch` holds the changed fields |
| `redutzu-mdt:vehicle:profile-updated` | `plate`, `data`, `source?` | A vehicle profile was saved |
| `redutzu-mdt:vehicle:image-changed` | `plate`, `url`, `source?` | A vehicle's photo changed |
| `redutzu-mdt:vehicle:modification-added` | `plate`, `modification`, `source?` | A modification was added, `{ id, name, category }` |
| `redutzu-mdt:vehicle:modification-removed` | `plate`, `modification`, `source?` | A modification was removed, `{ id, name, category }` |
| `redutzu-mdt:weapon:created` | `serial`, `source?` | A weapon was registered |
| `redutzu-mdt:weapon:updated` | `serial`, `status`, `source?` | A weapon's status changed |

### Dispatch

| Name | Arguments | Description |
| - | - | - |
| `redutzu-mdt:dispatch-call:created` | `call` | A dispatch call was created |
| `redutzu-mdt:dispatch-call:updated` | `call` | Any change to a call |
| `redutzu-mdt:dispatch-call:unit-attached` | `call`, `callsign` | A unit attached to the call |
| `redutzu-mdt:dispatch-call:unit-detached` | `call`, `callsign` | A unit detached from the call |
| `redutzu-mdt:dispatch-call:unit-arrived` | `call`, `callsign`, `travelMs` | A unit arrived on scene |
| `redutzu-mdt:dispatch-call:acknowledged` | `call`, `callsign` | A unit acknowledged the call |
| `redutzu-mdt:dispatch-call:resolved` | `call`, `cause` | The call closed, for example `cause` is `resolved` |

### Field

| Name | Arguments | Description |
| - | - | - |
| `redutzu-mdt:suspect:cuff-changed` | `target`, `cuffed`, `source` | A player was cuffed or uncuffed |
| `redutzu-mdt:radar:plate-hit` | `source`, `alert` | The radar flagged a plate |
| `redutzu-mdt:prop:placed` | `prop` | An officer placed a prop, `{ entity, netId, key, model, source }` |
| `redutzu-mdt:prop:removed` | `prop` | A prop was removed, `{ entity, key, owner, reason, source }` |
| `redutzu-mdt:officer:status-changed` | `source`, `status` | An officer changed status |
| `redutzu-mdt:officer:bodycam-changed` | `source`, `active` | An officer turned their bodycam on or off |

### Cameras

| Name | Arguments | Description |
| - | - | - |
| `redutzu-mdt:camera:created` | `id`, `source` | A CCTV camera was placed |
| `redutzu-mdt:camera:updated` | `id`, `source` | A CCTV camera was edited |
| `redutzu-mdt:camera:deleted` | `id`, `source` | A CCTV camera was removed |
| `redutzu-mdt:camera:imported` | `ids`, `source`, `replace` | Cameras were imported, `replace` is `true` when existing ones were replaced |
| `redutzu-mdt:camera:destroyed` | `id`, `attackerSource?`, `weaponHash?` | A CCTV camera was destroyed |

The following events fire for every camera type. Replace `<kind>` with `camera`, `bodycam` or `dashcam`. `id` is the camera id for CCTV and the entity id for bodycams and dashcams.

| Name | Arguments | Description |
| - | - | - |
| `redutzu-mdt:<kind>:viewed` | `id`, `source` | An officer opened the feed |
| `redutzu-mdt:<kind>:left` | `id`, `source` | An officer closed the feed |
| `redutzu-mdt:<kind>:recording-started` | `id`, `source` | Recording started |
| `redutzu-mdt:<kind>:recording-ended` | `id`, `source` | Recording stopped |
| `redutzu-mdt:<kind>:screenshot-taken` | `id`, `source` | A screenshot was taken |

### Chat

| Name | Arguments | Description |
| - | - | - |
| `redutzu-mdt:chat:sent` | `messageId`, `channel`, `source` | A message was sent |
| `redutzu-mdt:chat:image-sent` | `messageId`, `channel`, `source` | An image was sent |
| `redutzu-mdt:chat:location-sent` | `messageId`, `channel`, `source` | A location was shared |
| `redutzu-mdt:chat:location-updated` | `messageId`, `channel`, `sharer`, `live` | A live location moved or stopped |
| `redutzu-mdt:chat:record-shared` | `messageId`, `channel`, `source` | A record was shared |
| `redutzu-mdt:chat:edited` | `messageId`, `channel`, `source` | A message was edited |
| `redutzu-mdt:chat:deleted` | `messageId`, `channel`, `source` | A message was deleted |
| `redutzu-mdt:chat:reacted` | `messageId`, `channel`, `source`, `emoji`, `removed` | A reaction was added or removed |
| `redutzu-mdt:chat:pinned` | `messageId`, `channel`, `source?` | A message was pinned |
| `redutzu-mdt:chat:unpinned` | `messageId`, `channel`, `source?` | A message was unpinned |
| `redutzu-mdt:chat:poll-created` | `messageId`, `channel`, `source` | A poll was created |
| `redutzu-mdt:chat:voted` | `messageId`, `channel`, `optionId`, `source`, `removed` | A vote was cast or removed |
| `redutzu-mdt:chat:system-message-posted` | `messageId`, `channel`, `key` | The MDT posted a system message |
| `redutzu-mdt:chat:dm-opened` | `channelId`, `targetIdentifier`, `source` | A direct message was opened |
| `redutzu-mdt:chat:marked-read` | `channel`, `messageId`, `source` | A channel was read up to `messageId` |
| `redutzu-mdt:chat:marked-unread` | `channel`, `messageId`, `source` | A channel was marked unread from `messageId` |

### Collaboration

| Name | Arguments | Description |
| - | - | - |
| `redutzu-mdt:collab:room-created` | `docId`, `source`, `visibility` | A collaboration room was opened |
| `redutzu-mdt:collab:room-closed` | `docId`, `reason` | A room closed, `reason` is `closed` or `owner_dropped` |
| `redutzu-mdt:collab:room-joined` | `docId`, `source` | A player joined a room |
| `redutzu-mdt:collab:room-left` | `docId`, `source` | A player left a room |
| `redutzu-mdt:collab:member-invited` | `docId`, `source`, `targetSource` | A player was invited |
| `redutzu-mdt:collab:invite-expired` | `docId`, `identifier` | An invite expired |
| `redutzu-mdt:collab:member-kicked` | `docId`, `source`, `targetIdentifier`, `banned` | A member was kicked or banned |
| `redutzu-mdt:collab:member-connected` | `docId`, `identifier` | A member's connection opened |
| `redutzu-mdt:collab:member-disconnected` | `docId`, `identifier` | A member's connection dropped |
| `redutzu-mdt:collab:ownership-transferred` | `docId`, `identifier`, `previousOwnerId?` | Room ownership moved to `identifier` |

### Employees

| Name | Arguments | Description |
| - | - | - |
| `redutzu-mdt:employee:recruited` | `identifier`, `job`, `grade`, `callsign`, `source?` | An officer was hired |
| `redutzu-mdt:employee:fired` | `identifier`, `job`, `reason?`, `source?` | An officer was fired |
| `redutzu-mdt:employee:grade-changed` | `identifier`, `job`, `grade`, `source?` | An officer was promoted or demoted |
| `redutzu-mdt:employee:callsign-changed` | `identifier`, `job`, `callsign`, `source?` | An officer's callsign changed, `source` is `nil` when assigned automatically |
| `redutzu-mdt:employee:badge-changed` | `identifier`, `job`, `badge`, `source?` | An officer's badge number changed |
| `redutzu-mdt:employee:strike-added` | `id`, `identifier`, `job`, `count`, `source?` | A strike was added, `count` is the new total |
| `redutzu-mdt:employee:strike-removed` | `id`, `identifier`, `job`, `count`, `source?` | A strike was removed, `count` is the new total |
| `redutzu-mdt:employee:review-created` | `id`, `identifier`, `job`, `source?` | A performance review was written |
| `redutzu-mdt:employee:review-updated` | `id`, `identifier`, `job`, `source?` | A review was edited |
| `redutzu-mdt:employee:review-deleted` | `id`, `identifier`, `job`, `source?` | A review was deleted |
| `redutzu-mdt:employee:hr-note-created` | `id`, `identifier`, `job`, `source?` | An HR note was written |
| `redutzu-mdt:employee:hr-note-updated` | `id`, `identifier`, `job`, `source?` | An HR note was edited |
| `redutzu-mdt:employee:hr-note-deleted` | `id`, `identifier`, `job`, `source?` | An HR note was deleted |

### Shifts, certifications & achievements

| Name | Arguments | Description |
| - | - | - |
| `redutzu-mdt:shift:started` | `source`, `identifier`, `job` | An officer clocked in |
| `redutzu-mdt:shift:ended` | `identifier`, `job`, `duration`, `reason` | An officer clocked out, `duration` is in seconds and `reason` defaults to `self` |
| `redutzu-mdt:certification:granted` | `identifier`, `job`, `certificationTypeId`, `source?` | A certification was granted |
| `redutzu-mdt:certification:revoked` | `identifier`, `job`, `certificationTypeId`, `source?` | A certification was revoked |
| `redutzu-mdt:certification-type:created` | `id`, `job`, `source?` | A certification type was created |
| `redutzu-mdt:certification-type:updated` | `id`, `job`, `source?` | A certification type was edited |
| `redutzu-mdt:certification-type:deleted` | `id`, `job`, `source?` | A certification type was deleted |
| `redutzu-mdt:achievement:created` | `id`, `job`, `source?` | An achievement was created |
| `redutzu-mdt:achievement:updated` | `id`, `job`, `source?` | An achievement was edited |
| `redutzu-mdt:achievement:deleted` | `id`, `job`, `source?` | An achievement was deleted |

### Department

| Name | Arguments | Description |
| - | - | - |
| `redutzu-mdt:management:funds-adjusted` | `job`, `type`, `amount`, `reason?`, `source` | Society money was deposited or withdrawn, `type` is `deposit` or `withdraw` |
| `redutzu-mdt:management:permission-rank-changed` | `job`, `permission`, `grade`, `source` | The minimum grade for a permission changed |
| `redutzu-mdt:unit:updated` | `unitId` | A unit was created or changed |
| `redutzu-mdt:unit:removed` | `unitId` | A unit was disbanded |
| `redutzu-mdt:record:access-changed` | `entityType`, `id`, `visibility`, `jobs`, `source?` | A record's visibility changed |
| `redutzu-mdt:radio-code:created` | `id`, `source?` | A radio code was created |
| `redutzu-mdt:radio-code:updated` | `id`, `source?` | A radio code was edited |
| `redutzu-mdt:radio-code:deleted` | `id`, `source?` | A radio code was deleted |
| `redutzu-mdt:tag:created` | `id`, `source?` | A tag was created |
| `redutzu-mdt:tag:updated` | `id`, `source?` | A tag was edited |
| `redutzu-mdt:tag:deleted` | `id`, `source?` | A tag was deleted |

### Penal code, settings & logs

| Name | Arguments | Description |
| - | - | - |
| `redutzu-mdt:penal-category:updated` | `id`, `source?` | A penal category was saved |
| `redutzu-mdt:penal-category:deleted` | `id`, `source?` | A penal category was deleted |
| `redutzu-mdt:penal-article:updated` | `id`, `source?` | A penal article was saved |
| `redutzu-mdt:penal-article:deleted` | `id`, `source?` | A penal article was deleted |
| `redutzu-mdt:admin:setting-changed` | `key`, `value`, `source?` | An admin panel setting changed |
| `redutzu-mdt:notification:sent` | `type`, `entries` | MDT notifications were sent |
| `redutzu-mdt:log:created` | `entry` | A log entry was written, `{ action, author, message, metadata }` |

## Client events

| Name | Arguments | Description |
| - | - | - |
| `redutzu-mdt:client:open` | | The MDT opened |
| `redutzu-mdt:client:close` | | The MDT closed |
| `redutzu-mdt:client:player-died` | | The local player died, the MDT closes itself |
| `redutzu-mdt:client:notification` | `notification` | Trigger from the server to show a notification, `{ title, description?, route? }` |
| `redutzu-mdt:client:settings-changed` | `keys` | Admin panel settings changed, `{ [key] = true }` |
| `redutzu-mdt:client:interactions-page-changed` | `page?` | The interactions menu switched page, `nil` when it closed |

### Voice bridge

A voice bridge must fire these local client events so the Radio page can show who's on the channel. See `client/custom/voice/pma-voice.lua` for a reference bridge.

| Name | Arguments | Description |
| - | - | - |
| `redutzu-mdt:voice:radio-roster` | `members` | Everyone on the channel, `{ [serverId] = isTalking }` |
| `redutzu-mdt:voice:radio-member-joined` | `src`, `name?` | A player joined the channel |
| `redutzu-mdt:voice:radio-member-left` | `src` | A player left the channel, also fires for the local player |
| `redutzu-mdt:voice:radio-member-talking` | `src`, `talking` | A player started or stopped talking |
| `redutzu-mdt:voice:radio-channel` | `channel` | The local player's channel changed, `0` for none |
| `redutzu-mdt:voice:radio-transmitting` | `talking` | The local player started or stopped transmitting |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.