> For the complete documentation index, see [llms.txt](https://otters-developments.gitbook.io/otters-developments/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://otters-developments.gitbook.io/otters-developments/paid-resources/used-vehicle-dealership/configuration.md).

# Configuration

All normal server configuration is handled in `config.lua`.

{% hint style="warning" %}
Do not publish private Discord webhooks or image-upload endpoints. The distribution config keeps both values blank until the server owner adds their own.
{% endhint %}

## Framework, Commands & Admin Access

```lua
Config.Debug = false
Config.Framework = 'auto' -- auto | qbox | qb | esx
Config.Currency = '$'
Config.Command = 'usedcars'
Config.AdminCommand = 'usedvehiclesadmin'
Config.AdminAce = 'otters_usedvehicles.admin'
```

`auto` is recommended for framework detection. `/usedcars` is the normal player command and requires the player to be at a dealership. `/usedvehiclesadmin` is ACE protected and can be used anywhere by authorised staff.

## Dealerships

`Config.Dealerships` controls every dealership location. Each entry can define its own NPC, listing drop-off, photography bay, blip, ambient traffic zone, showcase slots and test-drive settings.

```lua
['sandy'] = {
    Label = 'Used Vehicles Dealership',
    Open = vector3(1224.74, 2728.29, 38.01),
    Ped = vector4(1224.74, 2728.29, 38.01, 180.0),
    SellerDropoff = vector4(1235.41, 2731.32, 38.01, 357.47),
    PhotoBay = vector4(-147.44, -596.23, 167.00, 102.57),

    TestDriveReturnMode = 'ped', -- ped | custom
    TestDriveReturnDistance = 0.9,

    TestDriveSeconds = 60,
    TestPlatePrefix = 'TEST',
    TestDriveFuel = 100.0,
    OpenDistance = 2.0,
    ShowcaseSpawnDistance = 120.0,
}
```

With `TestDriveReturnMode = 'ped'`, the return point is calculated slightly in front of the dealership NPC from the NPC position and heading. This avoids needing a separate return coordinate.

Add additional dealership entries by copying the supplied structure and changing its coordinates/settings.

## Showcase Slots & Vehicle Information

Each dealership contains a `ShowcaseSlots` list of `vector4` positions. Active listings are displayed in these slots while players are near the dealership.

```lua
Config.Showcase = {
    RefreshIntervalMs = 5000,
    VehicleInfo = {
        Enabled = true,
        ShowDistance = 2.0,
        MaxVisible = 1,
        HeightOffset = 1.55,
        UpdateMs = 50,
    },
}
```

The floating vehicle card can show listing price and available vehicle information such as mileage, condition, performance, tuner chip, nitro and harness data.

## Listing Settings

```lua
Config.Listing = {
    BaseFee = 2500,
    RelistDurationHours = 24,
    Durations = {
        { label = '24 Hours', hours = 24, fee = 2500 },
        { label = '3 Days', hours = 72, fee = 5500 },
        { label = '7 Days', hours = 168, fee = 10000 },
        { label = '14 Days', hours = 336, fee = 17500 },
    },
    MaxActivePerPlayer = 3,
    MaxCommentLength = 500,
    PaymentMethods = { 'bank', 'cash' },
    MinSalePrice = 5000,
    MaxSalePrice = 5000000,
    SaleCommissionPercent = 5,
}
```

`BaseFee` is combined with the selected duration fee when a listing is created. Listing payment is validated server-side. The optional sale commission is deducted from the seller after a successful sale.

Anti-abuse pricing can also compare the requested sale price against the vehicle's original/base price when one can be resolved.

## Stored Vehicle Validation

Only vehicles currently stored/garaged can be listed.

```lua
Config.Garage = {
    System = 'auto',
    ListingHoldingGarage = 'otters_usedvehicles',
    BuyerDefaultGarage = 'pillboxgarage',
    StoredState = 1,
    CustomGetOwnedVehicles = nil,
    CustomIsVehicleStored = nil,
    CustomSetStoredState = nil,
}
```

For the normal QB/Qbox vehicle table, `state = 1` is treated as stored. ESX uses its stored state. The listing menu filters out vehicles that are currently outside, and the server validates the stored state again when the listing is submitted.

Custom garage systems can override the stored check with `CustomIsVehicleStored`.

## Listing Restrictions

```lua
Config.Restrictions = {
    RequirePhysicalVehicleForListing = false,
    BlockEmergencyClass = true,
    BlockModels = {},
    RequireVehicleEmpty = true,
}
```

Use `BlockModels` to prevent specific vehicle models from being listed. Emergency-class blocking can be enabled independently.

## Bumping

```lua
Config.Bump = {
    Enabled = true,
    CooldownHours = 6,
    Fee = 2500,
    PaymentMethod = 'bank',
}
```

When a seller presses Bump Listing, the UI asks them to confirm the configured price before payment is processed. A successful bump moves the listing back up the marketplace and starts the configured cooldown.

## Offers & Messages

```lua
Config.Offers = {
    Enabled = true,
    MinimumPercent = 50,
    ReservationMinutes = 30,
    MaxMessageLength = 500,
}
```

Sellers can choose whether a listing is open to offers. Buyers can send an offer only when offers are enabled for that listing.

The server checks that the buyer currently has enough money for the proposed amount, but **does not remove the money when the offer is sent**. The seller can agree, decline or counter. Accepted offers reserve the vehicle for the configured period; payment happens when the purchase is completed.

## Seller Profiles & Reviews

```lua
Config.Reviews = {
    Enabled = true,
    MaxCommentLength = 500,
    MaxProfileDescriptionLength = 250,
    AllowProfileImageUpload = true,
    DefaultAvatar = '',
    AllowProfilePhoneDisplay = true,
    VerifiedPurchasesOnly = true,
}
```

The global Seller Directory lets players compare sellers using their rating, verified reviews, completed sales and active listings. With `VerifiedPurchasesOnly = true`, reviews can only be submitted by genuine buyers after a completed marketplace sale.

## Interaction

```lua
Config.Interaction = {
    System = 'auto', -- auto | ox_target | qb-target | qtarget | interact | textui
    PedModel = 'a_m_m_business_01',
    PedScenario = 'WORLD_HUMAN_CLIPBOARD',
    TargetIcon = 'fa-solid fa-car',
    TargetLabel = 'Browse Used Vehicles',
}
```

Use `auto` for automatic selection of a supported interaction resource.

## Notifications

```lua
Config.Notifications = {
    System = 'ox_lib',
    Title = 'Used Vehicles',
    Position = 'top-right',
    Duration = 5000,
    Custom = nil,
}
```

External notifications are used while the dealership panel is closed.

The open dealership panel uses a separate built-in themed notification stack:

```lua
Config.PanelNotifications = {
    Enabled = true,
    Title = 'Used Vehicles',
    Duration = 5000,
    MaxVisible = 3,
}
```

This keeps feedback visible above the focused dealership UI without relying on another NUI resource being rendered on top.

## Phone Integration

```lua
Config.Phone = {
    System = 'auto', -- auto | lb-phone | qb-phone | qs-smartphone | high-phone | renewed-phone | roadphone | yseries | framework | custom | none
    CustomGetNumber = nil,
    LBApp = {
        Enabled = true,
        Identifier = 'otters-usedvehicles',
        Name = 'Used Dealership',
        Description = 'Browse player-owned vehicles for sale.',
        DefaultApp = true,
        Icon = 'html/phone/icon.svg',
    },
}
```

LB Phone can use the included custom application for browsing listings, seller information, messages and offers. Other configured phone modes can be used for phone-number resolution where supported.

## Vehicle Photography

```lua
Config.Photos = {
    Enabled = true,
    RequiredToList = true,
    System = 'fivemanage_url',
    ScreenshotResource = 'screenshot-basic',
    UploadURL = '',
    Count = 4,
    ModelLoadTimeoutMs = 15000,
    UploadTimeoutMs = 10000,
}
```

When enabled, the resource captures four listing images using `screenshot-basic`. The upload endpoint is intentionally blank in the distributed config and must be supplied by the server owner.

`PhotoBay` is configured per dealership and is used as the remote capture location. The player is not required to manually position the vehicle for each image.

## Vehicle Keys

```lua
Config.Keys = {
    System = 'auto', -- auto | qbx_vehiclekeys | qb-vehiclekeys | Renewed-Vehiclekeys | cd_garage | wasabi_carlock | custom | none
    VerifyTimeoutMs = 3500,
    CustomGive = nil,
    CustomRemove = nil,
}
```

The Qbox vehicle-keys integration supports the compatibility path used by custom builds and falls back to the standard Qbox GiveKeys method where required.

## Fuel

```lua
Config.Fuel = {
    System = 'auto',
    CustomGet = nil,
    CustomSet = nil,
}
```

Use `auto` for supported fuel detection or configure the desired fuel integration in the release config. Custom getter/setter functions are available for unsupported systems.

## Mileage

```lua
Config.Mileage = {
    System = 'auto', -- auto | jg-vehiclemileage | custom | none
    ResourceNames = { 'jg-vehiclemileage', 'jg_mileage', 'jg-mileage' },
    DisplayUnit = 'mi',
    StoredUnit = 'mi',
    CustomGet = nil,
}
```

`DisplayUnit` controls whether buyers see miles or kilometres. `StoredUnit` tells the resource which unit the source mileage data uses so it can convert where required.

## Vehicle Database & Lambra Data

`Config.VehicleDatabase` maps the QB/Qbox ownership table columns used for fuel, engine/body health, mileage, tuner chip, nitro, harness and damage data.

Lambra tuner and nitro support can read the configured owned-vehicle columns directly. Friendly tuner chip names can be configured in:

```lua
Config.Lambra.Tuner.ChipLabels = {
    tunerchip1 = 'White Tuner Chip',
}
```

Harness information can also be read from the owned vehicle database or a custom/database integration.

## Admin Showcase Testing

```lua
Config.AdminTestMode = {
    Enabled = true,
    Command = 'usedvehiclestest',
    DefaultVehicle = 'sultan',
    ShowSlotLabels = true,
}
```

This is intended for staff setting up showcase positions. It temporarily fills the configured slots with local test vehicles and can display slot coordinates/labels.

## Discord Logging

Discord is kept at the bottom of the config:

```lua
Config.Discord = {
    Enabled = true,
    Webhook = '',
    BotName = 'Otters Used Vehicles',
    BotAvatar = '',
    ImageIndex = 3,

    LogListed = true,
    LogPurchased = true,
    LogRemoved = true,
    LogTestDrives = true,
    LogBumps = true,
    LogReviews = true,
}
```

Paste your own Discord webhook into `Webhook`. The distributed value is intentionally blank. Individual log types can be enabled or disabled.

## Theme

The main theme values are available through `Config.Theme`, while `html/css/theme.css` remains accessible for additional UI styling.

The default release uses the Otters Developments dark/magenta visual style. Server owners can change the colours without editing protected source files.

## Automatic Database Installation

The resource automatically checks and creates its required database tables after `oxmysql` is available.

The release also includes:

```
sql/otters_usedvehicles.sql
```

This can be used as a manual installation or backup option if required.

## Debugging

```lua
Config.Debug = false
```

Change this to `true` while troubleshooting framework/integration data. Disable it again on a live server.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://otters-developments.gitbook.io/otters-developments/paid-resources/used-vehicle-dealership/configuration.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
