FiveM & QBCore · Practical guide

How to Add a Reporter Job to Your QBCore FiveM Server

A reporter job lets players document server events, interview other players and broadcast news. Great for adding depth to roleplay servers.

Stellar AI · Updated 8 September 2026 · 6 min read

A practical guide to how to add a reporter job to your qbcore fivem server, with implementation decisions, validation steps, and security considerations for a production-minded project.

Introduction

This guide walks you through adding a robust, secure Reporter job to a QBCore FiveM server. It covers architecture, configuration, server-side enforcement, client integration, optional ox_lib/qb-target UX, testing, deployment, and maintenance. The goal is a production-ready job that protects server authority, validates all client requests, and integrates cleanly with common FiveM/QBCore toolchains. If you want a companion web or management workflow for roles and content, consider the management app available at https://trystellarai.com/app.

Architecture and design considerations

Keep a clear separation between authoritative server state and client presentation. The server is the single source of truth for player jobs, grades, permissions, and payroll. Clients only request actions (for UX) and receive updates or confirmations. Your architecture should include:

  • Persistent job definitions in the server config (shared/jobs.lua or a dedicated resource).
  • Server-side handlers for job assignment and job-based actions.
  • Client-side UI and interaction hooks (qb-target, ox_lib, or native menus) that call server endpoints.
  • Audit logs and basic telemetry for job changes and abuse detection.

Job definition and server configuration

Define the Reporter job centrally so payroll, duty toggles, and grade names are consistent. For QBCore, jobs are typically declared in a shared table. Example pattern (adapt to your qb-core version):

-- shared/jobs.lua (extract)
['reporter'] = {
  label = "Reporter",
  defaultDuty = true,
  grades = {
    ['0'] = {name = "Intern", payment = 50},
    ['1'] = {name = "Reporter", payment = 80},
    ['2'] = {name = "Editor", payment = 120},
  }
}

Keep this in the same format your core expects. Avoid duplicating job logic in multiple places; import the job table where needed.

Server-side implementation and security

All sensitive actions must be validated and executed on the server. Never trust client payloads for role changes, money transfers, or access checks. Typical server responsibilities for the Reporter job:

  • Assign job and grade after admin approval or in-game workflow.
  • Validate job-related actions (e.g., publishing an article, creating billing items) against allowed job/grade.
  • Record job changes for auditing and rollback.

Server-side Lua pseudocode demonstrates the pattern of authoritative handling with whitelists and source validation:

local QBCore = exports['qb-core']:GetCoreObject()

local allowedJobs = { reporter = true }
local allowedGrades = { ["0"]=true, ["1"]=true, ["2"]=true }

RegisterNetEvent('reporter:server:SetJob', function(jobName, grade)
  local src = source
  local Player = QBCore.Functions.GetPlayer(src)
  if not Player then return end

  -- Validate incoming values against server-side whitelist
  if not allowedJobs[jobName] or not allowedGrades[tostring(grade)] then
    -- Optional: increment a suspicious activity counter for src
    return
  end

  -- Authoritative set on server only
  Player.Functions.SetJob(jobName, tonumber(grade))
  -- Confirm to the client
  TriggerClientEvent('QBCore:Notify', src, 'Job updated to '..jobName, 'success')
end)

Note: adapt Player.Functions.SetJob to match your core's API. The key point is source-derived Player lookup, whitelist validation, and server-executed mutation.

Payment and payroll

Handle payment calculations on the server and store any configurable pay rates in a server-side table. If you implement on-duty bonuses, ensure the duty toggle is server controlled. Never allow the client to send raw cash amounts for transactions without server verification.

Client integration and UX

Client code should provide an intuitive way for reporter players to access job features (writing, submitting, pickups, vehicle spawns). Use interaction systems already in your stack:

  • qb-target for in-world interaction points.
  • ox_lib or qb-menu for contextual menus.
  • client-side UI libraries for text entry and article previews.

Example qb-target registration (client):

exports['qb-target']:AddBoxZone("reporter_desk", vector3(200.0, -900.0, 30.7), 1.2, 1.0, {
  name = "reporter_desk",
  heading = 340,
  debugPoly = false,
  minZ = 29.7,
  maxZ = 31.7
}, {
  options = {
    { type = "client", event = "reporter:client:OpenDeskMenu", icon = "fas fa-pen", label = "Reporter Desk" }
  },
  distance = 2.5
})

When the client triggers "reporter:client:OpenDeskMenu", the menu should gather text input locally but send a summarized, validated payload to the server for publishing.

Data flow and validation

Client sends minimal descriptive data (e.g., title, body hash, attachments list) to the server. The server validates the format, length, and any blacklist/whitelist rules before storing or broadcasting. Reject or sanitize anything that fails validation.

Integration notes: ox_lib, ESX, and compatibility

If you use ox_lib for UI helpers or ESX instead of QBCore, adapt the patterns but keep the core principles: server authority, whitelist validation, and consistent job definitions. For ESX the APIs differ; use ESX.GetPlayerFromId(source) and ESX.SetJob historically, but always consult the versioned docs for exact function names.

When integrating third-party libs (ox_lib, qb-target, qb-menu), treat them as presentation layers. Your server must not rely on these libs for security checks.

Roblox server-authoritative note (if porting patterns)

If you are translating concepts to Roblox, the same server-authority rules apply. Use RemoteEvents only for client-to-server requests and validate every request on the server. For persistent player state, use DataStores and handle failures robustly:

-- Luau example: server handler and DataStore retry
local DataStoreService = game:GetService("DataStoreService")
local ReporterStore = DataStoreService:GetDataStore("ReporterJobs")

local function SavePlayerReport(userId, reportData)
  local success, err
  local attempts = 0
  repeat
    attempts = attempts + 1
    success, err = pcall(function()
      ReporterStore:SetAsync(tostring(userId), reportData)
    end)
    if not success then
      wait(2 ^ attempts) -- exponential backoff
    end
  until success or attempts >= 5
  return success, err
end

remoteEvent.OnServerEvent:Connect(function(player, payload)
  -- Validate payload server-side, then call SavePlayerReport
end)

Always pcall DataStore calls, implement retries with backoff, and consider local caching to reduce rate-limit exposure.

Testing, QA, and security checks

Test in a staging environment that mirrors production. Recommended test matrix:

  • Assign and revoke Reporter role via admin tools and in-game flow.
  • Attempt unauthorized actions from a non-reporter client (should fail server-side).
  • Simulate malformed client payloads and measure server validation behavior.
  • Stress test payroll and publishing to detect race conditions.

Use logs and a lightweight audit table to record job changes, IPs, and timestamps for post-incident investigation. For further reading on deployment and maintenance, check the operator blog at https://trystellarai.com/blog.

Deployment and maintenance

Deployment checklist:

Task Confirm
Job definition loaded from central config Yes/No
Server-side validation for all job actions Yes/No
Admin approval or safe auto-assign flow Yes/No
Backup/rollback plan for shared/jobs.lua Yes/No
Monitoring and audit logging enabled Yes/No
Management console or app connected (optional) Use the management app: https://trystellarai.com/app

For maintenance, keep your core and libs up to date in a staging branch. Test job-related changes in staging before pushing to production. Maintain a changelog that records job schema changes so migration scripts can be applied when players remain online across updates.

Operational security and abuse mitigation

Implement the following mitigations:

  • Rate-limit job-change and publish events per user and per IP/source.
  • Whitelist allowable job names and grades server-side.
  • Log failures and invalid attempts for later review.
  • Use server-side anti-cheat hooks if available to detect tampering.

Audit logs should include: timestamp, player identifier, old job, new job, invoking source (admin or in-game process), and proof of approval when relevant.

Checklist: Practical steps to add Reporter job

  1. Define the Reporter job and grades in shared job config.
  2. Implement server-side endpoints to set and validate job changes.
  3. Register interaction points (qb-target) and menus (ox_lib/qb-menu) for reporters.
  4. Ensure all client requests are validated and authoritatively executed on the server.
  5. Implement payment and payroll server-side, not client-side.
  6. Add audit logging, rate-limiting, and monitoring.
  7. Test thoroughly in staging with edge-case client payloads and simultaneous transactions.
  8. Rollback plan and backup of job config before deploying to production.

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 →