FiveM & QBCore · Practical guide

FiveM Bank Heist Script — Design, Security, and Implementation Guide

A practical, security-first guide to designing and building a FiveM bank heist resource. Covers server authority, QBCore/ESX/ox_lib boundaries, anti-cheat validation, qb-target.

Stellar AI · Updated 8 September 2026 · 5 min read

This guide walks a builder from system design to a secure FiveM bank heist implementation. It emphasises server authority, framework boundaries (QBCore/ESX/ox_lib), target interactions, anti-cheat validation, and installation notes so you can ship a robust resource.

Overview

A bank heist is a complex multiplayer interaction: multiple players coordinate, timed objectives run, inventory items are consumed, doors unlock, police must be notified, and rewards are distributed. Because these actions affect player economy and persistence, authority must live on the server. This guide explains design choices, includes a server-authoritative code example for QBCore, details how to wire qb-target/ox_lib interactions, and provides a practical checklist for deployment.

Design goals

  • Server authority: all state-changing decisions and reward distribution are validated on the server.
  • Minimal client trust: clients can request actions but cannot grant themselves rewards or bypass checks.
  • Framework agnostic patterns: implementable with QBCore, ESX, or ox_lib by swapping small integration layers.
  • Auditability and logging: every heist attempt is logged for moderation and debugging.
  • Rate-limited and auditable: cooldowns and per-player concurrency limits to deter farming.

High-level flow

  1. Player approaches vault and uses qb-target/ox_lib to trigger a heist attempt.
  2. Client fires a safe server event asking to start the heist; server verifies prerequisites (police online, item requirements, cooldowns, job restrictions).
  3. Server reserves the vault (locks it) and notifies all clients to start synchronized animations/timers.
  4. During the heist, server-side timers validate progress; anti-cheat checks watch for impossible state changes.
  5. At completion, server awards loot (cash, items) and logs the transaction; failure triggers cleanup and cooldowns.

Server authority & validation (must-haves)

All critical checks occur on the server. Do not accept client claims about item removals or task completion. Example validations:

  • Check police count server-side with an authoritative player list.
  • Verify and consume required items from the player inventory with framework APIs (QBCore: QBCore.Functions.GetPlayer, ESX: xPlayer).
  • Use database flags or in-memory locks to reserve the vault during an ongoing heist.
  • Use server-side timers and verify heartbeats from clients, but treat them as hints — don’t grant rewards without final server checks.

Common server-side checks

  • Active heist flag: is the vault already in progress?
  • Whitelist/exclusion: is the player allowed to initiate? (jobs, bans)
  • Cooldowns: last completed attempt time per player and per-vault cooldown.
  • Item/skill verification: confirm removal of physical items or craft requirements.

QBCore / ESX / ox_lib boundaries

Design your resource with a small integration layer so that the core logic is framework-agnostic. Provide adapter functions for inventory ops, job checks, and notifications.

  • QBCore: use QBCore.Functions.GetPlayer, player.Functions.RemoveItem, and exports for logging.
  • ESX: use xPlayer methods like xPlayer.removeInventoryItem and server callbacks.
  • ox_lib: use its notification and lib callbacks; for target interactions use ox_target if present.

Client ↔ Server separation and qb-target

Clients only trigger an intent. Use qb-target or ox_target to start the interaction; client code should only display prompts and animations. Example target action: client triggers heist:server:tryStart. The server responds with confirmations or errors.

Example: secure server start (QBCore-style)

-- server.lua (QBCore example)
local QBCore = exports['qb-core']:GetCoreObject()
local activeVaults = {}

RegisterNetEvent('heist:server:tryStart', function(vaultId)
  local src = source
  local Player = QBCore.Functions.GetPlayer(src)
  if not Player then return end

  -- Basic checks
  if activeVaults[vaultId] then
    TriggerClientEvent('heist:client:notify', src, 'Vault already in use')
    return
  end

  -- Police count check (server-side authoritative)
  local policeCount = 0
  for k,v in pairs(QBCore.Functions.GetPlayers()) do
    local p = QBCore.Functions.GetPlayer(v)
    if p and p.PlayerData.job.name == 'police' then policeCount = policeCount + 1 end
  end
  if policeCount < Config.RequiredPolice then
    TriggerClientEvent('heist:client:notify', src, 'Not enough police online')
    return
  end

  -- Verify and remove required item
  if not Player.Functions.RemoveItem('thermite', 1) then
    TriggerClientEvent('heist:client:notify', src, 'You need a thermite')
    return
  end

  -- Reserve vault and start server timer
  activeVaults[vaultId] = {starter = src, startedAt = os.time()}
  TriggerClientEvent('heist:client:started', -1, vaultId)

  -- Example reward after completion (server-only)
  SetTimeout(Config.HeistDuration * 1000, function()
    if not activeVaults[vaultId] then return end
    -- Final validation (player still connected, hasn't been arrested, etc.)
    local starter = QBCore.Functions.GetPlayer(activeVaults[vaultId].starter)
    if starter then
      starter.Functions.AddMoney('cash', math.random(50000,120000))
      exports['oxmysql']:insert('INSERT INTO heist_logs (starter, vault, reward) VALUES (?, ?, ?)', {starter.PlayerData.citizenid, vaultId, 'cash'})
    end
    activeVaults[vaultId] = nil
    TriggerClientEvent('heist:client:finished', -1, vaultId)
  end)
end)

This example demonstrates server-side locking, police count verification, item consumption, delayed reward distribution, and a log insert. Replace the DB call with your chosen MySQL wrapper (oxmysql/ghmattimysql) and adapt the player APIs to ESX if needed.

Anti-cheat and validation strategies

  • Never trust client-sent timers or reward events — always issue rewards from server-side timers or final validation RPCs.
  • Rate-limit start attempts per player and per IP; log repeated failures for moderation review.
  • Use server-side position checks if necessary: confirm the starter is near the vault when finishing.
  • Whitelist network events in your server config; avoid generic global events that can be abused.

Persistence, logging, and economics

Keep a heist_logs table with starter id, participants, timestamp, vault id, and reward. This aids rollback and moderation. Ensure reward values are balanced and server-side configurable via config.lua, and avoid hard-coding large sums in client files.

Deployment checklist

FilePurpose
fxmanifest.luaResource manifest and dependency declarations (qb-core, qb-target, oxmysql)
config.luaAll tunables: police requirement, cooldowns, rewards
server.luaServer authority logic, DB writes, validation
client.luaqb-target/ox_lib interaction, animations, UI
sql/Schema for heist_logs and any persistence

Quick installation notes

  • Declare dependencies in fxmanifest (e.g., dependency 'qb-core').
  • Install SQL schema and verify oxmysql/ghmattimysql connectivity.
  • Tune Config.RequiredPolice and Config.HeistDuration on a test server before live.
  • Test with staff accounts or a private server then roll to staging.

Testing and monitoring

Test heists with multiple clients, ensure cooldowns are enforced, and deliberately attempt invalid flows (missing items, insufficient police) to ensure server responses are robust. Monitor logs for rapid repeat attempts and unexpected DB errors. For team workflows, use the Stellar AI app to sketch the resource's states and file outputs, then iterate on implementation — you can start a new project at https://trystellarai.com/app or open the app later when designing variants at https://trystellarai.com/app.

Further reading

For patterns and community discussions about resource structure and security, check the project writing on the Stellar AI blog: https://trystellarai.com/blog. Also consult core docs for QBCore, ESX, qb-target, and your chosen MySQL wrapper when wiring production code.

Practical launch checklist

  1. Run unit smoke tests on server-side checks: police count, item removal, cooldowns.
  2. Deploy to staging; run multi-client heist stress tests with logging enabled.
  3. Verify DB inserts and ensure no sensitive keys are in code or logs.
  4. Review and set event whitelists; ensure only expected resources can call your server events.
  5. Document admin commands for forced cleanup and logs retrieval.

Build your next system with Stellar AI

Describe one feature, get organized project files, then bring back your errors to keep improving. Start free with no card required. Test generated code in a private development environment before release.

Create your first script free →