DND Docs

Resourcesdnd_phoneReference

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 a Security.Callback(...) endpoint in server/modules/*.lua, or
  • fire a TriggerServerEvent('dnd_phone:<module>:<action>', ...), wrapping a Security.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.luaSetup Wizard / Settings (recovery)dnd_phone:account:*
advertise.luaAdvertisednd_phone:advertise:*
bank.luamBankingdnd_phone:bank:*
billing.luaBilling / EDCdnd_phone:billing:*
birdie.luaBirdiednd_phone:birdie:*
cab.luaDowntown Cab Co.dnd_phone:cab:*
calls.luaPhone (dialer) / Group Calldnd_phone:calls:*
charging.luaMy Sim / battery / Settingsdnd_phone:charging:*
contacts.luaContactsdnd_phone:contacts:*
custom_apps.luaStore / app registrydnd_phone:apps:*
darkchat.luaDark Chatdnd_phone:darkchat:*
dataplan.luaMy Simdnd_phone:dataplan:*
device.luaSetup Wizarddnd_phone:device:*
economy.luaEconomy Marketdnd_phone:economy:*
gallery.luaGallerydnd_phone:gallery:*
garage.luaGaragednd_phone:garage:*
gps.luaGPSdnd_phone:gps:*
housing.luaPropertydnd_phone:housing:*
jobmanagement.luaJobdnd_phone:job:*
mail.luaMaildnd_phone:mail:*
messages.luaMessages / Angpaodnd_phone:messages:*
notepad.luaNotepaddnd_phone:notepad:*
payphone.luaPayphonednd_phone:payphone:*
powerbank.luaPowerbank itemdnd_phone:powerbank:*
profile.luaContacts / accountdnd_phone:profile:*
service_station.luaService Stationdnd_phone:service:*
settings.luaSettingsdnd_phone:settings:*
share.luaShare-to-nearbydnd_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:*)

EventModulePurpose
phoneNotifynotifications.luaIn-phone push banner (also public, see above)
cabRequest / cabRequestExpired / cabMatched / cabNoDriver / cabAccepted / cabInProgress / cabCompleted / cabCancelled / cabTippedcab.luaDowntown Cab Co. live trip state
incomingCall / callConnected / callEndedcalls.luaPhone dialer overlay
chargingAutoStopped / chargecharging.luaCharging session end / external charge trigger
valetgarage.luaGarage valet delivery
newMailmail.luaMail push
newMessagemessages.luaMessages push
contactOffershare.luaShare-number-to-nearby incoming offer
phoneNotify (kind='billing')billing.luaNew/overdue bill push
incomingCall (payphone-originated)payphone.luaReuses 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, always pcall-wrapped

On this page