> For the complete documentation index, see [llms.txt](https://docs.kuzquality.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kuzquality.com/resources/premium-resources/drug-dealing/api/server.md).

# Server

Server exports for kq\_drugdeals. Call them from any resource.

### Organisations

#### IsOrganisationUnlocked

Returns whether the player has the organisation unlocked (visible on the map and in the app). A `public` organisation is always visible without an unlock, and `hidden` / `public` organisations are tradeable regardless. This reads the per-player unlock only

```lua
-- Example
exports.kq_drugdeals:IsOrganisationUnlocked(
    player, -- integer - Server id
    orgId, -- string - Organisation id
)

-- Returns: boolean - Whether the organisation is unlocked for this character
```

#### UnlockOrganisation

Unlocks the organisation for this player's character. Persisted, the blip and app contact appear. This is the intended way to open a `Closed` organisation from your own quest or job script

```lua
-- Example
exports.kq_drugdeals:UnlockOrganisation(
    player, -- integer - Server id
    orgId, -- string - Organisation id
)

-- Returns: boolean - Success (true when unlocked, or already was)
```

#### LockOrganisation

Re-locks the organisation for this player's character. Standing survives, only the unlock is removed

```lua
-- Example
exports.kq_drugdeals:LockOrganisation(
    player, -- integer - Server id
    orgId, -- string - Organisation id
)

-- Returns: boolean - Success
```

#### GetOrganisationIds

Returns the ids of all active organisations

```lua
-- Example
exports.kq_drugdeals:GetOrganisationIds()

-- Returns: table - { 'org_a1b2c3', 'org_d4e5f6', ... }
```

#### GetAllOrganisations

Returns all active organisations as deep copies, safe to keep or mutate

```lua
-- Example
exports.kq_drugdeals:GetAllOrganisations()

-- Returns:
-- {
--     {
--         id = 'org_a1b2c3',
--         name = 'The Lost MC',
--         coords = {x = 0.0, y = 0.0, z = 0.0, h = 90.0}, -- Sell point picked for this server session
--         unlockMode = 'buyin', -- 'public' | 'hidden' | 'buyin' | 'closed'
--         description = 'Bikers out east',
--         enemies = {'org_d4e5f6'},
--         buyPrices = {{drugId = 'drug_a1b2c3', min = 0.8, mid = 1.0, max = 1.2, minQuantity = 10}},
--     },
--     ...
-- }
```

### Organisation standing

Standing is 0-100 per character per organisation, priced in five levels (Bad / Poor / Neutral / Good / Great).

#### GetOrganisationStanding

Returns the raw standing value

```lua
-- Example
exports.kq_drugdeals:GetOrganisationStanding(
    player, -- integer - Server id
    orgId, -- string - Organisation id
)

-- Returns: number - Standing 0-100, or nil on an unknown id
```

#### GetOrganisationStandingLevel

Returns the standing level. This is what prices organisation sales

```lua
-- Example
exports.kq_drugdeals:GetOrganisationStandingLevel(
    player, -- integer - Server id
    orgId, -- string - Organisation id
)

-- Returns: number - Level 1-5, or nil on an unknown id
```

#### AddOrganisationStanding

Applies a standing change immediately (clamped 0-100) and pushes the new value to the player's app. Use for favours, quests, punishments

```lua
-- Example
exports.kq_drugdeals:AddOrganisationStanding(
    player, -- integer - Server id
    orgId, -- string - Organisation id
    delta, -- number - Change to apply, negative to lower
)

-- Returns: boolean - Success
```

#### SetOrganisationStanding

Sets standing to an absolute value (clamped 0-100) and pushes it to the player's app

```lua
-- Example
exports.kq_drugdeals:SetOrganisationStanding(
    player, -- integer - Server id
    orgId, -- string - Organisation id
    value, -- number - Target standing 0-100
)

-- Returns: boolean - Success
```

### Player rating

The app star rating (1.0-5.0) that gates buyer quality.

#### GetPlayerRating

Returns the player's app rating

```lua
-- Example
exports.kq_drugdeals:GetPlayerRating(
    player, -- integer - Server id
)

-- Returns: table - { stars = 3.8, dealsCompleted = 24 }
```

#### AddPlayerRating

Moves the star rating by a delta (clamped 1.0-5.0). Does not count a deal

```lua
-- Example
exports.kq_drugdeals:AddPlayerRating(
    player, -- integer - Server id
    delta, -- number - Change to apply, negative to lower
)

-- Returns: boolean - Success
```

### Market

#### GetMarketPrice

Returns the current drifted headline price of a drug (its best grade)

```lua
-- Example
exports.kq_drugdeals:GetMarketPrice(
    drugId, -- string - Drug id
)

-- Returns: number - Current price, or nil on an unknown id
```

#### GetMarketDrift

Returns the current drift of a drug as a fraction

```lua
-- Example
exports.kq_drugdeals:GetMarketDrift(
    drugId, -- string - Drug id
)

-- Returns: number - Drift fraction (0.05 = 5% above base)
```

#### GetAllMarketPrices

Returns every active drug's price and drift in one call

```lua
-- Example
exports.kq_drugdeals:GetAllMarketPrices()

-- Returns:
-- {
--     drug_a1b2c3 = {price = 120, drift = 0.05}, -- price is the drifted headline (best grade)
--     drug_d4e5f6 = {price = 850, drift = -0.02},
-- }
```

#### GetDrugIds

Returns the ids of all active drugs

```lua
-- Example
exports.kq_drugdeals:GetDrugIds()

-- Returns: table - { 'drug_a1b2c3', 'drug_d4e5f6', ... }
```

#### GetDrugInfo

Returns metadata for one active drug, as a deep copy

```lua
-- Example
exports.kq_drugdeals:GetDrugInfo(
    drugId, -- string - Drug id
)

-- Returns:
-- {
--     id = 'drug_a1b2c3',
--     item = 'weed_packaged', -- Inventory item name
--     label = 'Weed',
--     street = true, -- Enabled sell channels
--     app = true,
--     useMetaPricing = true,
--     metaKey = 'quality',
--     metaSteps = {...}, -- Grade list, empty when the drug isn't metadata priced
--     appMinRating = 0,
--     streetQuantity = {min = 1, max = 3},
--     appQuantity = {min = 5, max = 20},
--     price = 120, -- Current headline market
--     drift = 0.05,
-- }
```

### App

#### ForceBuyerRequest

Forces a buyer inquiry to the player's phone, same as the `drugdeals:forcemsg` admin command. Fails when the player has no advertised stock and no app-enabled drug exists

```lua
-- Example
exports.kq_drugdeals:ForceBuyerRequest(
    player, -- integer - Server id
)

-- Returns: boolean - Success
```

### Saved contacts

Repeat customers saved in the player's app (Contacts tab). Per character, stored in `kq_drugdeals_contacts`. Standing uses the same 0-100 / five-level model as organisations. Statuses: `active`, `blocked` (standing hit 0), `deleted` (profile expired).

#### GetPlayerContacts

Returns every saved contact as deep copies, dead ones included

```lua
-- Example
exports.kq_drugdeals:GetPlayerContacts(
    player, -- integer - Server id
)

-- Returns:
-- {
--     {
--         id = 'ct_a1b2c3',
--         name = 'Fat Tony',
--         status = 'active', -- 'active' | 'blocked' | 'deleted'
--         standing = 46,
--         standingLevel = 3,
--         deals = 4, -- Completed deals with this contact
--         persona = 'rich',
--         deliveryMethod = 'npc', -- 'npc' | 'deaddrop'
--         pedModel = 'a_m_m_hillbilly_01', -- Their guy at npc handovers
--         neverBuys = false,
--         drugs = {'drug_a1b2c3'}, -- Preferred drugs
--     },
--     ...
-- }
```

#### GetContactSlots

Returns the active contact count against the rating-based cap (`Config.contacts.maxByRating`)

```lua
-- Example
exports.kq_drugdeals:GetContactSlots(
    player, -- integer - Server id
)

-- Returns: table - { active = 2, max = 4 }
```

#### AddPlayerContact

Creates an active contact and pushes it to the player's app. Ignores the slot cap, the caller decides. Every field of `data` is optional and rolled like a natural add when unset

```lua
-- Example
exports.kq_drugdeals:AddPlayerContact(
    player, -- integer - Server id
    data, -- table - Optional contact fields, see below
)

-- data:
-- {
--     name = 'Fat Tony',
--     persona = 'rich', -- 'broke' | 'cheap' | 'rushed' | 'regular' | 'rich' (drives budget and haggle patience)
--     deliveryMethod = 'npc', -- 'npc' | 'deaddrop'
--     drugs = {'drug_a1b2c3'}, -- Preferred drugs
--     standing = 40, -- 0-100
--     neverBuys = false,
--     pedModel = 'a_m_m_hillbilly_01',
--     home = {x = 215.7, y = -810.1}, -- Deliveries meet at the nearest free spot to this, random when unset
-- }

-- Returns: string - Contact id (ct_...), or false when no valid drug exists
```

#### RemovePlayerContact

Deletes the contact outright, whatever its status. Players can do the same from the contact's profile page

```lua
-- Example
exports.kq_drugdeals:RemovePlayerContact(
    player, -- integer - Server id
    contactId, -- string - Contact id
)

-- Returns: boolean - Success
```

#### GetContactStanding

Returns the raw standing value of a contact

```lua
-- Example
exports.kq_drugdeals:GetContactStanding(
    player, -- integer - Server id
    contactId, -- string - Contact id
)

-- Returns: number - Standing 0-100, or nil on an unknown id
```

#### AddContactStanding

Applies a standing change immediately (clamped 0-100) on an active contact. Dropping it to 0 blocks the contact exactly like a natural fallout, message included

```lua
-- Example
exports.kq_drugdeals:AddContactStanding(
    player, -- integer - Server id
    contactId, -- string - Contact id
    delta, -- number - Change to apply, negative to lower
)

-- Returns: boolean - Success
```

#### ForceContactRequest

Makes a contact message the player with a buy request now, skipping the message interval, decline snooze and police gate. Fails without a free delivery spot or thread slot

```lua
-- Example
exports.kq_drugdeals:ForceContactRequest(
    player, -- integer - Server id
    contactId, -- string - Optional, picks a random active contact when nil
)

-- Returns: boolean - Success
```

#### ForceContactReferral

Spawns a referred stranger vouched for by a random active contact, skipping the standing gate and the hourly roll. Still needs a free contact slot, a phone item and a free delivery spot

```lua
-- Example
exports.kq_drugdeals:ForceContactReferral(
    player, -- integer - Server id
)

-- Returns: boolean - Success
```

#### ExpireContact

Ends a contact's lifetime now. The profile flips to "Profile Deleted" and the farewell message is sent

```lua
-- Example
exports.kq_drugdeals:ExpireContact(
    player, -- integer - Server id
    contactId, -- string - Optional, picks a random active contact when nil
)

-- Returns: boolean - Success
```

### Debug commands

ACE-gated in game (`command.drugdeals:admin`), or from the server console with the player id as first argument:

* `drugdeals:forcemsg [playerId]` - force a random buyer inquiry
* `drugdeals:givecontact [playerId]` - add a rolled contact (skips the cap)
* `drugdeals:forcecontactmsg [playerId]` - force a contact buy request
* `drugdeals:forcereferral [playerId]` - force a referral stranger
* `drugdeals:expirecontact [playerId]` - expire a random active contact
* `drugdeals:contactstanding [playerId] <delta>` - move a random active contact's standing (e.g. `-45` to test blocking)

### Owner hooks

Beyond exports, `server/editable/editable.lua` carries the `Dummy.*` hooks. Edit that file directly to react to events without another resource
