Exports & Events
Last updated on
dnd_phone exposes a small public surface for other resources to integrate with (exports + a couple of client events), and a much larger internal surface between the NUI (web UI) and the Lua client/server (every dnd:* / dnd_phone:* NUI callback and push event). This page documents both, split accordingly.
Public exports (for other resources)
dnd_phone:open (client)
Opens the phone for the calling player. Wired as the phone item's client.export in install/ox_inventory.txt — using the item calls this automatically. Also triggerable directly:
exports.dnd_phone:open()dnd_phone:chargePhone(source, amount) (server)
Charge a player's phone battery from your own item/script (e.g. a custom charger cable), instead of using dnd_phone's built-in charging stations/powerbank item.
---@param source number -- player server id
---@param amount number|nil -- percent to add, default 25
exports.dnd_phone:chargePhone(source, 25)dnd_phone:usePowerbank (client)
Wired as the powerbank item's client.export. Reports success/failure via a toast; server-authoritative (goes through dnd_phone:powerbank:use, never trusts the client's own claimed charge amount).
dnd_phone:RecordBankHistory(source, identifier, amount, description, direction) (server)
Records an external (non-phone) money movement into a player's own mBanking history, so a transaction from another resource (e.g. dnd_economy's bank-paid Buy/Sell) shows up in their phone, not just a silent balance change. This resource's own BankingBridge.LogTransaction/Renewed-Banking's handleTransaction do not reach mBanking's history — Bank.History reads from dnd_phone_bank_history, a table this resource owns.
---@param source number|nil -- player server id, used to read a live balance for the stored balance_after (nil is fine — just stores 0)
---@param identifier string -- framework identifier (citizenid/license)
---@param amount number -- positive integer
---@param description string -- shown in the phone's history list
---@param direction 'in'|'out' -- 'in' renders green/incoming, 'out' renders red/outgoing
local ok = exports.dnd_phone:RecordBankHistory(source, identifier, 4200, 'Bought 5x Steel from Ammunation', 'out')ok == false (no error) if Config.Banking.StoreHistory is off or the player has no registered phone device — both are safe no-ops. The actual database write happens in its own thread so this export never yields, which matters if you're calling it from inside your own lib.callback handler (a second nested yield across the resource boundary is what caused a hard-to-diagnose ox_lib callback-marshalling crash before this was fixed).
dnd_phone:CreateBill(target, price, reason, society, societyName, authorIdentifier, dueMinutes) (server)
Creates a job invoice against a player from your own resource, exactly like the in-phone EDC tool (/billing_edc) does — the target either pays it themselves from their Billing app, or it's auto-collected once dueMinutes passes (online-only; retried the moment they're next reachable). Parameter order deliberately mirrors okokBilling's CreateCustomInvoice, so an existing integration usually only needs the export/event name changed.
---@param target number -- player server id (a live source, not a phone number)
---@param price number -- amount to bill, clamped to Config.Billing.MinAmount/MaxAmount
---@param reason string -- shown to the target as the bill description
---@param society string -- job name credited when the bill is paid (e.g. 'police')
---@param societyName string|nil -- display label; falls back to `society` if omitted
---@param authorIdentifier string|nil -- who/what issued it, for record-keeping; falls back to `society`
---@param dueMinutes number|nil -- minutes until auto-charge kicks in, default Config.Billing.DefaultDueDurationMinutes
local result = exports.dnd_phone:CreateBill(targetSrc, 5000, 'Vehicle impound fee', 'police', 'Police Department', 'police', 60)
-- result = { ok = true, billId = 'BILL...' } or { ok = false, reason = '...' }Also reachable as a plain event, for parity with the export:
TriggerEvent('dnd_phone:billing:createInvoice', targetSrc, 5000, 'Vehicle impound fee', nil, 'police', 'Police Department', 'police', 60)Both are deliberately server-internal only — the event is a plain AddEventHandler, not RegisterNetEvent, so it's reachable exclusively from another server script's own TriggerEvent call on the same FXServer instance, never over the network from a client. A network-exposed version would let any client bill an arbitrary player under a fake society name and have it auto-charged against their real balance.
Public client events (server → client, for other resources)
dnd_phone:client:phoneNotify (client)
Push an in-phone notification banner (the same iOS/Android-style push used internally for messages, bank transfers, calls, etc.) to a specific player from your own resource:
TriggerClientEvent('dnd_phone:client:phoneNotify', targetSrc, {
kind = 'system', -- a real app id (e.g. 'messages') routes tap-to-open correctly
title = 'Title',
body = 'Body text',
})Internal architecture (not for external use, documented for maintainers)
NUI → Lua (web UI calling into the client)
Every app calls fetchNui(action, data) (web/src/lib/nui.ts), which posts to a RegisterNUICallback('dnd:<action>', ...) in client/modules/*.lua. Most of those either:
- call a secured server callback via
lib.callback.await('dnd_phone:<module>:<action>', false, ...), wrapping aSecurity.Callback(...)endpoint inserver/modules/*.lua, or - fire a
TriggerServerEvent('dnd_phone:<module>:<action>', ...), wrapping aSecurity.NetEvent(...)endpoint.
Every server endpoint is registered through Security.Callback / Security.NetEvent (server/security.lua), which enforces: a resolved player identifier, per-action rate limiting (server/rate_limit.lua), and (inside each handler) server-side ownership/ownership-of-device checks and input sanitization (shared/validators.lua). Nothing is trusted from the client beyond an opaque request — position, device ownership, and money amounts are always re-derived/re-checked server-side.
Server → Lua → NUI (push events)
TriggerClientEvent('dnd_phone:client:<event>', target, data) in a server module is caught by a matching RegisterNetEvent in client/modules/*.lua, which calls SendReactMessage('<action>', data) to forward it into the web UI, where a useNuiEvent('<action>', handler) hook (web/src/hooks/useNuiEvent.ts) picks it up.
Module → server endpoint map
Module (server/modules/) | App(s) | Endpoint prefix |
|---|---|---|
account.lua | Setup Wizard / Settings (recovery) | dnd_phone:account:* |
advertise.lua | Advertise | dnd_phone:advertise:* |
bank.lua | mBanking | dnd_phone:bank:* |
billing.lua | Billing / EDC | dnd_phone:billing:* |
birdie.lua | Birdie | dnd_phone:birdie:* |
cab.lua | Downtown Cab Co. | dnd_phone:cab:* |
calls.lua | Phone (dialer) / Group Call | dnd_phone:calls:* |
charging.lua | My Sim / battery / Settings | dnd_phone:charging:* |
contacts.lua | Contacts | dnd_phone:contacts:* |
custom_apps.lua | Store / app registry | dnd_phone:apps:* |
darkchat.lua | Dark Chat | dnd_phone:darkchat:* |
dataplan.lua | My Sim | dnd_phone:dataplan:* |
device.lua | Setup Wizard | dnd_phone:device:* |
economy.lua | Economy Market | dnd_phone:economy:* |
gallery.lua | Gallery | dnd_phone:gallery:* |
garage.lua | Garage | dnd_phone:garage:* |
gps.lua | GPS | dnd_phone:gps:* |
housing.lua | Property | dnd_phone:housing:* |
jobmanagement.lua | Job | dnd_phone:job:* |
mail.lua | dnd_phone:mail:* | |
messages.lua | Messages / Angpao | dnd_phone:messages:* |
notepad.lua | Notepad | dnd_phone:notepad:* |
payphone.lua | Payphone | dnd_phone:payphone:* |
powerbank.lua | Powerbank item | dnd_phone:powerbank:* |
profile.lua | Contacts / account | dnd_phone:profile:* |
service_station.lua | Service Station | dnd_phone:service:* |
settings.lua | Settings | dnd_phone:settings:* |
share.lua | Share-to-nearby | dnd_phone:share:* |
Each endpoint follows dnd_phone:<module>:<action> — e.g. dnd_phone:bank:transfer, dnd_phone:cab:respond, dnd_phone:messages:sendGroup. Browse the relevant server/modules/*.lua file for exact parameters; every endpoint is a thin, readable wrapper around a <Module>.<Action>(...) function of the same name.
Client push events (dnd_phone:client:*)
| Event | Module | Purpose |
|---|---|---|
phoneNotify | notifications.lua | In-phone push banner (also public, see above) |
cabRequest / cabRequestExpired / cabMatched / cabNoDriver / cabAccepted / cabInProgress / cabCompleted / cabCancelled / cabTipped | cab.lua | Downtown Cab Co. live trip state |
incomingCall / callConnected / callEnded | calls.lua | Phone dialer overlay |
chargingAutoStopped / charge | charging.lua | Charging session end / external charge trigger |
valet | garage.lua | Garage valet delivery |
newMail | mail.lua | Mail push |
newMessage | messages.lua | Messages push |
contactOffer | share.lua | Share-number-to-nearby incoming offer |
phoneNotify (kind='billing') | billing.lua | New/overdue bill push |
incomingCall (payphone-originated) | payphone.lua | Reuses the normal call overlay for the recipient of a payphone call |
Third-party exports dnd_phone itself calls
See Dependencies for the full bridge list. Notably:
exports['Renewed-Banking']:handleTransaction/getAccountMoney/addAccountMoney/removeAccountMoney(mBanking)exports['jg-advancedgarages']:getAllGarages()(Garage)exports.bcs_housing:GetOwnedHomeKeys/isLocked/GetKeyHolders/SetWaypoint(Property)exports[<Config.JobApps.Resource>]:.../exports[<Config.DarkChat.Resource>]:.../exports[<Config.EcoMarket.Resource>]:...— soft integrations, alwayspcall-wrapped