# Integrations

koja-mdt talks to the rest of your server through three small files that are not escrowed and can be edited:

| File | Side | Contains |
|---|---|---|
| `editable/server/framework.lua` | Server | `Bridge`: people, vehicles, licenses, roster queries |
| `editable/server/integrations.lua` | Server | `Integrations`: jail, release, impound, fines, society payout, medical bills, sentence handling, events |
| `editable/client/integrations.lua` | Client | `ClientIntegrations`: death check, open rules, phone number, notifications |

The loading order is set in `fxmanifest.lua`: the editable files load before the rest of the resource, so everything else calls them.

## koja-lib

koja-lib is required. It provides the cross-framework layer used everywhere in the MDT: player lookup by source or identifier, money (`getMoney`, `removeMoney`), job and duty changes, offline job changes, usable items, inventory access, notifications and the version check. It sets `KojaLib.Framework`, which the bridge below uses to choose its implementation. The resource also reads koja-lib's inventory functions to give and find items.

## Frameworks

`editable/server/framework.lua` has one implementation per framework and picks it from `KojaLib.Framework`:

| Value | Implementation | Source tables |
|---|---|---|
| `esx` | built in | `users`, `owned_vehicles`, `user_licenses` |
| `qb` | built in | `players`, `player_vehicles`, player metadata for licenses |
| anything else | `custom` stub that returns empty results | none |

Functions every implementation provides:

| Function | Purpose |
|---|---|
| `SearchCitizens(query, limit)`, `GetCitizen(identifier)`, `GetCitizensByIdentifiers(list)` | People search and lookup |
| `SearchVehicles(query, limit)`, `GetVehicle(plate)`, `GetVehiclesByPlates(plates)`, `GetCitizenVehicles(identifier)` | Vehicle lookup |
| `GetLicenses(identifier)`, `RevokeLicense(identifier, type)`, `GrantLicense(identifier, type)` | Licenses |
| `GetRoster(jobs)`, `GetOfflineJob(identifier)` | Employee list and offline job lookup |

Optional extra: define `Bridge.ChargeOffline(identifier, amount, account, reason)` to let citizens pay citations on the web portal while offline.

### Using a custom framework or custom tables

Fill in the `Custom` functions at the bottom of `framework.lua` and make sure koja-lib reports a framework value that is not `esx` or `qb`. Each function must return the same shape the built in versions return. Read the ESX version of the same function in the file and copy its output fields.

Citizens and vehicles are also the tables where your framework stores characters, so changes to column names (for example a custom `users` table) are made in the `Esx` or `Qb` functions in this file.

## Inventory

All item handling goes through `server/inventory.lua`:

- With `koja_grid_inventory` started (and `Config.Inventory.system = 'auto'`), the MDT registers its items there with sizes and images from `Config.Inventory.items`.
- Otherwise it uses the inventory supported by koja-lib: it registers usable items, gives items with metadata and reads metadata. The inventories the startup banner looks for are `koja_grid_inventory`, `ox_inventory`, `qs-inventory`, `qb-inventory`, `ps-inventory` and `codem-inventory`.

You still define the items in your inventory resource, see [Installation](/new/mdt/installation).

## Jail

`Integrations.Jail(target, months, reason, officerSrc, context)` sends a player to jail. `Config.Processing.jailSystem` selects the system: `'auto'` takes the first started resource in this list, or you name one, or use `'none'` (the MDT only records the sentence).

| System | Jail | Release from the MDT | Live state |
|---|---|---|---|
| `koja-police` | `JailPlayer` export | `ReleasePlayer` export | `IsJailed` |
| `pickle_prisons` | `JailPlayer` | `UnjailPlayer` | none |
| `rcore_prison` | `Jail` | `Unjail` | none |
| `qb-prison` | player metadata plus client event `prison:client:Enter` | metadata plus `prison:client:UnjailPerson` | none |
| `xt-prison` | client event `xt-prison:client:enterJail` | no, the MDT fires `koja-mdt:server:releasePlayer` | none |
| `esx_jail` | `esx_jail:sendToJail` | no, the MDT fires `koja-mdt:server:releasePlayer` | none |

If no adapter handles the sentence, the MDT triggers `koja-mdt:server:jailPlayer` with `target, months, reason, officerSrc` so you can listen for it.

### Adding your own jail script

Add an entry to the `jailAdapters` list in `editable/server/integrations.lua`:

```lua title="editable/server/integrations.lua"
{
    resource = 'my_prison',
    jail = function(target, months, reason, officerSrc)
        exports.my_prison:Jail(target, months, reason)
        return true            -- return false to report a failure
    end,
    release = function(target, identifier, reason)
        if not target then return false end
        exports.my_prison:Release(target)
        return true
    end,
    isJailed = function(target)           -- optional
        return exports.my_prison:IsJailed(target) == true
    end,
},
```

With `isJailed`, the MDT checks online inmates every 30 seconds and closes the record when the script no longer holds the player.

Jail scripts can also report their own sentences and releases to the MDT. See the [Server exports](/new/mdt/api/server-exports) and [Events](/new/mdt/api/events) pages (`ReportJail`, `ReportRelease`, `ReportImpound`, `ReportImpoundRelease`, `GetActiveSentence`, `IsImpounded`).

## Impound

`Config.Custody.impoundSystem` selects the adapter: `'auto'`, `'koja-police'`, `'garage'` or `'none'`.

- koja-police: `ImpoundVehicle(plate, fee, hours, reason)`. Release calls `ReleaseImpound(plate, waived)` when present, otherwise it ends the hold in `koja_police_impound`.
- garage: ESX `owned_vehicles.stored` (2 is impounded, 1 is in the garage) or QB `player_vehicles.state` (2 impounded, 1 garage) with `depotprice`. This is the fallback used with your normal garage script as long as it reads those columns.

The MDT also sets the vehicle's `impounded` flag and fires `koja-mdt:server:impoundVehicle` and `koja-mdt:server:releaseImpound`. To use another garage or impound script, add an entry to `impoundAdapters` with `impound(plate, data)` and `release(plate, data)` functions.

## Fines and society payout

`Integrations.Fine(target, amount, reason, officerSrc)` takes the fine from `Config.Processing.fineAccount` (bank), falls back to cash if the balance is too low, verifies the balance changed, and then calls `Integrations.PaySociety(amount)`. Citations and court sentences use the same function.

`Integrations.PaySociety` pays the account in `Config.Processing.fineSociety` through the first of these that is started: `esx_addonaccount`, `Renewed-Banking`, `qb-banking`, `qb-management`. To use another banking resource, edit that function. Medical bills use `Integrations.ChargeMedicalBill` and `Integrations.PayMedicalSociety` the same way.

## Sentences

`Integrations.ApplySentence(target, sentence, judgeSrc)` applies a court sentence: fine first, then jail. It fires `koja-mdt:server:sentenced`. Edit it if you need extra steps such as clearing a wanted level.

## Phone

`ClientIntegrations.GetPhoneNumber()` in `editable/client/integrations.lua` returns `nil` by default. Return the player's phone number from your phone resource and it is attached to 911 calls:

```lua title="editable/client/integrations.lua"
function ClientIntegrations.GetPhoneNumber()
    return exports['my_phone']:GetOwnNumber()
end
```

## Opening rules and notifications

In the same client file:

| Function | Default |
|---|---|
| `ClientIntegrations.IsDead()` | Uses koja-lib, falls back to the native death check. |
| `ClientIntegrations.CanOpen()` | Blocks the pause menu, dead players (unless `Config.Open.allowWhenDead`) and non-emergency vehicles when `Config.Open.vehicleOnly` is on. |
| `ClientIntegrations.Notify(title, desc, type)` | Uses the koja-lib notification. Replace it to use your own notify resource. |

## Dispatch

Dispatch is built into the MDT. Other scripts create calls with the `CreateDispatch` server export, see [Server exports](/new/mdt/api/server-exports). With koja-police started, `Config.Police` controls the link: `dispatch` lets koja-police use the MDT dispatch and `duty` shares duty state. With koja-ambulance, see `Config.Ems.ambulance` in [Configuration](/new/mdt/configuration).

## Radial menus and targets

The interaction menu detects these resources automatically (`Config.Interact.radial` and `Config.Interact.target`):

| Type | Resources |
|---|---|
| Radial menu | `koja-police`, `ox_lib`, `qb-radialmenu` |
| Target | `ox_target`, `koja-interactions`, `qb-target` |

With koja-police and `Config.Interact.replacePoliceItems`, koja-police's own ID, plate, bill and MDT radial items are replaced by the MDT ones.

## Screenshots

`screenshot-basic` is optional. It is used for the Take a photo button (needs `Config.Camera.uploadUrl`) and for selfies. The profile pictures from `Config.Avatars` use the game's own head shot and do not need it.

## Events and exports

The MDT fires server events you can listen to (for example `koja-mdt:server:processed`, `koja-mdt:server:warrantActive`, `koja-mdt:server:sentenced`, `koja-mdt:server:custody`). The full list, with arguments, is in [Events](/new/mdt/api/events). Hooks that can modify or cancel actions are in [Hooks](/new/mdt/api/hooks). Exports are in [Server exports](/new/mdt/api/server-exports) and [Client exports](/new/mdt/api/client-exports). Start with the [API overview](/new/mdt/api/overview).
