Developer API
Integrate RedM Reports with your own resources.
Exports use the folder name
The resource folder is pc_report, so exports are exports.pc_report:... — even though the manifest name is pc_reports. If you rename the folder, every export below changes with it.
Server exports
-- Read
local ticket = exports.pc_report:GetReport(id) -- ticket table or nil
local open = exports.pc_report:GetOpenReports() -- array of tickets
local byPlayer = exports.pc_report:GetReportsByLicense(license) -- array of tickets
local staff = exports.pc_report:IsStaff(src) -- boolean
-- Reports
exports.pc_report:SetPriority(id, 'urgent') -- returns true/false
-- Player notes
exports.pc_report:AddPlayerNote(license, 'Watch this one', 'System')
local notes = exports.pc_report:GetPlayerNotes(license) -- array
-- Blocks
exports.pc_report:BlockPlayer(license, 3600, 'Report abuse', 'System') -- 0 seconds = permanent
exports.pc_report:UnblockPlayer(license)
local blocked = exports.pc_report:IsPlayerBlocked(license) -- boolean
-- Stats
local one = exports.pc_report:GetStaffStats(staffLicense) -- { avgRating, reviews }
local all = exports.pc_report:GetStaffStats() -- array by staff_nameClient exports
exports.pc_report:OpenReports() -- open the menu (if the player is registered)
local open = exports.pc_report:IsOpen() -- boolean
exports.pc_report:OpenReport(id) -- open the menu and jump to a reportServer events
Listen with AddEventHandler in any server script.
| Event | Args |
|---|---|
pcr:reportCreated | (ticket) |
pcr:reportClaimed | (ticket, staffLicense) |
pcr:reportUnclaimed | (ticket, staffLicense) |
pcr:reportClosed | (ticket) |
pcr:reportReopened | (ticket) |
pcr:reportDeleted | (reportId) |
pcr:reportPriorityChanged | (ticket, priority) |
pcr:playerBlocked | (license, expiresAt) |
pcr:playerUnblocked | (license) |
pcr:reviewSubmitted | (review) |
pcr:mediaUploaded | (reportId, mediaItem) |
AddEventHandler('pcr:reportCreated', function(ticket)
print(('New report #%s: %s'):format(ticket.id, ticket.title))
end)The ticket table
Fields available on a ticket:
id, title, description, category, status, priority,
player_info { id, name, license },
reported_player, admin_info,
created_at, closed_at,
tags, anonymous, escalated, resolution,
locationCreated { x, y, z },
nearby_playersAnonymous tickets
On an anonymous report, player_info.name and player_info.id are masked — but license is retained server-side so your own tooling still works. Do not echo the license anywhere a player can see it.
Integration hooks
Wire kick and ban into your existing admin or ban system without editing the core:
Config.Integrations.Ban = function(targetSrc, reason, staffSrc)
exports['your_ban_system']:BanPlayer(targetSrc, reason, staffSrc)
endCustom report actions
Add a tool button that runs your own server-side logic:
Config.ReportActions[#Config.ReportActions + 1] = {
id = 'jailtime', label = 'Jail 30m', icon = 'fa-clock',
targets = { 'reported' }, permission = 'punish',
handler = function(staffSrc, targetSrc, ticket)
exports['your_jail']:Send(targetSrc, 30)
end,
}targets controls which party the button can act on — 'reporter', 'reported', or both. permission is a capability key; pick an existing one rather than inventing a new string, or the gate will never pass.
Handlers run on the server
handler executes server-side, and targetSrc is resolved by the resource — never by the client. Do not add a client-supplied ID into a handler and trust it.