Server Exports
On this page6
exports['5code-recognition']:<Name>(...)Read-only exports
Any resource may call these. They never change anything.
Recognition
| Export | Signature | Returns |
|---|---|---|
IsRecognized | (viewerSrc, targetSrc, minPct?, opts?) | boolean |
GetRecognitionLabel | (viewerSrc, targetSrc, minPct?, opts?) | string | nil |
GetRecognition | (viewerSrc, targetSrc, opts?) | nil | RecognitionInfo (see below) |
GetRecognizers | (targetSrc, minPct?, opts?) | integer[] — the viewers who recognize this player right now |
HasRecognized | (observerSrc, targetSrc, minPct?, opts?) | boolean — the "may act" check |
GetReliability | (observerSrc, targetSrc, opts?) | integer 0–100 |
minPct defaults to Config.Scoring.ActThreshold (80).
IsRecognized vs HasRecognized. IsRecognized is the name display
question — it follows Config.Api.RecognitionSource, which is 'scene' by
default, so it is only true while a live result is open. HasRecognized is the
may this character act question, and it always counts a valid Lab ID.
Use IsRecognized for nametags and chat. Use HasRecognized to gate an action.
-- server side
local label = exports['5code-recognition']:GetRecognitionLabel(viewerSrc, speakerSrc)
TriggerClientEvent('chat:addMessage', viewerSrc, { args = { label or 'Stranger', text } })Appearance
| Export | Signature | Returns |
|---|---|---|
IsFaceHidden | (targetSrc) | boolean — the same for every viewer |
GetConcealment | (targetSrc) | Concealment (see below) |
Memories
| Export | Signature | Returns |
|---|---|---|
GetMemory | (observerKey, targetKey, opts?) | nil | MemoryPublic — the best valid memory of the pair |
GetMemories | (observerKey, targetKey, opts?) | MemoryPublic[] |
These take character keys, not server ids. Use GetCharacterKey(src).
State
| Export | Signature | Returns |
|---|---|---|
IsScanning | (src) | boolean — a quiet quick look does not count as a scan |
IsCameraActive | (src) | boolean |
IsVoiceChangerActive | (src) | boolean |
GetCharacterKey | (src) | string | nil |
GetIdentityGeneration | (charKeyOrSrc) | integer | nil — goes up after each face implant or identity reset |
GetImplantCooldown | (charKeyOrSrc) | integer seconds left, 0 = ready |
OpenAdminPanel | (adminSrc, aSrc?, bSrc?) | boolean — for an admin-menu button |
opts.includeDisguises
Every export that can answer from a saved memory takes
opts = { includeDisguises = true }. It defaults to false, and you almost
certainly want to leave it there.
A "masked person" memory of somebody must never make IsRecognized true, or
supply a label, while that person is standing there bare-faced — that would hand
your nametag script exactly the link the disguise system exists to hide.
The one exception is built in: a live result about a disguise does answer while it is open, which is how "the hoodie guy" keeps his label on screen while he is wearing the hoodie.
Set it to true only for staff tooling and incident reporting.
Return shapes
Written in TypeScript notation. Lua tables have the same fields, with nil where
a field is optional.
type Channel = 'memory' | 'camera' | 'report' | 'hearsay' | 'lab';Channel says where the knowledge came from: memory your own eyes, camera
your own hidden camera, report a police reading of a camera report, hearsay
a description a friend gave you, lab a fingerprint or DNA match.
RecognitionInfo
Returned by GetRecognition (server and client), and carried by the client
event onRecognitionChanged.
interface RecognitionInfo {
pct: number; // 0-100
channel: Channel;
live: boolean; // is a live result open right now?
label?: string; // the memory line, e.g. "Met him on Vinewood Blvd in the evening"
introducedName?: string; // the name they gave this viewer, if any
sceneEndsAt?: number; // epoch seconds, while live
expiresAt?: number; // epoch seconds, server side only
}Show introducedName or label, and show nothing when both are absent.
MemoryPublic
Returned by GetMemory and GetMemories.
interface MemoryPublic {
id: number;
source: Channel;
reliability: number; // as saved
effective: number; // what it is worth now
cues: { voice: number; clothing: number; face: number; tattoo: number };
label?: string; // the memory line
claimedName?: string; // an introduced name
createdAt: number; // epoch seconds
lastScanAt: number;
expiresAt: number;
}There is deliberately no "this is a disguise" flag: the filtering is ours to do,
not yours — see opts.includeDisguises.
Concealment
Returned by GetConcealment(targetSrc).
interface Concealment {
faceHidden: boolean; // at or above Config.FaceCoverage.HiddenAt (60)
facePct: number; // the VISIBLE share: 100 = fully visible
faceCategory: 'none' | 'hat' | 'eyes' | 'half' | 'full';
armsClass: string; // e.g. 'none' | 'short' | 'sleeveless' | 'bare'
torsoVisible: boolean;
handsVisible: boolean;
skinVisible: boolean; // any tattoo-capable skin uncovered
}Concealment is the same for every viewer — everybody can see a mask — so it is safe to use anywhere. A player the script has not read yet counts as hidden.
Write exports
These change state and are restricted. A call from a resource that is not
on the trusted list is refused (false, 'not_trusted') and logged.
-- config_server.lua
ServerConfig.Api = {
TrustedResources = { 'my_surgeon_shop', 'my_idcard' },
}Our own resource and our bridges are always trusted. Options that skip money or
cooldowns (charge = false, ignoreCooldown = true, setCooldown = false,
checkDistance = false) are honoured only for trusted callers.
Every write-export call is written to the audit log and posted to the admin Discord channel.
Identity
| Export | Signature | Returns |
|---|---|---|
ResetIdentity | (charKeyOrSrc, reason?, opts?) | ok, err |
StartFaceImplant | (src, opts?) | ok, err |
ResetIdentity makes everybody forget this character, with no payment and no
editor. opts: rotateVoice, ignoreCooldown, setCooldown, label.
StartFaceImplant runs our full implant flow from your script. opts:
charge (default true), price, ignoreCooldown, rename (default true),
account, label.
Error codes: unknown_character, implant_busy, implant_cooldown,
implant_conflict, not_trusted.
-- your surgeon shop, server side, after you have taken payment yourself
local ok, err = exports['5code-recognition']:StartFaceImplant(src, {
charge = false, -- trusted callers only
rename = true,
label = 'Backstreet Clinic',
})
if not ok then print(('implant refused: %s'):format(err)) endDeath
| Export | Signature | Returns |
|---|---|---|
ReportDown | (src, data?{stage, killer, weapon}) | boolean |
ReportRevived | (src, reviverSrc?, method?) | boolean |
ReportRespawned | (src, method?) | boolean |
Use these when your ambulance script is not one we have an adapter for. stage
is 'laststand' or 'dead'. They count as confirmed, exactly like an adapter —
so call them from your own server code, never from a client event.
Evidence
| Export | Signature | Returns |
|---|---|---|
GrantLabIdentification | (officerSrc, targetCharKey, opts?{source, evidenceType, ttlHours}) | ok, err |
Recognition records
| Export | Signature | Returns |
|---|---|---|
LogAction | (actorSrc, targetSrc, cause, meta?) | snapshotId | nil |
cause is one of aim, damage, kill, cuff, uncuff, rob, search,
escort, kidnap, custom.
It waits until the record is written (2-second timeout) and uses the same
cooldowns as the built-in records. meta is capped at 2 KB; meta.detail
becomes the record's detail text. Calls are capped per calling resource per
minute (Config.Proof.ExportCallsPerMinute).
-- your heist script, when a hostage is taken
exports['5code-recognition']:LogAction(robberSrc, hostageSrc, 'kidnap', {
detail = 'Fleeca Route 68',
})Social
| Export | Signature | Returns |
|---|---|---|
RecordIntroduction | (speakerSrc, listenerSrc, name, opts?{via, checkDistance}) | ok, code |
via is 'idcard' or 'script'. Use it when an ID card is shown, so the name
lands on the listener's memory the same way a spoken introduction would.
Items
| Export | Signature | Returns |
|---|---|---|
TriggerItemUse | (src, itemName, itemData?) | boolean — for custom inventories |
GiveItem | (src, itemName, count?) | ok — standalone virtual items |
useItem | (event, item, inventory, slot, data) | the ox_inventory server.export handler |
IDs you will see
| Thing | Format |
|---|---|
| Character key | A string from your framework (for example the citizenid on QBCore / Qbox). Treat it as an opaque id |
| Device serial | VC- voice changer · HC- hidden camera · MS- magnetic scanner, plus 9 characters |
| Paper id | RC- camera report · WS- witness statement · DS- description, plus 9 characters |
| Voiceprint | VP-XXXX-XXXX — the same voice changer always produces the same one |
See also
- Client Exports — and why they are not for draw loops
- Events — the push-based way to follow recognition
- Hooks