FiveM AI Script Generator
Describe the script. Get the whole resource.
Write what your server needs in plain English. The AI script maker writes the fxmanifest, client, server and config files, plus the NUI when you ask for a menu. It works from the real framework code, so the functions it calls exist.
250 free credits · a new script costs about 60 · no card needed
New project · Qbox- Read framework code from qbx_vehicleshop, qbx_vehiclekeys, ox_lib
- Looked up “Qbox vehicle spawn server callback CreateVehicle network vehicle keys export”
- Looked up “ox_target addLocalEntity onSelect Qbox NPC”
- Looked up “Qbox AddMoney RemoveMoney GetMoney ox_lib callback”
- fxmanifest.lua28 lines
- config/main.lua33 lines
- client/main.lua94 lines
- server/main.lua141 lines
- README.md25 lines
- Done: 5 files, 321 lines
A real run from October 2, 2026, 74 seconds, replayed faster.
UIs it built from one sentence
Ask for a menu and you get the html, css and js with it. We sent twelve UI requests on October 2 and kept the seven that looked best. The layouts are untouched; the names and numbers are sample data we sent in, as the game would.
Banking appQBCore · 3 min 13 s · 8 files“A banking app NUI at the bank NPCs: show balance, deposit, withdraw, transfer money to another player by server ID, and a transaction history list.”
Police MDTQbox · 3 min 21 s · 9 files“A police MDT tablet opened with /mdt for the police job: search citizens by name, show their licenses and warrants, and write incident reports saved to the database.”
Vehicle shopQbox · 2 min 20 s · 8 files“A vehicle shop with a nice NUI: browse cars by category, show stats like top speed, acceleration and braking, the price, a color picker and a test drive button. Pay with bank money.”
“A modern loading screen with the server logo, rotating gameplay tips, a progress bar and a music on/off button.”
PhoneQBCore · 3 min 11 s · 8 files“A phone-style NUI opened with F1 with three apps: contacts, messages between players, and a bank balance view.”
Fuel stationQbox · 2 min 39 s · 7 files“A fuel station NUI: pick how many liters, see the price update live, choose cash or bank, and a progress animation while refueling.”
Crafting benchQBCore · 2 min 37 s · 7 files“A crafting bench with a polished NUI: a list of recipes with the items each one needs, a progress bar while crafting, using qb-inventory items.”
Three scripts, straight from the generator
We sent three requests on October 2 and kept what came back. One prompt each, no follow-up, nothing edited. The slowest took 2 min 19 s, the one with a full shop UI.
Make a police duty script: /clockin and /clockout for the police job. While on duty pay $150 to the bank every 15 minutes and show a notify. Only works near the Mission Row PD.
- Time
- 48 s
- Files
- 5
- Lines
- 190
What to look for
- Job and distance are checked on the server, so the event cannot be fired from across the map.
- Pays with
Player.Functions.AddMoney("bank", amount, reason), the call qb-core actually exposes. - Clears duty when a player drops and when the resource stops.
Files it wrote
- fxmanifest.lua24 lines
- config.lua15 lines
- client/main.lua19 lines
- server/main.lua109 lines
- README.md23 lines
CreateThread(function()while true doWait(Config.PaymentInterval)for playerId in pairs(activeDuty) dolocal Player = QBCore.Functions.GetPlayer(playerId)if not Player thenactiveDuty[playerId] = nilelseif Player.PlayerData.job.name ~= Config.PoliceJob or not Player.PlayerData.job.onduty thenactiveDuty[playerId] = nilelsePlayer.Functions.AddMoney("bank", Config.PaymentAmount, "police-duty-pay")notifyPlayer(playerId, ("You received $%d for 15 minutes of police duty"):format(Config.PaymentAmount), "success")endendendend)Show the whole server/main.lua (109 lines)
local QBCore = exports["qb-core"]:GetCoreObject()local activeDuty = {}--- Check whether a player's ped is within the Mission Row Police Department clock-in area.--- @param playerId number The server player ID.--- @return boolean isNearby Whether the player is close enough to the station.local function isNearMissionRow(playerId)local playerPed = GetPlayerPed(playerId)if playerPed == 0 thenreturn falseendlocal playerCoords = GetEntityCoords(playerPed)return #(playerCoords - Config.MissionRow.coords) <= Config.MissionRow.radiusend--- Send a notification to a player through the QBCore client notification system.--- @param playerId number The server player ID.--- @param message string The notification message.--- @param notificationType string The QBCore notification type.local function notifyPlayer(playerId, message, notificationType)TriggerClientEvent("policeduty:client:notify", playerId, message, notificationType)end--- Set a police player's duty state after validating their job and station distance.--- @param playerId number The server player ID.--- @param dutyState boolean The requested duty state.local function setDuty(playerId, dutyState)local Player = QBCore.Functions.GetPlayer(playerId)if not Player thenreturnendif Player.PlayerData.job.name ~= Config.PoliceJob thennotifyPlayer(playerId, "Only police officers can use the duty commands", "error")returnendif not isNearMissionRow(playerId) thennotifyPlayer(playerId, "You must be at Mission Row PD to clock in or out", "error")returnendif dutyState thenif activeDuty[playerId] and Player.PlayerData.job.onduty thennotifyPlayer(playerId, "You are already clocked in", "primary")returnendPlayer.Functions.SetJobDuty(true)activeDuty[playerId] = truenotifyPlayer(playerId, "You are now on duty", "success")returnendif not activeDuty[playerId] and not Player.PlayerData.job.onduty thennotifyPlayer(playerId, "You are already clocked out", "primary")returnendPlayer.Functions.SetJobDuty(false)activeDuty[playerId] = nilnotifyPlayer(playerId, "You are now off duty", "success")endRegisterNetEvent("policeduty:server:setDuty", function(dutyState)local playerId = sourceif type(dutyState) ~= "boolean" thenreturnendsetDuty(playerId, dutyState)end)CreateThread(function()while true doWait(Config.PaymentInterval)for playerId in pairs(activeDuty) dolocal Player = QBCore.Functions.GetPlayer(playerId)if not Player thenactiveDuty[playerId] = nilelseif Player.PlayerData.job.name ~= Config.PoliceJob or not Player.PlayerData.job.onduty thenactiveDuty[playerId] = nilelsePlayer.Functions.AddMoney("bank", Config.PaymentAmount, "police-duty-pay")notifyPlayer(playerId, ("You received $%d for 15 minutes of police duty"):format(Config.PaymentAmount), "success")endendendend)AddEventHandler("playerDropped", function()activeDuty[source] = nilend)AddEventHandler("onResourceStop", function(resourceName)if resourceName ~= GetCurrentResourceName() thenreturnendfor playerId in pairs(activeDuty) dolocal Player = QBCore.Functions.GetPlayer(playerId)if Player and Player.PlayerData.job.name == Config.PoliceJob and Player.PlayerData.job.onduty thenPlayer.Functions.SetJobDuty(false)endendend)Car rental at the airport: an NPC with ox_target, an ox_lib menu with 3 cars and prices, pay with cash, spawn the car with keys and delete it after 30 minutes.
- Time
- 1 min 14 s
- Files
- 5
- Lines
- 321
What to look for
- Looked up three things in the docs while writing, listed below.
- Uses the Qbox exports (
exports.qbx_core,qbx.spawnVehicle) and loads@qbx_core/modules/lib.luain the manifest. - Refunds the cash if the car cannot be spawned.
Looked up while writing
- “Qbox vehicle spawn server callback CreateVehicle network vehicle keys export”
- “ox_target addLocalEntity onSelect Qbox NPC”
- “Qbox AddMoney RemoveMoney GetMoney ox_lib callback”
Files it wrote
- fxmanifest.lua28 lines
- config/main.lua33 lines
- client/main.lua94 lines
- server/main.lua141 lines
- README.md25 lines
local cash = exports.qbx_core:GetMoney(source, "cash")if type(cash) ~= "number" or cash < rental.price thenreturn refuseRental(source, ("You need $%d in cash."):format(rental.price))endlocal removed = exports.qbx_core:RemoveMoney(source, "cash", rental.price, "airport-rental")if not removed thenreturn refuseRental(source, "Your cash payment could not be processed.")endlocal spawned, netId, vehicle = pcall(qbx.spawnVehicle, {model = rental.model,spawnSource = Config.spawn,warp = GetPlayerPed(source)})if not spawned or not netId or netId == 0 or not vehicle or vehicle == 0 thenexports.qbx_core:AddMoney(source, "cash", rental.price, "airport-rental-refund")return refuseRental(source, "The rental vehicle could not be spawned; your cash was refunded.")endlocal plate = createRentalPlate()SetVehicleNumberPlateText(vehicle, plate)exports.qbx_vehiclekeys:GiveKeys(source, vehicle)Show the whole server/main.lua (141 lines)
local activeRentals = {}local rentalSequence = 0local RENTAL_DURATION_MS <const> = Config.rentalDurationlocal RESOURCE_PREFIX <const> = "[airport-rental]"--- Returns a configured rental option by its public identifier.--- @param rentalId string The rental identifier received from the client.--- @return table|nil rental The matching rental configuration, if found.local function getRental(rentalId)if type(rentalId) ~= "string" then return nil endfor _, rental in ipairs(Config.rentals) doif rental.id == rentalId thenreturn rentalendendreturn nilend--- Refuses a rental request and logs the server-side reason.--- @param source number The requesting player's server ID.--- @param reason string The refusal reason shown to the player.--- @return table result A failed callback result.local function refuseRental(source, reason)print(("%s Rental refused for %s: %s"):format(RESOURCE_PREFIX, tostring(source), reason))return {ok = false,error = reason}end--- Generates a short, unique-looking rental plate for the spawned vehicle.--- @return string plate The plate text assigned to the rental vehicle.local function createRentalPlate()rentalSequence = rentalSequence + 1local suffix = (rentalSequence % 10000)return ("%s%04d"):format(Config.platePrefix, suffix)end--- Removes an existing rental record and its vehicle entity.--- @param source number The player's server ID.--- @param notifyPlayer boolean Whether to notify the player about expiration.local function removeRental(source, notifyPlayer)local rental = activeRentals[source]if not rental then return endif rental.vehicle and DoesEntityExist(rental.vehicle) thenDeleteEntity(rental.vehicle)endactiveRentals[source] = nilif notifyPlayer thenTriggerClientEvent("airport-rental:client:rentalExpired", source)endend--- Processes a cash rental, spawns the selected vehicle, and assigns its keys.--- @param source number The requesting player's server ID.--- @param rentalId string The configured rental identifier.--- @return table result The server-confirmed rental result.lib.callback.register("airport-rental:server:rentVehicle", function(source, rentalId)local player = exports.qbx_core:GetPlayer(source)if not player thenreturn refuseRental(source, "Your character is not ready yet.")endlocal rental = getRental(rentalId)if not rental thenreturn refuseRental(source, "That vehicle is not available.")endlocal existingRental = activeRentals[source]if existingRental thenif existingRental.vehicle and DoesEntityExist(existingRental.vehicle) thenreturn refuseRental(source, "You already have an active rental.")endactiveRentals[source] = nilendlocal cash = exports.qbx_core:GetMoney(source, "cash")if type(cash) ~= "number" or cash < rental.price thenreturn refuseRental(source, ("You need $%d in cash."):format(rental.price))endlocal removed = exports.qbx_core:RemoveMoney(source, "cash", rental.price, "airport-rental")if not removed thenreturn refuseRental(source, "Your cash payment could not be processed.")endlocal spawned, netId, vehicle = pcall(qbx.spawnVehicle, {model = rental.model,spawnSource = Config.spawn,warp = GetPlayerPed(source)})if not spawned or not netId or netId == 0 or not vehicle or vehicle == 0 thenexports.qbx_core:AddMoney(source, "cash", rental.price, "airport-rental-refund")return refuseRental(source, "The rental vehicle could not be spawned; your cash was refunded.")endlocal plate = createRentalPlate()SetVehicleNumberPlateText(vehicle, plate)exports.qbx_vehiclekeys:GiveKeys(source, vehicle)activeRentals[source] = {netId = netId,vehicle = vehicle}CreateThread(function()Wait(RENTAL_DURATION_MS)local currentRental = activeRentals[source]if not currentRental or currentRental.vehicle ~= vehicle then return endremoveRental(source, true)end)return {ok = true,netId = netId,plate = plate,duration = RENTAL_DURATION_MS}end)AddEventHandler("playerDropped", function()removeRental(source, false)end)AddEventHandler("onResourceStop", function(resourceName)if resourceName ~= GetCurrentResourceName() then return endfor playerSource in pairs(activeRentals) doremoveRental(playerSource, false)endend)A 24/7 store with a clean NUI shop: list items with prices, a cart, buy with cash and add the items with ox_inventory. Open it with a marker and E.
- Time
- 2 min 19 s
- Files
- 8
- Lines
- 729
What to look for
- Prices come from the server config, never from the browser, so a player cannot edit the cart total.
- If an item does not fit into the inventory, the items already added are taken back and the cash is refunded.
- Ships the shop UI as html, css and js next to the Lua.
Files it wrote
- fxmanifest.lua34 lines
- config/main.lua76 lines
- client/main.lua131 lines
- server/main.lua124 lines
- html/ui.html80 lines
- html/style.css14 lines
- html/script.js235 lines
- README.md35 lines
total = total + (item.price * quantity)endif total <= 0 thenreturn rejectPurchase(source, callback, "Your cart total is invalid.")endif xPlayer.getMoney() < total thenreturn rejectPurchase(source, callback, ("You need $%s in cash."):format(total))endxPlayer.removeMoney(total)local addedItems = {}for _, itemName in ipairs(orderedNames) dolocal quantity = requestedItems[itemName]local success = exports.ox_inventory:AddItem(source, itemName, quantity)if not success thenfor _, addedItem in ipairs(addedItems) doexports.ox_inventory:RemoveItem(source, addedItem.name, addedItem.quantity)endxPlayer.addMoney(total)return rejectPurchase(source, callback, "There is not enough room in your inventory.")endShow the whole server/main.lua (124 lines)
local itemByName = {}for _, item in ipairs(Config.Items) doitemByName[item.name] = itemend--- Reject a purchase request and log the server-authoritative reason.--- @param source number The requesting player's server ID.--- @param callback function The ESX callback to resolve.--- @param message string The safe message sent to the player.--- @return table result The rejected callback result.local function rejectPurchase(source, callback, message)print(("[247store] Purchase refused for player %s: %s"):format(source, message))return callback({ok = false,error = message})end--- Check that a player is physically close enough to one of the store locations.--- @param source number The player's server ID.--- @return boolean isNearStore Whether the player is near a configured store.local function isNearStore(source)local playerPed = GetPlayerPed(source)if playerPed == 0 then return false endlocal playerCoords = GetEntityCoords(playerPed)for _, store in ipairs(Config.StoreLocations) doif #(playerCoords - store.coords) <= Config.InteractionDistance + 3.0 thenreturn trueendendreturn falseendESX.RegisterServerCallback("247store:server:purchase", function(source, callback, payload)local xPlayer = ESX.GetPlayerFromId(source)if not xPlayer thenreturn rejectPurchase(source, callback, "Your player session is not ready.")endif not isNearStore(source) thenreturn rejectPurchase(source, callback, "You are too far from the shop.")endif type(payload) ~= "table" or type(payload.cart) ~= "table" thenreturn rejectPurchase(source, callback, "Your cart is invalid.")endif #payload.cart < 1 or #payload.cart > Config.MaxCartLines thenreturn rejectPurchase(source, callback, "Your cart has an invalid number of lines.")endlocal requestedItems = {}local orderedNames = {}local total = 0for _, entry in ipairs(payload.cart) doif type(entry) ~= "table" thenreturn rejectPurchase(source, callback, "Your cart contains invalid data.")endlocal itemName = entry.namelocal quantity = tonumber(entry.quantity)local item = itemByName[itemName]if not item or not quantity or quantity ~= math.floor(quantity) or quantity < 1 thenreturn rejectPurchase(source, callback, "Your cart contains an invalid item.")endif not requestedItems[itemName] thenrequestedItems[itemName] = 0orderedNames[#orderedNames + 1] = itemNameendrequestedItems[itemName] = requestedItems[itemName] + quantitylocal maxQuantity = item.maxQuantity or Config.MaxQuantityPerItemif requestedItems[itemName] > maxQuantity thenreturn rejectPurchase(source, callback, ("You can only buy %s of %s at once."):format(maxQuantity, item.label))endtotal = total + (item.price * quantity)endif total <= 0 thenreturn rejectPurchase(source, callback, "Your cart total is invalid.")endif xPlayer.getMoney() < total thenreturn rejectPurchase(source, callback, ("You need $%s in cash."):format(total))endxPlayer.removeMoney(total)local addedItems = {}for _, itemName in ipairs(orderedNames) dolocal quantity = requestedItems[itemName]local success = exports.ox_inventory:AddItem(source, itemName, quantity)if not success thenfor _, addedItem in ipairs(addedItems) doexports.ox_inventory:RemoveItem(source, addedItem.name, addedItem.quantity)endxPlayer.addMoney(total)return rejectPurchase(source, callback, "There is not enough room in your inventory.")endaddedItems[#addedItems + 1] = {name = itemName,quantity = quantity}endxPlayer.showNotification(("Purchase complete: $%s"):format(total))callback({ok = true,total = total,items = requestedItems})end)
How it works
The same three steps produced every example above.
- 01
Describe the script
Write it the way you would explain it to another developer: what starts it, what it pays or gives, and where it works. Pick your framework when you create the project.
- 02
It reads, looks up and writes
It starts from matching code in 91 framework and community repositories, looks up any call it is unsure about, and writes every file the resource needs.
- 03
Test it and keep talking
Download the folder or push it to GitHub. If the console shows an error, paste it into the chat and it fixes the file. A round of changes costs about 100 credits.
Why the code calls functions that exist
A general chat model has seen little FiveM code. Ask it to pay a QBCore player and it can write a call that sounds right but is not in qb-core. Here is the difference on the same task.
QBCore.Functions.AddMoney(source, "bank", 150)qb-core has no QBCore.Functions.AddMoney. The server prints “attempt to call a nil value” and nobody gets paid.
Player.Functions.AddMoney("bank", Config.PaymentAmount, "police-duty-pay")The money call qb-core defines on the player object in server/player.lua, with the reason that ends up in the logs.
It starts from real code
24,133 passages from 91 repositories: qb-core, qbx_core, esx-legacy, ox_lib, oxmysql, ox_inventory, ox_target, the FiveM natives and widely used community resources. The parts that match your request go in before the first line is written.
It looks things up while writing
When it is not sure how a call works, it searches that index before it writes the line. The Qbox example above did this three times, for vehicle keys, ox_target and money.
It checks the UI against the Lua
For resources with a NUI, a checker compares what the Lua sends with what the JavaScript handles, and sends mismatches back to fix. Run over 90 days of past projects, it found a mismatch in 294 of 2,291 NUI resources. Since October 1 it runs on every generation.
Written for your framework
Pick the framework when you start a project. The generator then uses that framework's calls and never mixes in another one.
QBCoreQBCore.Functions.GetPlayer, Player.Functions.AddMoneyQBCore script generator
Qboxexports.qbx_core:GetPlayer, qbx.spawnVehicleQbox script generator
ESXESX.GetPlayerFromId, xPlayer.removeMoneyESX script generator
ox_liblib.callback.register, lib.registerContextox_lib script generatorHow to write a prompt that works
The better the request, the fewer rounds you pay for. Four habits make the biggest difference.
Say what starts it
A command, a key, an ox_target option, a marker, using an item. One line saves a round of questions.
Give the numbers
Prices, payouts, timers, distances, job names. Anything you leave open, the AI has to guess.
Name the resources you run
ox_target or qb-target, ox_inventory or qb-inventory, ox_lib notify. It then writes for your server, not a generic one.
One resource per request
Get the core working first, then add features in follow-up messages. Each change is smaller and easier to check.
make a police script
Make a police duty script: /clockin and /clockout for the police job. While on duty pay $150 to the bank every 15 minutes and show a notify. Only works near the Mission Row PD.
More on this in how to prompt AI for FiveM Lua.
SwisserAI or ChatGPT for FiveM scripts?
General chat models are strong coders. For FiveM they miss the framework source, so you check every call yourself. If FiveM is only a small part of your work, that can be fine.
| SwisserAI | ChatGPT or Claude (chat) | |
|---|---|---|
| Reads qb-core, qbx_core and esx-legacy source while writing | Yes | Only what you paste in |
| Writes the whole resource (manifest, client, server, config, NUI) | Yes, as files | Mostly code blocks to copy |
| Looks up unknown FiveM calls before using them | Yes, in the framework index | Only if you paste the docs |
| Checks the NUI messages against the Lua | Yes | No |
| General coding outside FiveM | Fine | Better |
| Price | 250 free credits, plans from $9.99 | Free plan, paid plans |
Eight FiveM AI tools side by side: the 2026 comparison.
What a script costs
You pay in credits for what the AI actually reads and writes, so a small script costs less than a large one. The first two rows are medians from the last 30 days.
| What | Credits | Detail |
|---|---|---|
| A new script | about 60 | Median of the last 30 days. Half of all new scripts cost between 45 and 80. |
| A round of changes | about 100 | Median 97. Bigger projects cost more, because the AI reads more files. |
| Free start | 250 | Enough for a first script and about two rounds of changes. Use them within 14 days. |
| Hobby, $9.99 a month | 7,500 | About 48 new scripts with one round of changes each. |
| Pro, $19.99 a month | 20,000 | About 129 scripts with one round of changes each. |
Credits work across every tool here, from scripts to 3D models. See all plans
Frequently Asked Questions
You can start free. A new account gets 250 credits, enough for a first script and about two rounds of changes, plus 25 credits a day when you log in. Plans start at $9.99 a month for 7,500 credits. No card is needed to start.
QBCore, Qbox, ESX (Legacy), ox_lib and vRP. It uses the calls of the framework you pick: QBCore.Functions and Player.Functions for QBCore, exports.qbx_core for Qbox, ESX.GetPlayerFromId and xPlayer for ESX. It does not mix them.
Yes. Upload the resource or paste the file, then paste the console error into the chat. It reads the files, finds the cause and edits the file. You can also import a project from GitHub.
Yes. Ask for a menu and it writes the html, css and js next to the Lua, as in the ESX shop example above. A checker then makes sure the messages between the Lua and the UI match.
Yes. There is a SwisserAI extension for VS Code, and an OpenAI-compatible API at ai.swisser.dev/v1 that Cursor, Continue and Roo Code can use.
You do. Our terms say you keep ownership of the code you generate, and we claim no rights to it.
Often used next
Tools that pair with AI Script Generator on a FiveM server.
Generate your first FiveM script free
250 credits to start, no card. Describe the script, pick your framework, test it on your server.