> For the complete documentation index, see [llms.txt](https://docs.rainmad.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.rainmad.com/resources/blackmarkets-player-run-contraband-operation/configuration.md).

# Configuration

Everything is configured in **`cfg.lua`**. After editing, restart the resource (`ensure rm_blackmarkets`).

## Locale & bridge

```lua
cfg.locale = 'en' -- locale file in /locales (en.json by default)

cfg.bridge = {
    framework    = 'auto',     -- 'auto' | 'qb' | 'esx'
    target       = 'auto',     -- 'auto' | 'ox_target' | 'qb-target' | false
    notification = 'builtin',  -- builtin | ox_lib | qb | esx | okokNotify | ps-ui | lation_ui | wasabi_notify | zsxui
    textUI       = 'builtin',  -- builtin | ox_lib | esx | qb | okokTextUI | jg-textui
    progressbar  = 'builtin',  -- builtin | ox_lib | qb | esx | wasabi_uikit | lation_ui | zsxui | false
    vehiclelock  = 'auto',     -- auto | false | qb-vehiclekeys | wasabi_carlock | qs-vehiclekeys | ... | jaksam
    dispatch     = false,      -- false | default_dispatch | cd_dispatch | ps-dispatch | rcore_dispatch | ...
    phone        = 'auto',     -- auto | lb-phone | yseries | false
}
```

`'auto'` tries known resources in order and uses the first one that is running. Set an explicit value to force one, or `false` to disable that integration. The full list of supported values for each key is in [Phone & Integrations](/resources/blackmarkets-player-run-contraband-operation/phone-and-integrations.md).

## NUI

```lua
cfg.nui = {
    primaryColor   = '#f15d38',                  -- accent color of every panel
    itemImagesPath = 'ox_inventory/web/images/', -- where item icons are served from

    overlays = { -- on-screen position of each HUD overlay
        driver       = 'top-right',
        vanInspect   = 'top-right',
        door         = 'top-right',
        seller       = 'top-left',
        customer     = 'right-center',
        customerCart = 'left-center',
    },
}
```

`primaryColor` also accepts an `{ r, g, b, a }` table. `itemImagesPath` is a resource-relative path; the script adds the `nui://` prefix itself, so don't include it. Each overlay accepts any of the nine anchor positions (`top-left`, `center`, `bottom-right`, …).

## Interaction prompt

```lua
cfg.interaction = {
    controlId = 38,    -- key for "press to interact" (38 = E)
    text      = 'E',
    colors = {
        background = '#f15d38ff',
        text       = '#e2e8f0ff',
    },
}
```

## Police

```lua
cfg.police = {
    jobs        = { 'police', 'sasp', 'bcso', 'sheriff' }, -- jobs treated as officers
    countMethod = 'fast',  -- 'fast' (state-based) | 'scan' (per-player check)

    seize  = { progressMs = 5000 },                 -- van seizure cast time
    search = { enabled = true, progressMs = 4000 }, -- van search cast time
}
```

`jobs` decides who counts as police for drop gating, dispatch and the seize/search interactions. `countMethod` controls how the live officer count is measured; leave it on `fast` unless you have a reason to change it.

## Warehouse

The core of each operation. Coordinates here are the **default template** used when an admin creates a warehouse (the entrance/exit are then placed in-world per warehouse).

```lua
cfg.warehouse = {
    coords = {
        door         = vec4(1087.12, -3099.38, -39.35, 270.0), -- on-foot interior door
        laptop       = vec3(1087.74, -3101.30, -39.20),        -- laptop interaction point
        fallbackExit = vec4(-269.41, -956.32, 31.22, 200.0),   -- where players are sent if their warehouse is deleted
        vehicle = {
            park   = vec4(1095.8, -3099.54, -39.35, 90.0),  -- where the van parks inside
            entry  = vec4(1101.68, -3099.54, -39.35, 90.0), -- where the van enters/exits the interior
            camPos = vec3(1087.21, -3102.49, -36.43),
            camRot = vec3(-29.95, 0.00, -57.32),
        },
    },

    crates = { enabled = true, slots = { --[[ decorative crate props ]] } },

    blip = { enabled = true, sprite = 478, color = 5, scale = 0.8, label = locale('blip.warehouse') },

    defaults = { stock = {}, vault = 0 }, -- starting stock/vault for a brand-new warehouse

    vault   = { maxBalance = false },               -- false = no ceiling, or a number cap
    pricing = { default = 2.0, limit = { min = 0.0, max = 5.0 } }, -- sale-price multiplier of supply price
}
```

* **`crates.slots`** are purely cosmetic crate props placed inside the interior.
* **`pricing`** sets the default sale-price multiplier and the min/max range owners may set per item.

## Items

The master catalog. Anything sellable, orderable or stockable must be listed here **and** exist in your inventory.

```lua
cfg.items = {
    { name = 'WEAPON_PISTOL', supplyPrice = 250 },
    { name = 'WEAPON_APPISTOL', supplyPrice = 850, maxPerSupplyOrder = 8 },
    { name = 'coke_brick', supplyPrice = 1500 },
    { name = 'meth', supplyPrice = 80, maxPerSupplyOrder = 100 },
    -- ...
}
```

| Field                                     | Required | Meaning                                                     |
| ----------------------------------------- | -------- | ----------------------------------------------------------- |
| `name`                                    | Yes      | Item/weapon name as registered in your inventory.           |
| `supplyPrice`                             | Yes      | Cost to order one unit (the buy price from the vault).      |
| `label`                                   | No       | Display name override (otherwise taken from the inventory). |
| `weight`                                  | No       | Per-unit weight override for van capacity.                  |
| `minPerSupplyOrder` / `maxPerSupplyOrder` | No       | Per-order quantity limits for this item.                    |
| `propModel`                               | No       | Custom prop shown in the van item-preview camera.           |

Full walkthrough on [How to Add an Item](/resources/blackmarkets-player-run-contraband-operation/how-to-add-an-item.md).

## Supply orders

```lua
cfg.supplyOrder = {
    maxConcurrentOrders      = 2,    -- active orders per warehouse
    defaultMinPerSupplyOrder = 1,
    defaultMaxPerSupplyOrder = 10,

    deliveryFee   = 1000, -- flat fee added on top of the items
    minOrderTotal = 1000, -- minimum subtotal to place an order (or false)

    scaling = { -- bigger orders take longer and draw more police attention
        maxAtSubtotal      = 50000, -- subtotal at which ETA/dispatch hit their max
        baseEtaSeconds     = 1200,
        maxEtaSeconds      = 4800,
        baseDispatchChance = 0.3,
        maxDispatchChance  = 1.0,
    },

    refundOnCancel = false, -- refund the vault if an order is cancelled

    drop = {
        claimSeconds = 1800, -- time to collect a ready drop before it expires
        locations    = { --[[ vector4 list of possible drop points ]] },
        model        = `prop_cs_cardbox_01`,
        minPoliceCount = 0,  -- min officers online before a drop can be revealed

        dispatch = { enabled = true, code = 'SUPPLY_PICKUP', message = locale('dispatch.supply_pickup'), blip = { sprite = 616, color = 1 } },
        livePoliceTrack = { enabled = false, durationSeconds = 180, updateIntervalMs = 2000, blip = { --[[...]] } },
    },
}
```

* **`scaling`** makes the order ETA and the police-dispatch chance ramp up linearly with the order subtotal, up to `maxAtSubtotal`.
* **`drop.locations`** is the pool the server randomly picks from when an order goes *ready*. Add your own `vec4` points or edit them live from the admin panel ([Admin Panel](/resources/blackmarkets-player-run-contraband-operation/admin-panel.md)).
* **`livePoliceTrack`** optionally pins a live, updating blip on the suspect during pickup.

## Van

```lua
cfg.van = {
    model             = `speedo4`,
    color             = { primary = 0, secondary = 0 }, -- vehicle color ids
    platePattern      = false,  -- false = random, or a pattern like '11AAA111'
    cargoLostOnDestroy = true,  -- destroyed van also wipes its cargo

    capacity = { mode = 'weight', max = 150000 }, -- 'weight' | 'slots' | false

    exteriorProps = { --[[ decorative props attached to the van ]] },
    orderProp     = { --[[ the box prop shown while carrying an order ]] },

    replacement = {
        requireAfterSeize   = true,  -- must order a new van after a seizure
        requireAfterDestroy = false,
        fee                 = 50000, -- vault cost of a replacement van
        deliveryDelaySeconds = 600,
    },
}
```

* **`capacity.mode`** — `weight` uses each item's weight, `slots` counts item stacks, `false` disables limits.
* **`replacement`** controls whether losing the van (seizure/destruction) forces a paid replacement before the warehouse can operate again.

## Sale

Controls the mobile van sale and the customer experience.

```lua
cfg.sale = {
    publicShare = {
        mode = 'optional', -- 'always' | 'optional' | 'never' — broadcast the sale publicly
        dispatch = { enabled = true, code = 'PUBLIC_SALE', message = locale('dispatch.public_sale') },
        blip     = { color = 1, sprite = 616, scale = 0.9, label = locale('blip.sale') },
    },

    forceFirstPerson = false,
    sellerPose  = { --[[ where/how the seller sits at the van ]] },
    customerCam = { enabled = true, distance = 2.2, height = 0.5, fov = 45.0 },
    itemPreview = { enabled = true, defaultProp = `v_ind_cs_box02`, --[[ zoom/offset for the 3D item preview ]] },
}
```

* **`publicShare.mode`** — `always` forces the sale onto the public map/dispatch, `never` hides it, `optional` lets the seller choose.
* **`itemPreview`** renders the highlighted item as a 3D prop on the van with an optional zoom; use a per-item `propModel` (in `cfg.items`) to override the prop.

## Member presets

Role templates offered when hiring crew. Each preset is a set of permission flags.

```lua
cfg.memberPresets = {
    {
        id = 'manager', label = 'Manager',
        permissions = { placeOrder = true, pickupOrder = true, orderVan = true, vanCargo = true,
            driveVan = true, setSalePrice = true, openSale = true, vaultDeposit = true, vaultWithdraw = true,
            viewFinancials = true, answerDoor = true, manageMembers = true, chatRespond = true },
    },
    {
        id = 'worker', label = 'Worker',
        permissions = { pickupOrder = true, vanCargo = true, driveVan = true, openSale = true,
            vaultDeposit = true, answerDoor = true, chatRespond = true },
    },
}
```

Owners can fine-tune individual permissions per member from the laptop afterwards; presets are just the starting point. The full permission key list is shown in the Members tab and on [Admin Panel](/resources/blackmarkets-player-run-contraband-operation/admin-panel.md).

## Admin

```lua
cfg.admin = {
    command            = 'bm_admin', -- command that opens the admin panel (or false to disable)
    allowedIdentifiers = {           -- identifiers allowed without an ace permission
        -- ['license:xxxxxxxx'] = true,
    },
}
```

Players with the `command` ace are always allowed. See [Admin Panel](/resources/blackmarkets-player-run-contraband-operation/admin-panel.md).

## NPC vendor

```lua
cfg.npcVendor = {
    enabled           = true,
    npcModel          = `a_m_m_business_01`,
    pricingMultiplier = 3.0, -- sell price = item price override, else supplyPrice * multiplier

    vendors = { --[[ one or more scheduled NPC vendors ]] },
}
```

This is large enough to have its own page — see [NPC Vendor](/resources/blackmarkets-player-run-contraband-operation/npc-vendor.md).

## Black money

```lua
cfg.blackMoney = {
    enabled     = false,         -- pay sales in black money instead of cash
    type        = 'item',        -- 'item' | 'account' | 'markedbills'
    item        = 'black_money',
    account     = 'black_money',
    metadataKey = 'worth',       -- metadata key for markedbills worth
}
```

When disabled, sales pay out in regular cash.

## Retention

```lua
cfg.retention = {
    logs          = 30, -- days to keep sales/vault/audit logs
    conversations = 30, -- days to keep phone chats (0 disables pruning)
}
```

A daily cron prunes anything older than these windows. Set a value to `0` to keep records forever.
