> 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/server-exports.md).

# Server Exports

Server-side exports available on viper-multijob.

All server exports are called on viper-multijob and take the player's server id as the first argument.

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

Most of these query the database synchronously, so call them from inside a thread or an event handler — not at resource start.

## 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="#getidentifier">GetIdentifier</a></td><td>The player's cid / citizenid / identifier</td></tr><tr><td><a href="#getjobs">GetJobs</a></td><td>Every job the player owns</td></tr><tr><td><a href="#getjobcount">GetJobCount</a></td><td>How many jobs the player owns</td></tr><tr><td><a href="#getjobgrade">GetJobGrade</a></td><td>The grade a player owns a job at</td></tr><tr><td><a href="#hasjob">HasJob</a></td><td>Check job ownership</td></tr><tr><td><a href="#addjob">AddJob</a></td><td>Add a job to the player</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</td></tr><tr><td><a href="#clearjobs">ClearJobs</a></td><td>Remove every job from the player</td></tr><tr><td><a href="#isonduty">IsOnDuty</a></td><td>The player's current duty state</td></tr><tr><td><a href="#setduty">SetDuty</a></td><td>Set the player's duty state</td></tr></tbody></table>

***

## Misc

{% hint style="success" %}

#### GetFramework

Same as the client export, available server-side.

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

{% endhint %}

{% hint style="success" %}

#### GetIdentifier

Returns the value stored in the cid column for this player — the citizenid on QBCore, the identifier on ESX.

```lua
---@param src number
---@return string?
local cid = exports["viper-multijob"]:GetIdentifier(src)
```

{% endhint %}

## Jobs

{% hint style="success" %}

#### GetJobs

Returns every job the player owns.

```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 src number
---@return JobEntry[]
local jobs = exports["viper-multijob"]:GetJobs(src)
```

Returns an empty table if the player is not online.
{% endhint %}

{% hint style="success" %}

#### GetJobCount

```lua
---@param src number
---@return number
local count = exports["viper-multijob"]:GetJobCount(src)
```

{% endhint %}

{% hint style="success" %}

#### GetJobGrade

Returns the grade the player owns a job at, or nil if they do not own it.

```lua
---@param src number
---@param job string
---@return number?
local grade = exports["viper-multijob"]:GetJobGrade(src, "police")
```

{% endhint %}

{% hint style="success" %}

#### HasJob

Checks job ownership. When grade is passed, the grade must match as well.

```lua
---@param src number
---@param job string
---@param grade? number
---@return boolean
local hasJob = exports["viper-multijob"]:HasJob(src, "police")
local isSergeant = exports["viper-multijob"]:HasJob(src, "police", 3)
```

{% endhint %}

{% hint style="success" %}

#### AddJob

Adds a job to the player's list, or updates the grade if they already own it. Does not switch them to the job — use SetJob for that.

```lua
---@param src number
---@param job string
---@param grade? number # Defaults to 0
---@return boolean success
---@return string? reason
local success, reason = exports["viper-multijob"]:AddJob(src, "mechanic", 0)
```

**Failure reasons**

| Reason             | Meaning                                            |
| ------------------ | -------------------------------------------------- |
| player\_not\_found | The player is not online                           |
| invalid\_job       | job is not a string                                |
| job\_not\_found    | The job does not exist in the framework's job list |
| job\_blacklisted   | The job is listed in Config.BlackListedJobs        |
| already\_owned     | The player already owns the job at that grade      |
| {% endhint %}      |                                                    |

{% hint style="success" %}

#### SetJob

Switches the player to a job they already own, and sets their duty state. Fails if they do not own the job.

```lua
---@param src number
---@param job string
---@param grade? number # Defaults to the grade stored in the database
---@return boolean success
---@return string? reason
local success, reason = exports["viper-multijob"]:SetJob(src, "police", 2)
```

**Failure reasons**

| Reason             | Meaning                                            |
| ------------------ | -------------------------------------------------- |
| player\_not\_found | The player is not online                           |
| invalid\_job       | job is not a string                                |
| job\_not\_found    | The job does not exist in the framework's job list |
| not\_owned         | The player does not own this job                   |
| grade\_mismatch    | The grade passed does not match the stored grade   |

{% hint style="info" %}
Unlike the `multijob:server:changeJob` net event, this export never drops the player — it returns false with a reason instead. Prefer it for server-side integrations.
{% endhint %}
{% endhint %}

{% hint style="success" %}

#### RemoveJob

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

```lua
---@param src number
---@param job string
---@return boolean success
---@return string? reason
local success, reason = exports["viper-multijob"]:RemoveJob(src, "trucker")
```

Failure reasons: player\_not\_found, invalid\_job, not\_owned.
{% endhint %}

{% hint style="success" %}

#### ClearJobs

Removes every job from the player's list and sets them to unemployed.

```lua
---@param src number
---@return boolean success
---@return string? reason
local success, reason = exports["viper-multijob"]:ClearJobs(src)
```

{% endhint %}

## Duty

{% hint style="success" %}

#### IsOnDuty

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

{% endhint %}

{% hint style="success" %}

#### SetDuty

Sets the player's duty state.

```lua
---@param src number
---@param state boolean
---@return boolean success
---@return string? reason
local success = exports["viper-multijob"]:SetDuty(src, true)
```

On QBCore this calls SetJobDuty and syncs the client. On ESX it swaps between police and offpolice when the off-job exists in the jobs table, and otherwise falls back to an internal duty flag.
{% endhint %}

***

### Examples

#### Give a job from your own resource

```lua
RegisterNetEvent("myscript:server:hire", function(job)
    local src = source
    local success, reason = exports["viper-multijob"]:AddJob(src, job, 0)

    if not success then
        return print("Could not hire player: " .. reason)
    end

    exports["viper-multijob"]:SetJob(src, job, 0)
end)
```

#### Gate a shop behind job ownership

```lua
RegisterNetEvent("myscript:server:openPoliceArmory", function()
    local src = source

    if not exports["viper-multijob"]:HasJob(src, "police") then
        return
    end

    if not exports["viper-multijob"]:IsOnDuty(src) then
        return
    end

    -- open armory
end)
```

#### Limit how many jobs a player can hold

```lua
local MAX_JOBS = 3

AddEventHandler("viper-multijob:server:jobAdded", function(src, job)
    if exports["viper-multijob"]:GetJobCount(src) > MAX_JOBS then
        exports["viper-multijob"]:RemoveJob(src, job)
    end
end)
```
