F5 StudioF5 Studio
Skip to main content

Configuration

F5 Shadow Market has two configuration files:

FileLoaded asContents
config.luasharedEverything below — economy, bargaining, discounts, delivery, framework/inventory/target, money, admin, database, debug, placer, controls
config_webhooks.luaserver-onlyDiscord webhook URLs, per-event colors, queue tuning (Webhooks)
Server-only

config_webhooks.lua is loaded only on the server. Never put webhook URLs in config.lua — they would leak to clients.

Sections in config.lua are ordered by how often a server owner needs them — economy first, advanced tuning last.

General

config.lua
F5Cfg.Locale = 'en'
F5Cfg.Item = 'market_tablet'

F5Cfg.Command = {
mode = 'item_or_command',
name = 'market',
suggestion = true,
}
OptionTypeDefaultDescription
Localestring'en'Translation file from locales/<code>.lua. See Localization
Itemstring'market_tablet'The usable inventory item that opens the market
Command.modestring'item_or_command''item_only' (no command), 'item_or_command' (command requires the item), 'command_always' (command needs no item). See Commands
Command.namestring'market'Chat command name (ignored when mode = 'item_only')
Command.suggestionbooleantrueShow the /command hint in the chat suggestion list

Interface Sounds

config.lua
F5Cfg.Sounds = {
enabled = true,
defaultVolume = 0.6,
}
OptionTypeDefaultDescription
enabledbooleantrueMaster switch for the NUI sound layer
defaultVolumenumber0.6Starting volume 0.01.0. Players can override it in the UI

Market Economy

The core auction-house economy.

config.lua
F5Cfg.Market = {
feeMode = 'percent',
listingFee = 500,
listingFeePercent = 0.07,
listingFeeCharge = 'on_sale',

platformCut = 0.10,

minPrice = 1,
maxPrice = 1000000,
minBuyout = 1,
maxBuyout = 5000000,

maxActiveListings = 10,

durations = {
{ hours = 1 },
{ hours = 6 },
{ hours = 12 },
{ hours = 24 },
{ unlimited = true },
},

anonymousPrefix = 'Shadow',

minBidIncrement = 0.05,
minBidIncrementFlat = 100,

reviewWindowHours = 24,
favoriteRetentionDays = 2,
bidHistoryRetentionDays = 2,
notificationRetentionDays = 7,
notificationStaleGraceMinutes = 30,

itemFilter = { mode = 'blacklist', blacklist = { ... }, whitelist = { ... } },
}

Fees & Cuts

OptionTypeDefaultDescription
feeModestring'percent''percent' → fee is a share of the start price; 'flat' → fee is a fixed amount
listingFeenumber500Flat fee amount (used when feeMode = 'flat')
listingFeePercentnumber0.07Share of the start price (used when feeMode = 'percent'), 0.07 = 7%
listingFeeChargestring'on_sale''on_sale' → fee deducted from the seller's payout on completion; 'on_create' → fee paid upfront when the listing is created
platformCutnumber0.10The market's cut of every completed sale, 0.10 = 10%. Deducted from the seller's payout
Seller payout math

On a completed sale the seller receives price − platformCut − (listingFee if charged on_sale). The buyer's money is held in escrow from the moment of purchase / winning bid until the package is collected.

Price Limits

OptionTypeDefaultDescription
minPrice / maxPricenumber1 / 1000000Allowed start-price range
minBuyout / maxBuyoutnumber1 / 5000000Allowed buy-now / auction buyout range
maxActiveListingsnumber10Max active listings per seller

Durations

durations is the list of listing lifetimes offered in the UI. Each entry is either { hours = N } or { unlimited = true }.

FormMeaning
{ hours = N }Timed listing that expires after N hours
{ unlimited = true }Buy-now only — never expires. Auctions clamp an "unlimited" choice down to the longest timed entry

Seller Identity

OptionTypeDefaultDescription
anonymousPrefixstring'Shadow'Prefix for the anonymous seller handle, e.g. Shadow#1234. Each citizen is assigned a stable handle stored in f5_shadowmarket_identities

Bidding

OptionTypeDefaultDescription
minBidIncrementnumber0.05Next bid must beat the current bid by at least this share (5%) …
minBidIncrementFlatnumber100… or by at least this flat amount — whichever is larger. The first bid must be at least the start price

Retention & Cleanup

A sweep runs server-side every 60 s; the retention purges below run every 5 cycles (≈5 min).

OptionTypeDefaultDescription
reviewWindowHoursnumber24How long after an order concludes a buyer may rate the seller
favoriteRetentionDaysnumber2Favorites of closed listings are purged after this many days
bidHistoryRetentionDaysnumber2Closed auctions stay in the player's "My bids" view this long
notificationRetentionDaysnumber7Old notifications are deleted after this many days
notificationStaleGraceMinutesnumber30Notifications about a concluded listing/order are purged this long after conclusion (read ones go sooner). sale_completed and review notifications are protected from auto-purge

Item Filter

Controls which inventory items can be listed on the market.

config.lua
itemFilter = {
mode = 'blacklist',

blacklist = {
'id_card', 'driver_license', 'lawyerpass',
'market_tablet', 'phone', 'radio',
},

whitelist = {
'lockpick', 'weapon_pistol', 'goldbar',
},
}
OptionTypeDefaultDescription
modestring'blacklist''blacklist' → every item is listable except those in blacklist; 'whitelist'only items in whitelist are listable
blackliststring[](6 items)Item names blocked when mode = 'blacklist'
whiteliststring[](3 items)Item names allowed when mode = 'whitelist'

The active list is evaluated by Bridge.IsItemAllowed(itemName) — an item with no name is always rejected.

Bargaining

Buy-now counter-offers: a buyer proposes a lower price, the seller accepts, declines or counters.

config.lua
F5Cfg.Bargain = {
enabled = true,
allowOnDiscounted = true,
maxDiscount = 0.30,
maxOffers = 5,
}
OptionTypeDefaultDescription
enabledbooleantrueMaster switch for bargaining
allowOnDiscountedbooleantrueAllow offers on listings the seller already marked down. false hides the offer box on discounted listings
maxDiscountnumber0.30Lowest acceptable offer = buyout * (1 - maxDiscount) — here, 70% of buyout
maxOffersnumber5Number of times the seller can decline before the buyer is locked out of offering on that listing

See Features → Bargaining.

Price Discounts

Lets sellers cut the price of their own buy-now listings.

config.lua
F5Cfg.Discount = {
enabled = true,
minStepPercent = 0.05,
cooldownMinutes = 10,
notifyFavorites = true,
}
OptionTypeDefaultDescription
enabledbooleantrueLet sellers lower the price of their own buy-now listings
minStepPercentnumber0.05Each cut must lower the price by at least this share (5%)
cooldownMinutesnumber10Minimum wait between price cuts on the same listing
notifyFavoritesbooleantrueNotify buyers who favorited the listing when its price drops

See Features → Seller Discounts.

Delivery

After a sale the seller physically drops off a package, the buyer collects it. This section drives that loop.

config.lua
F5Cfg.Delivery = {
itemPackage = {
model = 'prop_cs_street_binbag_01',
blipSprite = 501,
blipColor = 2,
blipScale = 0.8,
blipLabel = 'blip_package_pickup',
pickupDist = 2.0,
},

dropoffTimeoutHours = 24,
pickupTimeoutHours = 24,
maxDropoffDist = 24.0,

visibility = 'parties',
renderDistance = 100.0,
syncInterval = 1500,
}
OptionTypeDefaultDescription
itemPackage.modelstring'prop_cs_street_binbag_01'Prop spawned as the dropped package
itemPackage.blipSpritenumber501Package blip sprite
itemPackage.blipColornumber2Package blip color
itemPackage.blipScalenumber0.8Package blip scale
itemPackage.blipLabelstring'blip_package_pickup'Locale key for the blip label
itemPackage.pickupDistnumber2.0Distance within which the buyer can collect the package (m)
dropoffTimeoutHoursnumber24Seller must drop the package within this window after the sale, or the order expires (buyer refunded)
pickupTimeoutHoursnumber24Buyer must collect within this window after dropoff, or the order expires (buyer refunded)
maxDropoffDistnumber24.0Max distance between the seller and the placed package (m)
visibilitystring'parties''parties' → only buyer + seller see the package; 'everyone' → anyone nearby sees it
renderDistancenumber100.0Distance at which the package prop spawns client-side (m)
syncIntervalnumber1500Client delivery sync loop interval (ms)

See Features → Delivery.

Framework & Compatibility

Framework, inventory, target and notification selection. All default to 'auto'.

config.lua
F5Cfg.Framework = {
mode = 'auto', -- 'auto' | 'esx' | 'qb' | 'qbx' | 'custom'
inventory = 'auto', -- 'auto' | 'ox' | 'qb' | 'qs' | 'ps' | 'codem' | 'tgiann' | 'esx_native' | 'custom'
notify = 'framework', -- 'auto' | 'framework' | 'oxlib'
notifyStyle = 'custom', -- 'custom' | 'native'

custom = { base = 'qb', getCore = nil, --[[ ... ]] },
}

F5Cfg.Target = {
system = 'auto', -- 'auto' | 'ox' | 'qb' | 'custom' | 'none'

pickup = {
label = 'target_pickup_package',
icon = 'fas fa-box-open',
iconColor = nil, -- ox_target only
distance = 2.5,
},

custom = { base = 'ox', resource = 'my-target' },
}

F5Cfg.Inventory = {
imageUrl = 'auto',
}
OptionTypeDefaultDescription
Framework.modestring'auto''auto' detects ESX/QB/QBX by resource; or force a specific adapter, or 'custom' for a fork
Framework.inventorystring'auto''auto' detects the inventory; or force one
Framework.notifystring'framework''framework' uses the framework's native notify; 'auto' uses ox_lib when present, else the framework; 'oxlib' forces ox_lib
Framework.notifyStylestring'custom''custom' = this resource's styled top-right toasts; 'native' = hand off to the notify backend
Framework.customtableCustom-framework adapter config (only when mode = 'custom')
Target.systemstring'auto''auto' detects ox_target/qb-target; or force one; 'none' = no target interaction; 'custom' for a fork
Target.pickup.labelstring'target_pickup_package'Locale key for the package pickup option label
Target.pickup.iconstring'fas fa-box-open'Font Awesome icon for the pickup option
Target.pickup.iconColorstringnilIcon tint, e.g. '#e6b800'. ox_target only — ignored by qb-target. nil = theme default
Target.pickup.distancenumber2.5Package pickup interaction distance (m)
Target.customtableCustom-target adapter config (only when system = 'custom')
Inventory.imageUrlstring'auto''auto' matches the detected inventory's image path, or set a nui://<resource>/.../%s.png override

The full detection logic, the custom adapter fields, and all per-inventory image paths and limitations are documented on the Framework Compatibility page.

Money & Currencies

Account-based (non-item) money. The whole market uses a single wallet — payoutType.

config.lua
F5Cfg.Money = {
payoutType = 'bank',

types = {
{ id = 'cash', label = 'Cash', icon = 'fa-solid fa-money-bill-wave', accounts = { qb = 'cash', qbx = 'cash', esx = 'money' } },
{ id = 'bank', label = 'Bank', icon = 'fa-solid fa-building-columns', accounts = { qb = 'bank', qbx = 'bank', esx = 'bank' } },
{ id = 'crypto', label = 'Crypto', icon = 'fa-solid fa-coins', accounts = { qb = 'crypto', qbx = 'crypto' } },
{ id = 'blackmoney', label = 'Black Money', icon = 'fa-solid fa-sack-dollar', accounts = { esx = 'black_money' } },
},
}
OptionTypeDefaultDescription
payoutTypestring'bank'The single wallet used for balances, all spending and all payouts. Must be the id of a type below
types[].idstringInternal currency id (alphanumeric, _, -)
types[].labelstringDisplay label
types[].iconstringA Font Awesome class (e.g. fa-solid fa-coins) or an image path relative to the NUI (e.g. img/crypto.png)
types[].accountstableNative account key per framework (qb / qbx / esx)

A type with no account for the active framework is auto-skipped (e.g. crypto has no ESX account, blackmoney has none on QB/QBox). If payoutType resolves to a skipped/invalid type, the bridge falls back to bank, then to the first active type. See Framework Compatibility → Money & Currencies.

Admin Panel

config.lua
F5Cfg.Admin = {
command = 'marketadmin',
acePermission = 'f5market.admin',
frameworkGroups = { 'admin', 'god' },
auditWebhook = true,
actionCooldownMs = 800,
pageSize = 25,
}
OptionTypeDefaultDescription
commandstring'marketadmin'Chat command that opens the admin panel
acePermissionstring'f5market.admin'ACE permission that grants access. '' disables the ACE path
frameworkGroupsstring[]{ 'admin', 'god' }Framework groups that grant access. {} disables group-based access
auditWebhookbooleantrueMirror admin actions to Discord (admin_audit webhook)
actionCooldownMsnumber800Per-admin debounce on mutating actions (ms)
pageSizenumber25Rows per page in admin tables

Access is granted if the player passes the ACE permission OR belongs to a listed framework group. See Admin Panel.

Database

config.lua
F5Cfg.Database = {
autoCreateTables = true,
}
OptionTypeDefaultDescription
autoCreateTablesbooleantrueCreate and migrate the schema on resource start. When false, no tables/columns/indexes/ENUMs are created or migrated — you must manage the schema yourself

Debug

config.lua
F5Cfg.Debug = {
enabled = false,

categories = {
INIT = true, BRIDGE = true, UI = true, MARKET = true, BARGAIN = true,
ORDER = true, DELIVERY = true, TARGET = true, PLACER = true, IO = true,
WEBHOOK = true, SECURITY = true, PERF = false, ERROR = true,
},

colors = { --[[ ANSI color per category ]] },
severityPrefix = { info = '', warn = '[WARN] ', err = '[ERR] ' },
perfThresholdMs = 75,
}
OptionTypeDefaultDescription
enabledbooleanfalseMaster switch for all debug output (server, client, NUI). false → nothing prints regardless of category
categories.<NAME>booleanmixedPer-category toggle
colors.<NAME>string^1..^8FiveM ANSI color code per category
severityPrefixtable(see above)Prefix per severity (info / warn / err)
perfThresholdMsnumber75DB queries slower than this (ms) log to the PERF category
CategoryWhat it logs
INITResource start, config loading, client readiness
BRIDGEFramework / inventory / target / money detection and resolution
UINUI open/close, tablet item use
MARKETListings, bids, purchases
BARGAINCounter-offer flow
ORDEROrder lifecycle, expiry, returns
DELIVERYPackage dropoff / pickup / sync
TARGETTarget system detection and pickup option add/remove
PLACERPackage placement mode
IODatabase queries, item use, network events
WEBHOOKDiscord dispatch / queue
SECURITYRejected actions, admin denials, cooldown hits
PERFSlow DB queries above perfThresholdMs
ERRORErrors (always logged when the master switch is on)

The Debug system is unified across server, client and NUI — there are no loose print() calls; everything flows through F5Cfg.Debug.

Placer (Advanced)

Tuning for the package placement mode (the ghost preview the seller controls to drop the package).

config.lua
F5Cfg.Placer = {
rotationStep = 5.0,
minDist = 1.5,
maxDist = 16.0,
zoomStep = 0.25,
startDist = 8.0,
surfaceOffset = 0.02,
pedDistMargin = 0.5,

ghostAlpha = 150,
ghostTintValid = { r = 255, g = 255, b = 255 },
ghostTintBlocked = { r = 230, g = 70, b = 70 },

raycastFlags = 1 | 16,
modelYawOffset = 0.0,
pitchSign = 1.0,
}
OptionTypeDefaultDescription
rotationStepnumber5.0Degrees rotated per scroll tick
minDist / maxDistnumber1.5 / 16.0Min/max prop distance from the camera (m)
zoomStepnumber0.25ALT+scroll zoom step (m)
startDistnumber8.0Initial placement distance (m)
surfaceOffsetnumber0.02Extra offset along the surface normal (m)
pedDistMarginnumber0.5Safety margin subtracted from Delivery.maxDropoffDist when validating placement (m)
ghostAlphanumber150Ghost prop transparency (0–255)
ghostTintValidrgbwhiteGhost color when placement is legal
ghostTintBlockedrgbredGhost color when placement is blocked (too far, blocked surface)
raycastFlagsnumber1 | 16GTA raycast bitmask: 1 = world, 16 = objects
modelYawOffsetnumber0.0Constant yaw correction (degrees)
pitchSignnumber1.0-1 or 1; flip if the prop renders upside-down

Controls (Advanced)

Keybinds for the placement mode and the HUD legend shown during placement. Values are FiveM control IDs.

config.lua
F5Cfg.Controls = {
ids = {
confirm = 24, -- LMB
cancel = 25, -- RMB
scrollUp = 241,
scrollDown = 242,
altModifier = 19, -- ALT
},
hud = {
{ key = 'scroll-rotate', label = 'ui_placer_rotate' },
{ key = 'alt+scroll', label = 'ui_placer_zoom' },
{ divider = true },
{ key = 'mouse-left', label = 'ui_placer_place', accent = 'ok' },
{ key = 'mouse-right', label = 'ui_placer_cancel', accent = 'no' },
},
}
FieldDescription
ids.confirmPlace the package (default 24, LMB)
ids.cancelCancel placement (default 25, RMB)
ids.scrollUp / ids.scrollDownRotate / zoom (default 241 / 242)
ids.altModifierHold to zoom instead of rotate (default 19, ALT)

Each hud entry is a key hint row: key (keycap text), label (locale key), optional accent ('ok'/'no'), or { divider = true } for a separator.

See Also

  • Framework Compatibility — framework / inventory / target / money detail and custom adapters
  • Commands — command modes and the admin command
  • Items — registering market_tablet
  • Webhooks — the server-only config_webhooks.lua
  • Features — what the economy options drive in-game