# Developer > Developer API reference for ML Vehicle Persistence Category: VEHICLE PERSISTENCE · Source: https://miciomods.it/docs/ml-vehicle-persistence-developer · Last updated: 2026-07-28 ## Overview Other resources integrate through server exports and the `open/server.lua` hooks. Exports coordinate with garages, tow jobs, mechanics and dealerships so a vehicle is never saved or deleted twice. The `open/` folder holds the ownership and garage-state logic, which the server owner adapts to their framework. ## Server Exports ### Coordination - `DoNotSave(plateOrNetId, reason?)`: Exclude a vehicle from persistence and remove it if already tracked. Accepts a plate, a network id, or an entity handle - `ExcludeFromPersistence(plateOrNetId, reason?)`: Alias of `DoNotSave` - `RemoveExclusion(plate)`: Clear an exclusion - `IsExcludedFromPersistence(plate)`: Returns `true` if excluded - `LockVehicle(plate)`: Temporarily block persistence (during towing, mechanic work). Auto-expires after 5 minutes - `UnlockVehicle(plate)`: Release a lock - `IsVehicleLocked(plate)`: Returns `true` if locked ### Vehicle Control - `PurgeVehicle(plateOrNetId, keepInWorld?)`: Remove a vehicle from persistence. Despawns it unless `keepInWorld` is `true` - `UpdateVehicle(plateOrNetId)`: Force a save and refresh the vehicle's stored properties from its owner client - `ClaimVehicle(plateOrNetId, src)`: Register a vehicle as persistent and owned by the player `src` - `TransferPersistedVehicle(plate, newOwner)`: Reassign a persisted vehicle to another owner identifier - `PurgeOwner(ownerId)`: Remove every persisted vehicle belonging to an identifier. Returns the count removed - `SetVehiclePlate(plateOrNetId, newPlate)`: Move persistence to a new plate after a plate change - `RemovePersistedVehicle(plate)`: Drop a vehicle from persistence, keeping it in the world ### Pause - `PausePersistence(state)`: Pause or resume all saving and claiming. Returns the new state - `IsPersistencePaused()`: Returns the current pause state ### Maintenance - `RunCleanup()`: Run the `Config.Cleanup` sweep once, outside its schedule. Returns the number of vehicles removed ### Query - `IsVehiclePersisted(plate)`: Returns `true` if the plate is persisted - `GetWorldVehicles()`: Returns every tracked vehicle - `GetVehicleStatus(plate)`: Returns `{ isPersisted, isExcluded, isLocked, exclusion, lock, vehicle }` - `GetVehicleCoords(plate)`: Returns the live or saved coordinates of a persisted vehicle. Always the real position, even when the owner's GPS is tampered - `GetVehiclesCoords(plates)`: Returns a map of plate to coordinates ## Client Exports - `PurgeVehicle(plateOrNetId)`: Drop a vehicle from persistence and delete it from the client side. Used by resources that delete vehicles on the client. Returns `true` when the request was accepted ## Deleting Vehicles From Another Resource With `Config.RespawnOnExternalDelete = false` (the default) nothing is needed: a plain `DeleteEntity` or an admin `/dv` drops the vehicle from persistence like every other script expects. With `Config.RespawnOnExternalDelete = true` every delete is undone by the respawn engine unless it is routed through the script. Either call the `PurgeVehicle` export, or add the shipped hook as the **first** script line of the deleting resource: **fxmanifest.lua** ```lua shared_script '@ml_vehicle_persistence/deletehook.lua' ``` > **WARNING:** First line > > The hook replaces the `DeleteEntity` and `DeleteVehicle` natives. A resource that caches them in a local before the hook loads bypasses it, so the `shared_script` line must come before every other script entry. The request is validated server-side: a tracked vehicle is only removed by an admin or by its own owner, and a locked vehicle is never touched. ## Server Handlers Located in `open/server.lua`. ### CanPersist **open/server.lua** ```lua function OpenServer.CanPersist(src, vehicle, plate, owner) return nil end ``` Ownership gate before a vehicle becomes persistent. Return `false` to block, `true` to force-allow for custom ownership systems, `nil` for the default owned-vehicles check. ### CanClaim **open/server.lua** ```lua function OpenServer.CanClaim(src, vehicle, plate) return nil end ``` Gate before a world vehicle is claimed by its first driver. Return `false` to block the claim, for example to require a hotwire item. ### FetchOwnedPlates **open/server.lua** ```lua function OpenServer.FetchOwnedPlates() return {} end ``` Returns an array of plate strings from the ownership database. Claiming never touches these plates. The default reads `player_vehicles` or `owned_vehicles`. ### IsPlateOwned **open/server.lua** ```lua function OpenServer.IsPlateOwned(plate) return false end ``` Authoritative single-plate check, consulted when a plate is missing from the cached registry, such as a car bought seconds earlier. Return `true` if the plate belongs to a player. ### FetchGaragedVehicles **open/server.lua** ```lua function OpenServer.FetchGaragedVehicles(src, owner) return {} end ``` Vehicles the player owns that are sitting in a garage right now, so the GPS item shows them as stored instead of a dead waypoint. Return an array of `{ plate, model?, garage? }`. The `garage` value is shown to the player as-is, so raw ids can be mapped to friendly names here. The default reads `player_vehicles` with `state = 1`. ### OnVehicleRestored **open/server.lua** ```lua function OpenServer.OnVehicleRestored(plate, owner) end ``` Fires for every vehicle put back into the world after a restart. The default marks the vehicle as out of the garage so garage scripts stay consistent. ### ImpoundVehicle **open/server.lua** ```lua function OpenServer.ImpoundVehicle(plate, owner, fee, impoundLot) end ``` Orphan cleanup with `action = 'impound'` routes here. Adapt it to your impound system. ### Lifecycle Hooks **open/server.lua** ```lua function OpenServer.OnVehiclePersisted(plate, owner) end function OpenServer.OnVehicleClaimed(plate, owner, src) end function OpenServer.OnVehicleRemoved(plate, owner, reason) end function OpenServer.OnGpsTampered(plate, owner, src) end function OpenServer.OnGpsInstalled(plate, owner, src) end ``` `OnVehicleRemoved` fires with `reason` one of: `stored`, `excluded`, `destroyed`, `limit`, `entity_removed`, `impounded`, `orphan_deleted`, `owner_purged`, `cleanup`, `admin_remove`, `admin_clear`, `deleted_by_client_hook`, `removed_by_`, `deleted_by_`. `OnGpsTampered` fires when a thief rips a vehicle's GPS out with the scanner item; `OnGpsInstalled` fires when the owner restores live tracking with a module. ## Client Handlers Located in `open/client.lua`. These listen for garage store events and drop the stored vehicle from persistence. Add your garage's event here: **open/client.lua** ```lua function OpenClient.NotifyStored(plate) TriggerServerEvent('ml_vehicle_persistence:server:vehicleStored', plate) end ``` ## Examples **Exclude a mission vehicle** ```lua local netId = NetworkGetNetworkIdFromEntity(vehicle) exports.ml_vehicle_persistence:DoNotSave(netId, 'mission') ``` **Refresh a vehicle after mechanic work** ```lua exports.ml_vehicle_persistence:UpdateVehicle(plate) ``` **Grant a vehicle to a player and persist it** ```lua exports.ml_vehicle_persistence:ClaimVehicle(netId, src) ```