# esx_garage (/docs/addons/esx_garage)

esx_garage is the ESX Legacy garage and impound system. Players park owned vehicles at garages, take them out again through a React UI, favorite, rename or transfer them, and pay a fee to recover vehicles from an impound lot. Version 2.0 replaced the old marker-based config completely, so configs from older versions do not work.

## Overview [#overview]

| | |
| --- | --- |
| Resource | `esx_garage` (version 2.0.0) |
| Side | client + server, NUI (`web/dist`) |
| Dependencies | `esx_lib`, `es_extended`, `oxmysql` |
| Optional | `esx_hud` (mileage shown in the UI) |
| Data | `owned_vehicles` plus columns `parking`, `pound`, `custom_name`, `is_favorite`, `last_used`, `mileage`; table `esx_garage_migrations` |

Features in-game:

- Garages with an entry point (opens the menu), an optional store point and an optional NPC.
- Impound lots with a per-lot fee. The fee is taken from cash first, then from the bank.
- Paginated vehicle list with search and filters (stored, impounded, favorite).
- Rename and favorite vehicles, transfer a stored vehicle to a player within 10 m.
- Optional "give keys" hook for a key script.

### Vehicle state [#vehicle-state]

| `stored` | `parking` | `pound` | Meaning |
| --- | --- | --- | --- |
| `1` | garage id or `NULL` | `NULL` | Parked in a garage |
| `1` | `NULL` | impound id | Impounded |
| `0` | `NULL` | `NULL` | Out in the world |

A vehicle that is out (`stored = 0`) but whose entity no longer exists can only be recovered at an impound, never at a garage. Before respawning it the server deletes every live entity with that plate, so it cannot be duplicated.

## Installation [#installation]

<Steps>
<Step>

### Database [#database]

Nothing to do on an existing server: on start the resource adds missing columns and indexes, converts legacy `stored = 2` rows to the new impound state and records version `1.16.0` in `esx_garage_migrations`. For a fresh or manual install you can import `esx_garage.sql`; every statement is guarded, so it is safe to run again.

</Step>
<Step>

### Ensure the resource [#ensure-the-resource]

```bash title="server.cfg"
ensure oxmysql
ensure esx_lib
ensure es_extended
ensure esx_garage
```

</Step>
</Steps>

## Configuration [#configuration]

### Config.Settings [#configsettings]

| Key | Default | Description |
| --- | --- | --- |
| `interactionDistance` | `3.0` | Distance to a garage or impound point to interact. |
| `restrictToGarage` | `false` | Only allow retrieval at the garage stored in `parking`. `NULL` or removed garages count as "any garage". |
| `defaultImpoundFee` | `400` | Fee for impound lots without their own `cost`. |
| `vehicleKeys` | `false` | Shows the give keys button and enables the `esx_garage:giveKeys` callback. |
| `storeMarkerOnlyInVehicle` | `true` | Only draw the store marker while sitting in a vehicle. |
| `showMarker` | `true` | Draw ground markers at the points. |
| `vehiclesPerPage` | `30` | Default page size of the vehicle list. |
| `maxVehiclesPerMenu` | `100` | Largest page size a client may request (hard capped at 100). |

`Config.Locale` defaults to `GetConvar('esx:locale', 'en')`.

### Config.Garages [#configgarages]

A list of garages. Defaults: `vespucci_boulevard` and `san_andreas_avenue`.

| Field | Type | Description |
| --- | --- | --- |
| `id` | `string` | Unique id, stored in `owned_vehicles.parking`. Do not rename it once used. |
| `label` | `string` | Display name and blip label. |
| `type` | `'public' \| 'job' \| 'gang' \| 'impound'` | Passed to the UI. Access is not derived from it; use `access`. |
| `entryPoint` | `vector3` | Opens the menu. |
| `storePoint` | `vector3?` | Where vehicles are parked. |
| `spawns` | `vector4[]` | Spawn positions; at least one. |
| `blip` | `{ sprite, scale, color }?` | Map blip. |
| `ped` | `{ model, z?, heading?, snapToGround? }?` | Optional NPC. |
| `access` | `{ jobs?, authorize? }?` | `jobs` maps job name to minimum grade. `authorize(source, garage)` is a server-side function that returns `true` to allow. |
| `logo` | `string?` | Image URL shown in the menu header. |
| `color` | `string?` | Hex accent color for the menu. |
| `pound` | `string?` | Linked impound id. |

```lua title="config.lua"
Config.Garages[#Config.Garages + 1] = {
    id = 'mrpd',
    label = 'Mission Row PD',
    type = 'job',
    entryPoint = vec3(454.6, -1017.4, 28.4),
    storePoint = vec3(447.6, -1021.5, 28.5),
    spawns = { vec4(438.4, -1018.3, 27.7, 90.0) },
    blip = { sprite = 357, scale = 0.8, color = 38 },
    access = { jobs = { police = 0 } },
    pound = 'los_santos',
}
```

### Config.Impounds [#configimpounds]

Defaults: `los_santos`, `paleto_bay`, `sandy_shores`, each with `cost = 3000`. The **first** entry is the default lot for automatic impounds.

| Field | Type | Description |
| --- | --- | --- |
| `id` | `string` | Unique id, stored in `owned_vehicles.pound`. |
| `label` | `string` | Display name. |
| `getOutPoint` | `vector3` | Opens the impound menu. |
| `spawns` | `vector4[]` | Spawn positions. |
| `blip` | `{ sprite, scale, color }?` | Map blip. |
| `ped` | `table?` | Optional NPC. |
| `cost` | `integer?` | Fee; falls back to `defaultImpoundFee`. |

## Server exports [#server-exports]

### registerGarage [#registergarage]

```lua
exports.esx_garage:registerGarage(garage)
```

Adds or replaces a garage at runtime and refreshes all clients. Throws (via `assert`) if `id`, `entryPoint` or `spawns` are missing or invalid. Use it for property or gang garages created by other resources.

```lua title="server/main.lua"
exports.esx_garage:registerGarage({
    id = 'apartment_12',
    label = 'Apartment 12',
    type = 'public',
    entryPoint = vec3(-796.1, 304.4, 85.7),
    spawns = { vec4(-791.6, 300.3, 85.4, 180.0) },
    access = {
        authorize = function(source)
            return exports.my_housing:isTenant(source, 12)
        end,
    },
})
```

### registerGarages [#registergarages]

```lua
exports.esx_garage:registerGarages(list)
```

Calls `registerGarage` for every entry in `list`.

### unregisterGarage [#unregistergarage]

```lua
exports.esx_garage:unregisterGarage(id)
```

Removes a garage by id and refreshes clients. Vehicles parked there remain available at any garage.

### getGarages / getImpounds [#getgarages--getimpounds]

```lua
local garages = exports.esx_garage:getGarages()
local impounds = exports.esx_garage:getImpounds()
```

Return arrays of all registered garages and impound lots (including runtime registrations).

### impoundVehicle [#impoundvehicle]

```lua
local ok = exports.esx_garage:impoundVehicle(plate, lotId)
```

| Name | Type | Description |
| --- | --- | --- |
| `plate` | `string` | Plate of an owned vehicle. |
| `lotId` | `string?` | Impound id. Unknown or `nil` falls back to the first lot. |

Deletes the live vehicle if it exists and marks it impounded. Returns `true` when the database row is in the impound afterwards. Typical use: a police or tow job command.

```lua title="server/main.lua"
ESX.RegisterCommand('impound', 'admin', function(xPlayer, args)
    local ok = exports.esx_garage:impoundVehicle(args.plate, 'los_santos')
    xPlayer.showNotification(ok and 'Vehicle impounded' or 'Impound failed')
end, false, {
    help = 'Impound a vehicle by plate',
    arguments = { { name = 'plate', help = 'Plate', type = 'string' } },
})
```

## Server events (hooks) [#server-events-hooks]

Local server events you can listen to with `AddEventHandler`. They cannot be triggered by clients.

| Event | Arguments | Fired when |
| --- | --- | --- |
| `esx_garage:vehicleTransferred` | `source, targetId, plate, fromIdentifier, toIdentifier` | A player transferred ownership of a vehicle. |
| `esx_garage:giveKeys` | `source, plate` | A player pressed "give keys" (only with `vehicleKeys = true`). |

```lua title="server/keys.lua"
AddEventHandler('esx_garage:giveKeys', function(source, plate)
    exports.my_keys:giveKey(source, plate)
end)
```

## Callbacks [#callbacks]

The UI talks to the server through these callbacks. Each is rate limited (1 to 1.5 s) and checks ownership, distance and garage access on the server, so they are safe against spoofed clients. They return `{ success = boolean, error = string? }`.

| Callback | Purpose |
| --- | --- |
| `esx_garage:getGarages` | Garages the player can access, plus all impounds. |
| `esx_garage:getVehicles` | Paginated vehicle list. |
| `esx_garage:retrieveVehicle` | Take a vehicle out (charges the impound fee). |
| `esx_garage:storeVehicle` | Park the current vehicle. |
| `esx_garage:toggleFavorite` | Toggle favorite. |
| `esx_garage:renameVehicle` | Set `custom_name`. |
| `esx_garage:transferVehicle` | Give a stored vehicle to a player within 10 m. |
| `esx_garage:giveKeys` | Fire the `esx_garage:giveKeys` hook. |

The client event `esx_garage:refresh` rebuilds blips and points; it is also triggered on `esx:playerLoaded` and `esx:setJob`.

## Integration tips [#integration-tips]

- Vehicles bought in [esx_vehicleshop](/docs/addons/esx_vehicleshop) land in `owned_vehicles` and show up automatically.
- The garage list does not filter by the vehicle `type` column, so boats and aircraft appear in every garage. Use separate garages with `access` if that matters.
- Despawning an owned vehicle with another script sends it to the default impound. Store it through esx_garage or call `impoundVehicle` with the right lot.
- The `maxVehicles` field from the type annotations is not enforced by the current code.

## Common issues [#common-issues]

- **Old config does not load**: v2 uses `Config.Settings`, `Config.Garages` as a list with `id`/`entryPoint`/`spawns`, and `Config.Impounds` with `getOutPoint`. `Config.DrawDistance`, `Config.Markers` and the old `EntryPoint`/`SpawnPoint` keys are gone.
- **`no_money` at the impound**: the player has neither enough cash nor enough bank money for the fee.
- **`wrong_garage`**: `restrictToGarage` is on and the vehicle is parked elsewhere.
- **`model_mismatch` or `plate_mismatch` when storing**: the vehicle entity does not match the owned record (for example after a plate change by another script).
- **Every vehicle impounded after restart**: expected, see the FAQ below.

## Related [#related]

- [esx_vehicleshop](/docs/addons/esx_vehicleshop)
- [esx_property](/docs/addons/esx_property)
- [Database schema](/docs/guides/database-schema)
- [oxmysql](/docs/guides/oxmysql)
