Exports
External resources can drive the car dealer programmatically via FiveM exports and hook events. All exports are defined in server/exports.lua.
Exports Trust Boundary
Section titled “Exports Trust Boundary”Mutating exports (forceSale, addVehicle) are gated by Config.Integrations.forceSaleAllowList. An empty table allows all resources (open-by-default). Listing specific resource names restricts those exports to only the listed callers (identified via GetInvokingResource). Read-only exports (getCatalog, getShop, getLoans) are always allowed — they leak no money or inventory.
Server Exports
Section titled “Server Exports”getCatalog
Section titled “getCatalog”Read-only snapshot of every vehicle the dealer network can sell. Does not require allow-listing.
--- @return table cataloglocal catalog = exports['ars_cardealer']:getCatalog()Returns an array of tables, sorted alphabetically by label:
| Field | Type | Description |
|---|---|---|
model |
string | Vehicle spawn name (unique key) |
label |
string | Display name |
category |
string | Vehicle category |
price |
number | Retail price |
stock |
number | Stock count (-1 = unlimited) |
Example:
local catalog = exports['ars_cardealer']:getCatalog()for _, v in ipairs(catalog) do print(v.model, v.label, v.price)endgetShop
Section titled “getShop”Read-only shop summary. Returns nil if the shop id does not exist. Does not require allow-listing.
--- @param shopId string--- @return table|nil shoplocal shop = exports['ars_cardealer']:getShop('pdm')| Field | Type | Description |
|---|---|---|
id |
string | Shop identifier |
label |
string | Display label |
active |
boolean | Whether the shop is active |
ownable |
boolean | Whether the shop can be player-owned |
productCount |
number | Number of vehicles stocked at this shop |
balance |
number? | Shop balance (only if owned) |
open |
boolean? | Shop open state (only if owned) |
Example:
local shop = exports['ars_cardealer']:getShop('pdm')if shop then print(shop.label, shop.balance, shop.open)endforceSale
Section titled “forceSale”Sell a vehicle to a player bypassing the in-game UI. Useful for VIP shops, event rewards, quest rewards, or external store menus. Requires allow-listing unless forceSaleAllowList is empty.
--- @param src number Player server ID--- @param dealerId string Shop identifier--- @param model string Vehicle spawn name--- @param opts table Options (all optional)--- @return table result { ok = true } | { ok = false, reason = '...' }local res = exports['ars_cardealer']:forceSale(playerSrc, 'pdm', 'sultan', { paymentType = 'bank', colorPrimary = { mode = 'palette', index = 0 },})Options (opts):
| Key | Type | Description |
|---|---|---|
paymentType |
string? | Payment method (cash, bank, or a custom currency id). Defaults to cash. |
colorPrimary |
table? | Primary colour payload. |
colorSecondary |
table? | Secondary colour payload. |
repSrc |
number? | Salesperson source for commission attribution. |
livery |
number? | Livery index applied to the delivered vehicle. |
chameleon |
boolean? | Apply chameleon paint. |
Failure reasons: not_allowed, invalid_source, not_sold, bad_payment_type, out_of_stock, no_price, and other processSale rejection reasons.
Example:
RegisterCommand('givevip', function(source, args) local target = tonumber(args[1]) local res = exports['ars_cardealer']:forceSale(target, 'pdm', 'adder', { paymentType = 'bank', colorPrimary = { mode = 'palette', index = 111 }, }) if not res.ok then print('Sale failed:', res.reason) endend)addVehicle
Section titled “addVehicle”Add or update a vehicle in the master catalog. Lets external resources register models without the admin UI. Requires allow-listing unless forceSaleAllowList is empty.
--- @param model string Vehicle spawn name--- @param opts table Options (all optional)--- @return table result { ok = true } | { ok = false, reason = '...' }local res = exports['ars_cardealer']:addVehicle('sultan', { label = 'Karin Sultan', category = 'cars', price = 25000, stock = -1,})Options (opts):
| Key | Type | Description |
|---|---|---|
label |
string? | Display name |
category |
string? | Vehicle category |
price |
number? | Retail price |
stock |
number? | Stock count (-1 = unlimited) |
image |
string? | Image URL |
stats |
table? | { speed, accel, handling, braking } |
Failure reasons: not_allowed, no_model, exists (model already in the catalog — reported by the create path), not_found (reported by the update path).
Example:
exports['ars_cardealer']:addVehicle('blista', { label = 'Dinka Blista', category = 'compacts', price = 12000,})getLoans
Section titled “getLoans”Query a player’s active loans so external finance UIs can show the same data as the in-game Financing view. Does not require allow-listing.
--- @param src number Player server ID--- @return table[] loanslocal loans = exports['ars_cardealer']:getLoans(playerSrc)Returns an array of loan rows from ars_cardealer_financing where status = 'active', ordered by financetime ascending.
Example:
local loans = exports['ars_cardealer']:getLoans(targetSrc)for _, loan in ipairs(loans) do print(loan.model, loan.plate, loan.payment_amount, loan.payments_remaining)endHook Events
Section titled “Hook Events”The hook API is toggled by Config.Integrations.Hooks.enabled (default true). External resources register for these events via RegisterNetEvent.
ars_cardealer:prePurchase
Section titled “ars_cardealer:prePurchase”Cancellable. Fires after all validation passes but before money is taken. A listener calls ctx.cancel('reason') to block the sale.
AddEventHandler('ars_cardealer:prePurchase', function(ctx) if ctx.paymentType == 'cash' and ctx.price > 500000 then ctx.cancel('luxury_cash_blocked') endend)Context payload:
| Field | Type | Description |
|---|---|---|
dealerId |
string | Shop identifier |
customerSrc |
number | Buying player server ID |
customerCitizenid |
string | Buying player citizenid |
customerName |
string | Buying player name |
repSrc |
number? | Salesperson server ID (assisted sale) |
repCitizenid |
string? | Salesperson citizenid |
model |
string | Vehicle spawn name |
plate |
string | Vehicle plate |
price |
number | Base price (pre-currency-conversion) |
charge |
number | Actual amount deducted (post-conversion) |
paymentType |
string | Payment method |
financed |
boolean | Whether the purchase is financed |
couponCode |
string? | Applied coupon code |
cancel(reason) |
function | Call to block the sale with a reason string |
ars_cardealer:postSale
Section titled “ars_cardealer:postSale”Notification only. Fires after the sale fully completes (vehicle delivered, money taken, commission paid). Errors in listeners are caught so they cannot roll back a completed sale.
AddEventHandler('ars_cardealer:postSale', function(ctx) -- Same payload as prePurchase, minus the cancel function print(('Sale: %s bought %s for $%s'):format( ctx.customerName, ctx.model, ctx.charge))end)Note: This guide is written by a third party. If you find any incorrect or outdated information, please contact us on Discord so we can update it for you.