Evidence Bridge Guide
How phone evidence reaches your police paperwork — the built-in Forensics layer, the evidence report event, report destinations that attach reports to your own MDT, your own search authorizations, a worked example and testing.
How phone evidence reaches your police paperwork. This guide is for server owners and developers who want extraction reports to land in their own MDT (the police computer where cases live), or who want to decide search authorizations with their own system.
If you run oxide-police, you don't need this guide: its bridge ships with the phone and is on by default.
The Three Layers
| Layer | What it gives you | What you need |
|---|---|---|
| 1. Built in | Seizure, search authorizations, extraction, reports, carrier records and traces in the Forensics app | Nothing. Works on every server |
| 2. Events and exports | Your own code gets every report | A small resource of your own |
| 3. Report destinations | Every report is also attached to a case in your MDT | A few lines in a resource of your own |
Each layer works without the ones below it.
Layer 1: Built In
Police (the jobs in Evidence → Police jobs of /phone settings) who open a phone registered to someone else see its number, who its SIM is registered to and its custody state on the lock screen, never its content. The phone is logged as seized and stops getting calls and texts while police hold it.
In the Forensics app, police:
- see the phones they carry, and lodge, return or destroy them;
- issue and revoke search authorizations (only Authorizers);
- extract a phone they carry, when a search authorization covers it. The report is kept in Forensics and can be copied as plain text;
- read carrier records for a number (calls and texts, with the area each party was in, never content);
- trace a number that is switched on right now to a rough area on the map.
Deleted texts, contacts, calls, mail, photos and notes are still found by an extraction for Recover deleted content for days. A secure wipe (the phone_wiper item) defeats that. Carrier records are kept per number, so they survive a wiped or destroyed phone.
Layer 2: Events and Exports
This server event fires once a report is stored, before it goes to any destination. Players can't trigger it.
| Event | Arguments | When |
|---|---|---|
oxide:phone:server:evidenceReport | reportId, report | An extraction finished |
AddEventHandler('oxide:phone:server:evidenceReport', function(reportId, report)
print(('[evidence] report %d for case %s'):format(reportId, report.caseRef))
end)Layer 3: Report Destinations
A destination is registered from your own server resource with one export:
exports['oxide-phone']:RegisterEvidenceBackend('my-mdt', {
AttachReport = function(src, caseRef, report) ... end, -- required
FlagNumber = function(number, info) ... end, -- optional
})Only AttachReport works the way it does in oxide-drugselling's evidence bridges: the same arguments, and true, or false and a reason, back. An AttachReport that only reads report.text works with both. The rest is different:
- Registering. A phone destination registers with this export from a resource of your own. An
oxide-drugsellingbridge is a file insideoxide-drugselling'sserver/integrations/evidence/folder. - Who gets a report. The phone sends each report to every running destination in its list.
oxide-drugsellingsends to the first one only. FlagNumber. The phone'sinfohasfound, andbyis the officer's name.oxide-drugselling's has nofound, andbyis the officer's character identifier.- The report. Each resource sends its own report table, with different fields. Both have
text.
The contract
| Function | Arguments | Return |
|---|---|---|
AttachReport | src (the officer's server ID), caseRef (what the officer typed), report | true, or false and a short reason. The reason is shown to the officer in Forensics |
FlagNumber | number, info ({ traced = true, found, by }: found is whether the trace located the phone, by the officer's name) | nothing; called when an officer traces a number |
A destination that errors or returns false never loses the report: it stays in Forensics, the failure is recorded on it, and the officer can press Send again.
The report
| Field | Contents |
|---|---|
kind | 'phone', or 'trap' for an oxide-drugselling trap phone |
caseRef | The officer's case reference, upper case |
officer | The officer's name |
authorization | What authorized the search, for example #12 LSPD-2026-0042 or Warrant #7 |
at | When, in milliseconds since 1970 |
device | { deviceId, number, simRegistered, simOwner, deviceOwner, custody, powered, wiped } |
recoveryDays | The recovery window the extraction used |
contacts, threads, calls, mail, media, notes | Everything found, including deleted items still inside the recovery window. Messages in threads carry deleted = true; the other rows carry the time they were deleted in deleted_at |
aliases, notifications, shares, albums, voicemails, blocks, places, feed | Mail aliases, stored notifications, nearby shares, Photos albums, voicemail, blocked numbers, saved places and Feed posts, under the same recovery window |
apps | What the phone did in apps from other resources (such as Hookr, Leakd and Glitch), one section per app |
darkchat | Recovered Dark Chat messages (room, handle, body, created_at). Empty unless Dark Chat recovery is above 0 |
custody | The custody log, oldest first (action, actor, caseRef, note, at) |
text | A plain-text copy of all of it. The copy a destination receives is cut to at most 60,000 bytes; Forensics keeps the full text |
Most MDTs only need report.text.
A 'trap' report is built by oxide-drugselling and has its own content fields. It always has kind, caseRef, officer, authorization, at, device and text.
Turning a destination on
Destinations are listed in Evidence → Report destinations of /phone settings. The name must be the name of the resource that registers it, and that resource must be running. oxide-police is in the list by default.
Your Own Search Authorizations
Set Evidence → Search authorization to Hook and register a check from your own server resource:
exports['oxide-phone']:SetAuthorizationCheck(function(src, target)
-- target = { deviceId?, number?, ownerCharId? }
local warrant = MyCourts.FindWarrant(target.ownerCharId)
if warrant then return true, ('Warrant %s'):format(warrant.id) end
return false
end)Return true and, optionally, a label for the report (up to 80 characters). The check is cleared when your resource stops. While the mode is Hook and no check is registered, every search is refused.
target.ownerCharId is the character the phone's SIM is registered to, or, without a registered SIM, the phone's registered owner.
With Built in, an active oxide-police warrant for that same character also counts, when oxide-police runs.
Worked Example: A Destination For Your MDT
Make a folder resources/phone_mdt_bridge/.
Create phone_mdt_bridge/fxmanifest.lua:
fx_version 'cerulean'
game 'gta5'
server_script 'server.lua'
dependency 'oxide-phone'Create phone_mdt_bridge/server.lua (replace my-mdt and its export with your MDT's):
exports['oxide-phone']:RegisterEvidenceBackend('phone_mdt_bridge', {
AttachReport = function(src, caseRef, report)
if GetResourceState('my-mdt') ~= 'started' then return false, 'MDT offline' end
local ok = exports['my-mdt']:AddCaseNote(caseRef, report.text)
if not ok then return false, 'Case not found' end
return true
end,
})Start it after the phone in server.cfg:
ensure oxide-phone
ensure phone_mdt_bridgeIn /phone settings → Evidence → Report destinations, add a row named phone_mdt_bridge.
Testing
As an officer on duty, carry someone else's phone and open it: the lock screen shows Evidence.
As an authorizer, issue an authorization for that phone's number in Forensics → Authorizations.
In Forensics → Phones, extract it with a case reference that exists in your MDT.
Forensics → Reports shows the report and, for each destination, Delivered or the reason it was not.
Building Apps for the Phone
Developer guide for putting your own resource on oxide-phone — registering apps, the web SDK, widgets, notifications and badges, security, and phone services such as permissions, media, links, accounts, evidence, live streams and custom photo storage.
Troubleshooting
Fixes for the most common oxide-phone problems — startup, opening the phone, SIM cards and numbers, passcodes, calls, texts and mail, photos, police evidence, apps, emergency numbers, settings and models.