API Reference

Server callbacks, pricing behavior, and integration expectations for Oxide Drugselling.

Technical reference for the current oxide-drugselling integration points.

Public Resource Exports

oxide-drugselling does not currently register public FiveM exports of its own.

This document covers:

  • the sale hook (event + config function) fired on every completed sale
  • the internal pricing helper used by the resource
  • the server callbacks used between client and server
  • the external exports expected from optional source drug resources
  • the NUI actions used by the packaged frontend

Sale Hook

Both fire on the server once per completed sale, covering all acceptance paths (instant accept, negotiated accept, and accepted counter-offer). Use either or both. They run after the item is removed and the player is paid.

Each receives the same saleData table:

FieldTypeDescription
sourcenumberSeller's server id
charIdstringSeller's character identifier
drugTypestringConfig.Drugs key (e.g. weed)
itemNamestringItem that was sold
quantitynumberUnits sold
finalPricenumberTotal amount paid
metaKeystringMetadata group key for the sold stack
modestringConfig.DirtyMoney.mode used for payout

Event: oxide-drugselling:drugSold

A plain server-side event (not net), so clients cannot trigger it. Use this when you prefer a listener in your own resource.

AddEventHandler('oxide-drugselling:drugSold', function(src, saleData)
    -- saleData.finalPrice, saleData.drugType, ...
end)

Config function: Config.OnDrugSold

A function in shared/config.lua (escrow-ignored, so editable) that runs on each sale. It is wrapped in pcall, so an error in your code is logged and the sale still completes. Leave the body empty to do nothing.

Config.OnDrugSold = function(saleData)
    local turfId = exports['op-crime']:getPlayerTurfZone(saleData.source)
    if turfId then
        TriggerEvent('op-crime:drugSold', saleData.source, turfId, saleData.finalPrice)
    end
end

Internal Pricing Helper

PricingPipeline.Calculate

Calculates fair price per unit and bonus-detail data for an item group.

local fairPricePerUnit, bonusDetails = PricingPipeline.Calculate(drugCfg, itemCfg, metadata)
ParameterTypeDescription
drugCfgtableEntry from Config.Drugs[drugKey]
itemCfgtableItem definition with label and basePrice
metadatatable or nilItem metadata used by modifier resolution

Returns:

ValueTypeDescription
fairPricePerUnitnumberFloored fair value per unit
bonusDetailstableUI-facing bonus/tag data

Server Callbacks

These callbacks are registered through Callback = olink.callback.

oxide-drugselling:server:getSellableItems

Returns sellable inventory entries grouped by item and metadata.

local items = Callback.Trigger('oxide-drugselling:server:getSellableItems')

Current behavior:

  • enforces the configured police minimum
  • reads player inventory through o-link.inventory
  • applies progression-level checks per drug type
  • calculates fair price per group before the panel opens

Rate limit: 3000 ms

Response shape:

{
    {
        drugType = 'weed',
        itemName = 'weed_1g',
        label = 'Weed (1g)',
        basePrice = 25,
        groups = {
            {
                metaKey = 'strain123:Gas',
                label = 'OG Kush',
                grade = 'Gas',
                gradeColor = 'text-purple-400',
                score = 91,
                bonuses = { ... },
                available = 3,
                fairPricePerUnit = 52,
            },
        },
    },
}

oxide-drugselling:server:initSale

Validates the selected group, calculates the fair total price, rolls risk outcomes, and either processes the sale or returns a negotiation result.

local result = Callback.Trigger('oxide-drugselling:server:initSale', {
    drugType = 'weed',
    itemName = 'weed_1g',
    quantity = 5,
    metaKey = 'strain123:Gas',
    playerPrice = 200,
    npcModel = 1234567890,
})

Rate limit: 5000 ms

Possible result values:

  • accept
  • counter
  • reject
  • robbery
  • error

Possible return fields:

FieldMeaning
resultOutcome type
finalPricePaid amount on acceptance
counterPriceOffer returned by the NPC
itemsLostQuantity stolen during robbery
cashLostDirty-money amount stolen during robbery
messageError reason such as missing_items
policeDispatchedWhether the dispatch roll succeeded

oxide-drugselling:server:acceptCounter

Accepts the player's currently pending counter-offer.

local result = Callback.Trigger('oxide-drugselling:server:acceptCounter')

Rate limit: 5000 ms

Notes:

  • pending counters expire after 60 seconds
  • accepted counters process item removal, payout, and progression just like a direct acceptance

Optional Source-Resource Exports

oxide-drugselling can call these exports on source drug resources when a drug type defines a progression block or a trait_bonus modifier.

GetPlayerLevel

Expected signature:

exports('GetPlayerLevel', function(source)
    return playerLevel
end)

Used for:

  • per-drug selling level requirements

AddXP

Expected signature:

exports('AddXP', function(source, amount)
end)

Used for:

  • awarding (xpPerUnit * quantity) after a successful sale

AddSold

Expected signature:

exports('AddSold', function(source, quantity)
end)

Used for:

  • tracking sold units after a successful sale

GetStrainData

Expected signature for the shipped weed config:

exports('GetStrainData', function(strainId)
    return {
        traits = {
            thc = 85,
            potency = 72,
        },
    }
end)

Used for:

  • resolving trait_bonus data from weed strain metadata

If a source resource is not started, the call is skipped and selling continues.

NUI Actions

Lua to NUI

ActionPayloadPurpose
openSellPanel{ items, isGangNpc, panelTitle }Opens the sell panel
sellThinkingnoneSwitches to the pending state
sellResult{ result, finalPrice?, counterPrice?, itemsLost?, cashLost?, policeDispatched? }Displays the final outcome
closenoneCloses the panel

NUI to Lua

CallbackPayloadPurpose
sellConfirm{ drugType, itemName, quantity, metaKey, playerPrice, isGangNpc }Submit a player offer
sellAcceptCounternoneAccept a counter-offer
sellClosenoneClose the sale
closenoneGeneric close action
getLocalenoneReturns { locale } from the ox:locale convar for the frontend locale loader

Integration Notes

If you are adding a custom drug-production resource and want it to work with oxide-drugselling:

  1. register your item names in the inventory used by o-link
  2. attach metadata fields that match your Config.Drugs definitions
  3. add optional progression exports if you want level gating and XP/sold tracking
  4. add the new drug entry in shared/config/drugs.lua

Next Steps