> For the complete documentation index, see [llms.txt](https://viper-development-1.gitbook.io/viperdocs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://viper-development-1.gitbook.io/viperdocs/viper-scripts/multi-job/developer-api/client-exports.md).

# Client Exports

Client-side exports available on viper-multijob.

All client exports are called on viper-multijob.

```lua
exports["viper-multijob"]:ExportName(...)
```

## Quick reference

<table data-search="false"><thead><tr><th>Export</th><th>Description</th></tr></thead><tbody><tr><td><a href="#getframework">GetFramework</a></td><td>Framework resolved at startup</td></tr><tr><td><a href="#ismenuopen">IsMenuOpen</a></td><td>Whether the job menu is currently open</td></tr><tr><td><a href="#openmenu">OpenMenu</a></td><td>Open the job menu</td></tr><tr><td><a href="#closemenu">CloseMenu</a></td><td>Close the job menu</td></tr><tr><td><a href="#getjob">GetJob</a></td><td>The player's current job</td></tr><tr><td><a href="#getjobs">GetJobs</a></td><td>Every job the player owns</td></tr><tr><td><a href="#hasjob">HasJob</a></td><td>Check whether the player owns a job</td></tr><tr><td><a href="#setjob">SetJob</a></td><td>Switch to a job the player already owns</td></tr><tr><td><a href="#removejob">RemoveJob</a></td><td>Remove a job from the player permanently</td></tr><tr><td><a href="#savecurrentjob">SaveCurrentJob</a></td><td>Save the current job into the multijob list</td></tr><tr><td><a href="#isonduty">IsOnDuty</a></td><td>The player's current duty state</td></tr><tr><td><a href="#toggleduty">ToggleDuty</a></td><td>Toggle the player's duty state</td></tr></tbody></table>

***

## Misc

{% hint style="success" %}

#### GetFramework

Returns the framework viper-multijob resolved at startup, after Config.Framework auto-detection.

```lua
---@return "qbcore" | "esx"
local framework = exports["viper-multijob"]:GetFramework()
```

{% endhint %}

{% hint style="success" %}

#### IsMenuOpen

```lua
---@return boolean
local isOpen = exports["viper-multijob"]:IsMenuOpen()
```

{% endhint %}

## Handle menu

{% hint style="success" %}

#### OpenMenu

Opens the job menu. Returns false if the menu is already open, or if the player has no job data yet.

```lua
---@return boolean success
local success = exports["viper-multijob"]:OpenMenu()
```

{% endhint %}

{% hint style="success" %}

#### CloseMenu

Closes the job menu and releases NUI focus. Returns false if the menu was not open.

```lua
---@return boolean success
local success = exports["viper-multijob"]:CloseMenu()
```

{% endhint %}

## Jobs

{% hint style="success" %}

#### GetJob

Returns the player's current job, read from the framework's player data.

```lua
---@class CurrentJob
---@field name string # Job name, e.g. "police"
---@field grade number # Grade level
---@field onduty boolean # Duty state

---@return CurrentJob?
local job = exports["viper-multijob"]:GetJob()
```

{% endhint %}

{% hint style="success" %}

#### GetJobs

Returns every job the player owns. Asynchronous — the result is passed to the callback.

```lua
---@class JobEntry
---@field job string # Job name
---@field grade number # Grade level
---@field jobLabel string # Display label of the job
---@field gradeLabel string # Display label of the grade
---@field salary number # Grade payment/salary
---@field icon string # Icon class from Config.Icons, or Config.DefaultIcon

---@param cb fun(jobs: JobEntry[])
---@return boolean # False if cb is not a function
exports["viper-multijob"]:GetJobs(function(jobs)
    for _, v in ipairs(jobs) do
        print(v.job, v.grade, v.jobLabel, v.salary)
    end
end)
```

{% endhint %}

{% hint style="success" %}

#### HasJob

Checks whether the player owns a job. The grade is passed as a second callback argument when owned.

```lua
---@param job string
---@param cb fun(hasJob: boolean, grade?: number)
---@return boolean # False if the arguments are invalid
exports["viper-multijob"]:HasJob("police", function(hasJob, grade)
    if hasJob then
        print("Owns police at grade " .. grade)
    end
end)
```

{% endhint %}

{% hint style="success" %}

#### SetJob

Switches the player to a job they already own. The server validates ownership and grade.

```lua
---@param job string
---@param grade number
---@return boolean # False if the arguments are invalid
local sent = exports["viper-multijob"]:SetJob("police", 2)
```

{% hint style="warning" %}
The server drops the player if the job or grade does not match the `player_jobs` table. Always read the grade from `GetJobs` or `HasJob` first — never guess it.
{% endhint %}
{% endhint %}

{% hint style="success" %}

#### RemoveJob

Removes a job from the player's list permanently. If it is their current job, they are set to unemployed.

```lua
---@param job string
---@return boolean # False if the argument is invalid
local sent = exports["viper-multijob"]:RemoveJob("trucker")
```

{% endhint %}

{% hint style="success" %}

#### SaveCurrentJob

Saves the player's current job into their multijob list. Use this only if your resource writes jobs directly to the database — jobs set through the framework's normal functions are picked up automatically.

```lua
---@return boolean
exports["viper-multijob"]:SaveCurrentJob()
```

{% endhint %}

## Duty

{% hint style="success" %}

#### IsOnDuty

```lua
---@return boolean
local onDuty = exports["viper-multijob"]:IsOnDuty()
```

On QBCore this reads `PlayerData.job.onduty`. On ESX it returns false when the job name starts with `off` (esx\_duty style), otherwise it returns the duty flag synced from the server.
{% endhint %}

{% hint style="success" %}

#### ToggleDuty

Toggles the player's duty state.

```lua
---@return boolean success
local success = exports["viper-multijob"]:ToggleDuty()
```

Returns false when:

* The job is listed in `Config.StationDutyJobs` (QBCore) — those jobs must use their station duty system
* `Config.ESXDuty` is false (ESX)
  {% endhint %}
