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

# Custom frameworks

> The bridge files are intentionally left outside the escrow encrypted set — escrowignore in fxmanifest.lua covers bridge/*.lua, so you can plug in NDCore, ox_core, or a fully custom framework…

The bridge files are intentionally left **outside** the escrow encrypted set — `escrow_ignore` in `fxmanifest.lua` covers `bridge/*.lua`, so you can plug in `ND_Core`, `ox_core`, or a fully custom framework without source access to the rest of the resource.

## Bridge API

`bridge/server.lua` is the single integration surface. To add a new framework, add a branch alongside the existing `FW_QBX` / `FW_QB` / `FW_ESX` checks. Mirror any event subscriptions on the client side in `bridge/client.lua`.

The rest of the resource calls only these functions:

### Player & identity

```
Bridge.GetPlayer(source)         -> opaque player object
Bridge.GetIdentifier(player)     -> string (license/citizenid/etc.)
Bridge.GetName(player)           -> "First Last"
```

### Job & grade

```
Bridge.GetPlayerJob(source)      -> { name, label, grade, grade_name, isboss }
Bridge.GetAllJobs()              -> { [jobName] = jobObject, ... }
Bridge.GetJobLabel(jobName)      -> string
Bridge.JobExists(jobName)        -> boolean
Bridge.GetJobGrades(jobName)     -> { [grade] = { name, label, salary }, ... }
Bridge.IsBoss(source, jobName)   -> boolean
Bridge.SetJob(source, jobName, grade)
Bridge.RemoveFromJob(source, jobName)        -- used for fire (clean drop)
Bridge.SetJobOffline(identifier, jobName, grade)
Bridge.GetEmployees(jobName)     -> { { id, name, grade, isboss }, ... }
```

`GetPlayerJob` is the most important one — return shape must match exactly. The resource treats `isboss = true` as overriding `Config.BossGradeNames`, so set it correctly for grades that are bosses.

### Money

```
Bridge.GetPlayerMoney(source, account)             -> number
Bridge.AddPlayerMoney(source, account, amount)     -> boolean
Bridge.RemovePlayerMoney(source, account, amount)  -> boolean
```

`account` is `'cash'` or `'bank'`. Return `false` on insufficient funds — the boss menu uses this to gate Deposit.

### Society

```
Bridge.GetSocietyBalance(jobName)         -> number
Bridge.AddSociety(jobName, amount)        -> boolean
Bridge.RemoveSociety(jobName, amount)     -> boolean
```

Make sure both `AddSociety` and `RemoveSociety` return `false` on failure — the boss menu uses the return value to drive the **refund-on-failure** behaviour. Returning `true` after a silent failure means money disappears.

### Admin & UX

```
Bridge.IsAdmin(source)                    -> boolean
Bridge.Notify(source, msg, kind, title)   -- 'success' | 'error' | 'inform'
```

`IsAdmin` is consulted alongside `Config.Admin.acePermissions` — return `true` from either path grants admin.

## Client side

`bridge/client.lua` mirrors the framework-specific event subscriptions the resource needs — typically the "player loaded" and "job changed" events. Add your branch and emit the same internal events used by the existing branches.

The most important client-side hook is publishing a refreshed `PlayerData` whenever the player's job changes, so the marker / blip filtering updates without waiting for the next `Config.JobPollMs` tick.

## Testing your branch

1. Set `Config.Framework = 'auto'` (or pin to your new value if you've added one to the framework switch).

2. Set `Config.SocietyBackend` accordingly.

3. Start your framework, then `nex_bossmenu`. The boot line should print:

   ```
   [nex_bossmenu] Framework: <your-key>
   ```

4. Run `/bossmenu_admin` as an admin, place a marker for a job that exists in your framework.

5. Walk to it as a boss of that job — the prompt should appear and the menu should open.

6. Try every action: balance read, withdraw, deposit, hire (with a candidate nearby), promote, demote, fire. Each one exercises a different bridge function — any missing return path will surface as a `Society account write failed` or `setJob did not stick` error.

Failed mutations log in **red** with the failing step, so you can map errors back to bridge functions immediately.
