Exports
Server and client exports for controlling the phone, looking up numbers, sending texts and notifications, answering phone numbers, and integrating other resources with nx_phone.
Call every export with the colon form, exports.nx_phone:Fn(...) or
exports['nx_phone']:Fn(...).
A few rules apply across the API:
- Exports marked Awaits read the database. Call them from a thread, an event handler or a callback, never from the top level of a file.
- A refusal comes back as
falseplus an error string. - Hotlines, service routers, phone disables, Live Activities, companies
and registered apps and sites belong to the resource that created them and
are released when it stops. When
nx_phonerestarts they are gone too, so register them again from thenx_phone:sdkReadyevent.
The MDT exports (MdtStatus, MdtHeldDevices, MdtExtractStart and the
rest of the Mdt* family) are reserved for nx_computer's MDT. They
answer only the resources listed in Config.MDT.ExportResources and are
not documented here.
Server
An export that fails returns false and a reason string, so
local id, err = exports.nx_phone:SendMessage(...) gives you the reason in err.
Phone control
exports.nx_phone:ClosePhoneserverPut the player's phone away. Returns false, 'invalid_source' when the player is not connected. Never yields.
Parameters
srcnumber— Player server id.reason?string— Passed to the nx_phone:closed client event. Up to 64 characters.
Returns
true | false, errexports.nx_phone:ClosePhone(source, 'cuffed')exports.nx_phone:LockPhoneserverPut the phone away and end its unlock, so the next open asks for the passcode even when Config.Auth.LockOnClose is false. Errors: invalid_source, failed. Awaits.
Parameters
srcnumber— Player server id.
Returns
true | false, errexports.nx_phone:LockPhone(source)exports.nx_phone:SetPhoneDisabledserverStop the player from using the phone. Each calling resource holds its own disable, and the phone stays disabled while any resource holds one, so handcuffs and jail are two separate holds. Your hold is released when your resource stops. Calls still ring a disabled phone. Never yields.
Parameters
srcnumber— Player server id.disabledboolean— true to add your hold, false to release it.reason?string— Shown to the player when they try to open the phone. Up to 64 characters.
Returns
true | false, errexports.nx_phone:SetPhoneDisabled(source, true, 'You are in handcuffs.')
-- later
exports.nx_phone:SetPhoneDisabled(source, false)exports.nx_phone:IsPhoneDisabledserverWhether any server-side hold disables this player's phone, and one hold's reason. Holds set with the client export are not included. Never yields.
Parameters
srcnumber— Player server id.
Returns
boolean, string?exports.nx_phone:IsPhoneOpenserverWhether the player has the phone open right now. Reads a replicated state, so it costs no round trip to the client.
Parameters
srcnumber— Player server id.
Returns
booleanif exports.nx_phone:IsPhoneOpen(source) then
return -- refuse the action while the player is on the phone
endexports.nx_phone:IsPhoneUnlockedserverWhether the player has an unlocked phone session.
Parameters
srcnumber— Player server id.
Returns
booleanexports.nx_phone:StartCallserverOpen the player's phone and call the number, as the client export StartCall does. Errors: invalid_source, invalid_number. Whether the call goes through is up to the player's phone (a passcode, no SIM). Never yields.
Parameters
srcnumber— Player server id.numberstring | number— The number to call: digits, + * # - ( ) . and spaces, up to 32 characters.opts?table— { hideCallerId? }: withhold the caller id.
Returns
true | false, errexports.nx_phone:StartCall(source, '555-0142')Lookups
exports.nx_phone:GetSourceFromNumberserverThe connected player holding the handset this number's active SIM is in, or nil. Awaits.
Parameters
numberstring— A phone number. Bare digits work.
Returns
number | nillocal target = exports.nx_phone:GetSourceFromNumber('555-4821')exports.nx_phone:GetNumberFromSourceserverThe number the player is using: the handset they unlocked, if they still carry it and it has an active SIM, otherwise the first handset they carry (lowest inventory slot) with an active SIM. Awaits.
Parameters
srcnumber— Player server id.
Returns
string | nillocal number = exports.nx_phone:GetNumberFromSource(source)exports.nx_phone:GetNumberFromIdentifierserverThe number a character uses, whether they are online or not. Use it to keep a customer's number on an order made while they are offline. Awaits.
Parameters
identifierstring— Character identifier (ESX xPlayer.identifier, QBCore/Qbox citizenid).
Returns
string | nilFor an online character it returns the same number as GetNumberFromSource.
For an offline character it checks, in order:
- Their main number, if that SIM is active.
- The number of their most recent phone session, if that SIM is still active. Sessions are kept for about 2 days after they end.
- The active SIM in the phone they own that was used most recently.
- The oldest active SIM they own.
To list every number a character owns, online or not, use
GetNumbersForActor.
local number = exports.nx_phone:GetNumberFromIdentifier(order.customerIdentifier)exports.nx_phone:IsInCallserverWhether the player is a party of a voice call (dialling, ringing out or connected) or is in a FaceTime call. A call that only rings them does not count. Never yields.
Parameters
srcnumber— Player server id.
Returns
booleanexports.nx_phone:GetNumbersForActorserverEvery active SIM the character owns, online or offline, as a list of { number, device_phone_id, carrier, primary }. device_phone_id is set while the SIM is in a phone. The main number comes first, with primary = true.
Parameters
identifierstring— Character identifier.
Returns
tableexports.nx_phone:GetNumbersForAccountserverThe same list for the character that owns the account.
Parameters
accountIdstring— Phone or laptop account id.
Returns
tableexports.nx_phone:GetDeviceForNumberserverThe phone id of the handset the number's active SIM is in, or nil.
Parameters
numberstring— A phone number.
Returns
string | nilexports.nx_phone:GetNumberForDeviceserverThe number of the SIM in that handset, or nil.
Parameters
phoneIdstring— A handset id.
Returns
string | nilexports.nx_phone:GetSessionContextserverThe player's unlocked phone session, or nil when the phone is locked. Fields include phoneId, accountId, number, actorIdentifier, username, displayName and allowedApps (the app ids this player may open).
Parameters
srcnumber— Player server id.
Returns
table | nillocal ctx = exports.nx_phone:GetSessionContext(source)
if ctx and ctx.number then
-- the player has an unlocked phone with a number
endexports.nx_phone:ResolveActorAccountserverThe account acting for this player: the phone session's account, else the laptop session's (with nx_computer), else the character's primary account.
Parameters
srcnumber— Player server id.
Returns
table | nilMessages, notifications and mail
exports.nx_phone:SendMessageserverSend a text message. Awaits.
Parameters
inputtable— { from, to, text?, attachments?, location?, choices? }. See below.
Returns
messageId, conversationId | false, errfrom: an active SIM number, or a hotline your resource registered withRegisterHotline.to: a number, or a list of numbers.text: the message body.attachments(SIM senders only):{ { photoId = 12 } }, photos the sending handset owns.location:{ x, y, name, sub? }. A map card the player taps to open Maps, where Directions sets the GPS waypoint. It is sent as its own message after the text.subis an optional street line.choices(hotline senders only): up to 4 reply buttons under the last message sent, as{ { id, label }, ... }(label up to 24 characters) or plain strings{ 'Yes', 'No' }(idsc1,c2, ...). A tap sends the label as the player's reply, and your hotline'sonMessagegets it withmsg.choice = { id, label, messageId }. The first tap answers the message and the buttons go away.
From a SIM, the message is that player's own: their blocks, their
handset's airplane mode and the app's rules apply, and a list of numbers
makes one group thread. From a hotline, each number gets its own private
thread titled with the hotline's label. With several numbers, the export
returns the first messageId, conversationId and a third value listing
every result: { { to, messageId?, conversationId?, error? }, ... }.
With text and a location, the returned id is the last message's (the one carrying the buttons).
Errors: invalid_request, invalid_sender, invalid_number,
unknown_number, empty, invalid_attachment, invalid_location,
invalid_choices, choices_hotline_only, choices_unavailable,
airplane, hotline_group, too_many_recipients, rate_limited,
unavailable.
The rate limit is Config.Server.RateLimits.ApiMessage, 60 messages per
10 seconds per calling resource by default.
local id, convOrErr = exports.nx_phone:SendMessage({
from = '555-0190',
to = '555-4821',
text = 'Your order is ready for pickup.',
})
if not id then print('send failed: ' .. convOrErr) end
-- A quest offer with a meeting point and two answers.
exports.nx_phone:SendMessage({
from = '555-0420',
to = playerNumber,
text = 'Got a job for you. Meet me here.',
location = { x = 215.4, y = -810.2, name = 'Legion Square' },
choices = { { id = 'accept', label = "I'm in" }, { id = 'decline', label = 'Not now' } },
})exports.nx_phone:NotifyserverShow a notification on a player's phone and store it on the handset. Returns the notification id. Awaits.
Parameters
inputtable— Target and content. See below.
Returns
number | false, errName the target with one of phoneId, number, source,
identifier or accountId. Content fields:
appId: the app the notification belongs to (default'system'). Use your SDK app's id so players can mute it in Settings.title(up to 120 characters) andmessage(up to 512). At least one is required.severity:'info','success','warning'or'error'.threadKey,category: grouping keys (default: the app id).deepLinkApp,deepLinkPayload: the app, and its route, a tap opens.sourceEvent: your own tag (up to 64 characters). It defaults to your resource's name. A tap raisesnx_phone:server:notificationTappedwith it, so you can react when the player opens your notification.
Returns false, 'suppressed' when the player turned notifications off for
that app, or when the app is not installed on that phone (a built-in app
removed in Depot, or an SDK app the player never installed). That is not
an error: nothing was stored or shown. Notifications with an appId
that is not an app, such as 'system', are never held back this way. Other
errors: no_target, number_has_no_device, holder_has_no_phone,
device_not_found, empty, invalid_input.
exports.nx_phone:Notify({
source = playerId,
appId = 'x.my_shop.orders',
title = 'Order ready',
message = 'Pick it up at the counter.',
})exports.nx_phone:SendMailserverDeliver a mail to a player's Mail inbox. With nx_computer running it goes through the laptop's mail system. Awaits.
Parameters
optstable— { to, from?, fromName?, subject, body, location? }
Returns
number | false, errto is a mail address, a phone number (the account active on the
handset holding that SIM, else the SIM owner's primary account), or a
character identifier (their primary account). from defaults to
noreply@ your mail domain. With nx_computer running, a from that is
one of its NPC mailboxes (exports.nx_computer:RegisterMailbox) sends as
that mailbox, so the player can reply to it. location = { x, y, name, sub? } adds a map card as the last line, on the phone and the laptop.
Errors: invalid_input, disabled, empty, no_recipient, full,
send_failed, insert_failed, invalid_location.
exports.nx_phone:SendMail({
to = '555-4821',
fromName = 'Pacific Standard',
subject = 'Statement ready',
body = 'Your monthly statement is available at any branch.',
})Hotlines
A hotline is a phone number your resource answers, for example a taxi company's line. Players call and text it like any other number.
exports.nx_phone:RegisterHotlineserverClaim a number for your resource. Registering the same number again from your resource updates it. Awaits.
Parameters
numberstring— Digits and dashes, 3 to 15 digits.optstable— { label, onCall?, onMessage?, onRead? }
Returns
true | false, errlabel: what callers and texters see, up to 32 characters.onCall(src, info): runs while the caller dials.infois{ callId, number, label, from?, hidden, source };fromis nil when the caller hides their ID, andsrccan be nil when no player's phone placed the call. Return{ ring = { playerIds } }to ring those players (the first to answer takes the call),{ reject = 'busy' }for a busy tone,{ bot = script, answerAfter? }to have the NPC answer itself with a scripted conversation (see NPC calls;answerAfteris the ring time in seconds, default 2), ornilfor unavailable. Keep it quick. An error counts asnil.onMessage(src, msg): a player texted the hotline.msgis{ from, to, text, conversationId, messageId, hasAttachments, source, attachments, choice? }.choice = { id, label, messageId }when the player tapped one of your reply buttons. Reply withSendMessageusing the hotline asfrom.onRead(src, info): the player read your messages.infois{ conversationId, number, hotline, upToId, readAt?, source }.
Errors: invalid_number, invalid_label, invalid_handler,
number_taken (a SIM, even a reclaimed one, another resource's hotline,
or a company's number), number_reserved (a Config.Calls.Services line),
number_virtual (a payphone number), too_many_hotlines (32 per
resource).
A new SIM never gets a hotline's number. Pick hotline numbers outside
the shape of Config.Sim.NumberFormat where you can, so a SIM issued
while your resource was stopped can never collide with one.
exports.nx_phone:RegisterHotline('555-0190', {
label = 'Downtown Cab Co.',
onCall = function(src, info)
return { ring = GetOnDutyDrivers() } -- your own function
end,
onMessage = function(src, msg)
exports.nx_phone:SendMessage({ from = '555-0190', to = msg.from, text = 'A driver is on the way.' })
end,
})exports.nx_phone:RemoveHotlineserverRelease a hotline. Errors: not_found, not_owner. Never yields.
Parameters
numberstring— A number your resource registered.
Returns
true | false, errNPC calls
An NPC can call a player, and a hotline can answer its own calls, with a scripted conversation. The player's call screen shows what the NPC says and up to four replies. There is no voice: the NPC's lines are text, with an optional audio clip.
A script node:
{
text = 'Vlad here. Still want that job?', -- up to 400 characters
audio = 'https://cdn.example/vlad_01.ogg', -- optional, played once
choices = { { id = 'yes', label = 'Yes' }, { id = 'no', label = 'No' } }, -- up to 4, or plain strings
timeout = 45, -- seconds to pick before the NPC hangs up (10 to 300)
hangup = false, -- true (or no choices) = the NPC's last line
onChoice = function(src, info) end, -- return the next node, or nil to hang up
onEnd = function(src, info) end,
}onChoice(src, info) gets { callId, hotline, number, choice, label, step } and returns the next node. A node may bring its own onChoice
and onEnd, otherwise the previous ones stay. A last line (no choices,
or hangup = true) ends the call once the player has had time to read
it. onEnd(src, info) gets { callId, hotline, reason, answered, number, step } for every ending: declined, missed, busy, hung up.
exports.nx_phone:CallPlayerserverAn NPC calls a player. Awaits.
Parameters
tostring | number— A phone number, or a player server id.optstable— { from, script, hidden? }
Returns
callId | false, errfrom is a hotline your resource registered: its label is the name on
the call screen and in Recents. The player's phone rings like any call:
busy, blocks, airplane mode and a flat battery apply. Call Screening
never asks a script for a reason. One hotline can be on any number of
NPC calls at once.
Errors: invalid_sender, not_owner, invalid_target, no_number,
invalid_script, invalid_audio, invalid_choices,
invalid_handler, rate_limited, unavailable, busy. A call that
ends straight away also reaches onEnd.
exports.nx_phone:CallPlayer(playerId, {
from = '555-0420',
script = {
text = 'Vlad here. Still want that job?',
choices = { { id = 'yes', label = 'Yes' }, { id = 'no', label = 'No' } },
onChoice = function(src, info)
if info.choice == 'yes' then
StartQuest(src) -- your own function
return { text = 'Good. Pier, midnight. Come alone.', hangup = true }
end
return { text = 'Your loss.', hangup = true }
end,
},
})A hotline that answers with a menu:
exports.nx_phone:RegisterHotline('555-0420', {
label = 'Vlad',
onCall = function(src, info)
return { bot = {
text = 'Vlad. What do you need?',
choices = { 'Work', 'Nothing' },
onChoice = function(src2, pick)
if pick.choice == 'c1' then
OfferJob(src2) -- your own function
return { text = 'Check your messages.', hangup = true }
end
end,
} }
end,
})Calls
exports.nx_phone:GetActiveCallsserverEvery live call as a list of { id, state, service, callerNumber, calleeNumber, startedAt }. A hidden caller's number is included, since server code is trusted.
Returns
tablefor _, call in ipairs(exports.nx_phone:GetActiveCalls()) do
print(call.id, call.state, call.callerNumber)
endexports.nx_phone:GetCallserverOne live call, or nil. Fields: callId, state, from?, to?, hidden, service?, hotline?, video (always false), fromSource?, answeredBy?, answeredSource?, startedAt, answeredAt?. Never yields.
Parameters
callIdstring— A call id from GetActiveCalls or a call event.
Returns
table | nilexports.nx_phone:EndCallserverEnd a live call. Errors: not_found, invalid. Never yields.
Parameters
callIdstring— The call to end.reason?string— hangup (default), dropped, failed, cancelled, declined, no_answer, busy or unavailable.
Returns
true | false, errexports.nx_phone:AdminEndCallserverForce-drop a live call for staff tools, the same way airplane mode drops one. The phone has no admin concept, so check permissions before you call it. Errors: invalid, unavailable, not_found, no_handset, still_active.
Parameters
callIdstring— The call to end.
Returns
true | false, errexports.nx_phone:SetServiceRouterserverDecide who rings for a service line. info is { callId, number, label, emergency, from, source, hidden }. When fn returns nil, the line falls back to its jobs, minGrade and requireDuty. One resource owns a line's router at a time. Errors: not_a_service, router_taken, invalid_handler. Never yields.
Parameters
serviceNumberstring— A number from Config.Calls.Services.fnfunction | nil— fn(info) returning a list of player ids, or nil for the normal rule. nil clears the router.
Returns
true | false, errexports.nx_phone:SetServiceRouter('911', function(info)
return GetDispatchersNear(info.source) -- your own function; nil = default rule
end)Numbers and SIMs
exports.nx_phone:GiveSimserverCreate a SIM owned by the player's character and put the SIM item in their inventory. Returns the number. Awaits.
Parameters
srcnumber— Player server id.opts?table— { number?, carrier? }
Returns
string | false, errnumber asks for an exact number. It must match the
Config.Sim.NumberFormat shape and be free. Without it, a random number
is issued. carrier is up to 32 characters.
Errors: invalid_source, no_inventory, invalid_format,
invalid_carrier, number_taken, number_reserved, number_virtual,
number_burned, pool_exhausted, inventory_full (no SIM is kept).
local number, err = exports.nx_phone:GiveSim(source, { number = '555-7777' })exports.nx_phone:CreateSimserverCreate a SIM record with a random number without giving an item. Most resources want GiveSim instead. Errors: schema_not_ready, pool_exhausted, insert_failed.
Parameters
ownerIdentifierstring— Character identifier of the owner.carrier?string— Carrier name. Defaults to Config.Sim.DefaultCarrier.
Returns
table | false, errexports.nx_phone:SuspendNumberserverStart the reclaim clock (Config.Sim.ReclaimDays). The number stops working at once and a call on it drops. Errors: sim_not_found, sim_reclaimed. Awaits.
Parameters
numberstring— The number to suspend.
Returns
true | false, errexports.nx_phone:ReclaimNumberserverTake the number from its owner for good. It is never issued again. Awaits.
Parameters
numberstring— The number to reclaim.
Returns
true | false, errexports.nx_phone:SetVanityNumberserverGive an unused SIM a different number. The old number is retired for good. Awaits.
Parameters
oldNumberstring— The SIM to rename.newNumberstring— The new number.
Returns
true | false, errThis only renames a SIM that has never been used: active, never inserted
into a handset, and named by no message, call, contact, block or
voicemail. Other players' records point at a used number, so renaming it
would mean rewriting strangers' Recents and threads. To give someone a
vanity number, use GiveSim(src, { number = ... }).
Errors: sim_not_found, sim_suspended, sim_reclaimed, sim_in_use,
has_history, same_number, and the GiveSim number errors for the new
number. An online player carrying the SIM sees its label update at once.
exports.nx_phone:InsertSimserverPut a SIM the player carries into a phone they carry, exactly as using the SIM item does. Returns the inserted number and the number of the SIM it replaced, if any. Awaits.
Parameters
srcnumber— Player server id.opts?table— { slot?, iccid?, number?, phoneId? }
Returns
string, string? | false, errBoth items must be in the player's inventory. The SIM item is used up, and a SIM that was already in that phone comes back as an item.
- Pick the SIM with
slot,iccidornumber. Without any of them, the SIM in the lowest inventory slot is used. - Pick the phone with
phoneId. Without it, the phone in the player's hand is used, else the first phone they carry. A phone that was never opened is set up first.
Errors: invalid_source, no_inventory, sim_missing, device_not_found,
not_authorised (the player does not hold that phone), sim_not_found,
sim_suspended, sim_reclaimed.
local number, replaced = exports.nx_phone:InsertSim(source, { number = '555-7777' })exports.nx_phone:EjectSimserverTake the SIM out of a phone the player carries and give it back as an item. Returns its number. Awaits.
Parameters
srcnumber— Player server id.opts?table— { phoneId? }
Returns
string | false, errWithout phoneId, the phone the player is using is chosen. A full inventory
returns inventory_full and the SIM stays in the phone.
Errors: invalid_source, no_inventory, no_sim, not_authorised,
inventory_full.
exports.nx_phone:GetSimserverOne SIM, whatever its status. Awaits.
Parameters
numberOrIccidstring— A phone number (exact or digits only) or a SIM ICCID.
Returns
table | nilReturns { number, iccid, carrier, status, owner?, phoneId?, holder? }.
statusisactive,suspendedorreclaimed.owneris the owning character's identifier.phoneIdis the phone the SIM is in.holderis the server id of the player holding that phone right now.
exports.nx_phone:GetPlayerSimsserverEvery SIM a player carries: SIM items in their inventory (with slot) and the SIMs inside the phones they carry (with phoneId). Each entry has the same fields as GetSim. Awaits.
Parameters
srcnumber— Player server id.
Returns
table | false, errA character's main number is the first SIM of their own they put into a
phone. Someone else's SIM never becomes their main number. It stays
theirs: a replacement SIM keeps the number, and with nx_computer the
MDT lists it on the person's page.
exports.nx_phone:GetPrimaryNumberserverThe character's main number, online or offline, or nil when they have none. A suspended main number is still returned; a reclaimed one is not. Awaits.
Parameters
identifierstring— Character identifier.
Returns
string | nillocal main = exports.nx_phone:GetPrimaryNumber(xPlayer.identifier)exports.nx_phone:GetPrimaryNumbersserverThe main numbers of many characters in one lookup, as { [identifier] = number }, for a list such as a dispatch roster. Characters without a main number are left out. Awaits.
Parameters
identifierstable— A list of up to 100 character identifiers.
Returns
tablelocal numbers = exports.nx_phone:GetPrimaryNumbers({ 'char1:abc', 'char1:def' })
for identifier, number in pairs(numbers) do
print(identifier, number)
endexports.nx_phone:IsPrimaryNumberserverWhether the number is someone's main number. Awaits.
Parameters
numberstring— A phone number.
Returns
booleanexports.nx_phone:ReplaceMainSimserverGive the player a replacement SIM for their main number, the same replacement the carrier counter sells, with no counter and no charge. Use it from your own shop. Returns the number. Awaits.
Parameters
srcnumber— Player server id.
Returns
string | false, errThe number moves to a new SIM card and the old card stops working, wherever it is. Contacts, messages, Recents and voicemail stay with the number, and a suspended main number comes back. The new card goes into the player's inventory.
Errors: invalid_source, no_main_number, no_inventory,
inventory_full, failed. If the card cannot be handed over after the
number has moved, the number is safe on the new card and calling again
issues another one.
local number, err = exports.nx_phone:ReplaceMainSim(source)
if not number then
return TriggerClientEvent('ox_lib:notify', source, { type = 'error', description = err })
endHandsets
exports.nx_phone:GetDeviceserverThe handset record: phone_id, imei, owner_identifier, model, lock_state, last known position and more. nil when it does not exist.
Parameters
phoneIdstring— A handset id.
Returns
table | nilexports.nx_phone:SetDeviceStateserverLock, set Lost Mode on, or erase a handset. byActor must be the handset's owner unless opts.adminOverride is true. message and contact are shown in Lost Mode. 'wiped' erases the phone's data. Returns how many unlock sessions were ended. Errors: invalid_device, invalid_state, device_not_found, not_your_device, invalid_contact.
Parameters
phoneIdstring— A handset id.statestring— 'open', 'locked', 'lost' or 'wiped'.byActorstring— Character identifier of the owner making the change.opts?table— { message?, contact?, adminOverride? }
Returns
number | false, errexports.nx_phone:CallerHoldsDeviceserverWhether the player carries that handset.
Parameters
srcnumber— Player server id.phoneIdstring— A handset id.
Returns
booleanexports.nx_phone:BatteryserverBattery level from 0 to 100.
Parameters
phoneIdstring— A handset id.
Returns
number | nilexports.nx_phone:SetBatteryserverSet the battery level, for example from a charger item.
Parameters
phoneIdstring— A handset id.pctnumber— New level, 0 to 100.
Returns
booleanexports.nx_phone:IsAirplaneserverWhether airplane mode is on.
Parameters
phoneIdstring— A handset id.
Returns
booleanContacts and blocks
exports.nx_phone:IsBlockedserverWhether ownerNumber has blocked otherNumber.
Parameters
ownerNumberstring— The number whose block list is checked.otherNumberstring— The number that may be blocked.
Returns
booleanexports.nx_phone:IsBlockedEitherWayserverWhether either number has blocked the other.
Parameters
astring— A number.bstring— Another number.
Returns
booleanexports.nx_phone:GetContactNameserverThe name ownerNumber saved for otherNumber, or nil.
Parameters
ownerNumberstring— The phonebook to read.otherNumberstring— The number to look up.
Returns
string | nilexports.nx_phone:GetContactNamesserverA map of number to saved name, for the numbers that have a contact.
Parameters
ownerNumberstring— The phonebook to read.numberstable— A list of numbers.
Returns
tableexports.nx_phone:AddContactserverSave a contact in a player's phonebook, for example an NPC. Awaits.
Parameters
targetnumber | string— A player's server id (the number they are using), or a phone number.cardtable— { number, firstName | name, lastName?, company?, email?, note?, favourite? }overwriteboolean?— Replace a contact the player already has for that number.
Returns
contact, created | false, err, field?A number the player already saved keeps the player's own name
(created is false) unless overwrite is true. The player's open
phone reloads its contacts.
Errors: invalid_target, no_number, name_required,
invalid_number, self_number, limit_reached, rate_limited,
unavailable, plus the Contacts app's field errors.
exports.nx_phone:AddContact(playerId, { number = '555-0420', name = 'Vlad', note = 'Knows people.' })exports.nx_phone:RemoveContactserverRemove a contact from a player's phonebook. Errors: invalid_target, invalid_number, not_found. Awaits.
Parameters
targetnumber | string— A player's server id, or a phone number.numberstring— The contact number to remove.
Returns
true | false, errLive Activities
A Live Activity shows something ongoing, such as a repair or a taxi fare, in the phone's island and on its lock screen. The SDK page lists the content fields.
exports.nx_phone:StartLiveActivityserverStart an activity on a player's phone. Returns its id. Errors: disabled, invalid_content:field, too_big, too_many, owner_cap, rate_limited.
Parameters
srcnumber— Player server id.contenttable— title is required. See the content fields.
Returns
string | false, errlocal id = exports.nx_phone:StartLiveActivity(customer, {
title = 'Repair order',
subtitle = 'Los Santos Customs',
glyph = 'wrench',
steps = { labels = { 'Placed', 'In progress', 'Ready' }, current = 1 },
actions = { { id = 'cancel', label = 'Cancel', style = 'destructive' } },
})exports.nx_phone:UpdateLiveActivityserverUpdate one of your activities. Errors: not_found, not_owner, rate_limited, invalid_content:field.
Parameters
idstring— An id from StartLiveActivity.patchtable— Fields to change. false clears an optional field. alert = true opens the island briefly.
Returns
true | false, errexports.nx_phone:UpdateLiveActivity(id, { steps = { labels = { 'Placed', 'In progress', 'Ready' }, current = 3 }, alert = true })exports.nx_phone:EndLiveActivityserverEnd one of your activities.
Parameters
idstring— An id from StartLiveActivity.opts?table— { content?, linger? }: final content, and seconds the card stays on the lock screen (0 to 3600).
Returns
true | false, errexports.nx_phone:GetLiveActivitiesserverYour own activities on that player as a list of { id, owner, title, startedAt, updatedAt }.
Parameters
srcnumber— Player server id.
Returns
tableApps and browser sites
exports.nx_phone:RegisterAppserverAdd your resource's web page to the phone as an app. Returns the app id, x.your_resource.name. The SDK page lists every field.
Parameters
deftable— App definition. See the SDK page.
Returns
string | false, errlocal id, err = exports.nx_phone:RegisterApp({
name = 'dice',
label = 'Dice',
page = 'web/index.html',
icon = 'web/icon.webp',
})exports.nx_phone:UnregisterAppserverRemove one of your apps. Errors: invalid_name, not_found.
Parameters
namestring— The name passed to RegisterApp.
Returns
true | false, errexports.nx_phone:IsAppInstalledserverWhether the app is installed on that handset. Always true when Depot is turned off.
Parameters
phoneIdOrSourcestring | number— A handset id, or a player server id (their unlocked handset).appIdstring— A built-in or SDK app id.
Returns
booleanexports.nx_phone:RegisterSiteserverAdd an .lc website to the phone's Browser. Your sites are removed when your resource stops.
Parameters
deftable— Site definition. See below.
Returns
true | false, errA site is either static pages or a handler:
exports.nx_phone:RegisterSite({
domain = 'pizza.lc', -- lowercase letters, digits and dashes, ending in .lc
title = 'Pizza Planet', -- up to 80 characters
description = 'Hot pizza, fast', -- optional, up to 200
keywords = { 'pizza', 'food' }, -- optional, used by search.lc
pages = {
['/'] = {
title = 'Pizza Planet',
theme = { accent = '#e23b3b', bg = '#1a1a1a' },
blocks = {
{ type = 'hero', title = 'Pizza Planet', subtitle = 'Open late' },
{ type = 'button', label = 'Call us', href = 'app:calls' },
},
},
},
-- or: handler = function(src, path, query, form) return page end
})Block types: hero, heading, text, list, card, image, button,
divider and form. A link is an .lc URL, a path on the same site, or
app:<id>. Images load only from Config.Browser.ImageHosts. A handler
must answer within 2 seconds; nil shows Not Found, and form is
{ id, values } when a form is submitted.
Errors: invalid_site, invalid_domain, reserved (a built-in
domain), taken (another resource's domain), invalid_title,
invalid_handler, invalid_pages.
exports.nx_phone:UnregisterSiteserverRemove one of your sites.
Parameters
domainstring— A domain your resource registered.
Returns
booleanShift and dispatch
exports.nx_phone:SendDispatchserverSend an alert to the Shift inbox of every on-duty holder of the job, with a phone notification. Returns the first alert id and the list of ids. Awaits.
Parameters
optstable— { job | jobs, title, code?, body?, coords?, street?, priority?, ref? }
Returns
number, table | false, errjoborjobs: a job name or a list of names.title(required, up to 120 characters),body(up to 500),code(up to 16),street(up to 80).coords: avector3or{ x, y, z }.priority: 1 (high) to 3 (low), or'high','medium','low'. Priority 1 gets through quiet hours.ref: your own id for the alert, up to 64 characters. Sending again with the samerefupdates the alert and notifies nobody again.
Errors: invalid_input, disabled, no_job, no_title,
insert_failed.
exports.nx_phone:SendDispatch({
job = 'police',
code = '10-90',
title = 'Store alarm',
body = '24/7 on Innocence Blvd',
coords = GetEntityCoords(GetPlayerPed(source)),
priority = 'high',
})The five multijob exports below let a job script hire into and fire from
a job without making it the job the player works. They answer only the
resources listed in Config.Shift.MultiJob.ExportResources (default
{ 'nx_employeemanagement' }); anyone else gets false, 'forbidden' or
an empty list.
exports.nx_phone:AddJobserverAdd a job to the character's held jobs, or change its grade. A new grade for the job they work reaches the framework too. Errors: forbidden, disabled, schema_not_ready, invalid_request, unknown_job, max_jobs.
Parameters
identifierstring— Character identifier.jobstring— Job name.gradenumber— Job grade.removable?boolean— Whether the player may leave the job in Shift. Default true.
Returns
true, 'added' | 'updated' | false, errexports.nx_phone:RemoveJobserverRemove a held job, online or offline. 'left' means it was the worked job and the character is now on Config.Shift.MultiJob.Fallback; do not set a job yourself in that case. Errors: forbidden, disabled, invalid_request, not_held, unavailable.
Parameters
identifierstring— Character identifier.jobstring— Job name.
Returns
true, 'left' | 'removed' | false, errexports.nx_phone:GetJobsserverThe character's held jobs as a list of { job, grade, removable }.
Parameters
identifierstring— Character identifier.
Returns
tableexports.nx_phone:GetJobMembersserverEveryone holding the job as a list of { identifier, grade, removable }.
Parameters
jobstring— Job name.
Returns
tableexports.nx_phone:GetJobNamesserverA list of every job name that has members.
Returns
tableCompanies
The Companies app lists your city's businesses (see
Configuration). A company's id is its job
name, or the id of an entry without a job in Config.Companies.List,
so these exports take either.
exports.nx_phone:GetCompanyOpenserverWhether the company is open right now. false for a company that is not listed. Never yields.
Parameters
jobOrIdstring— A job name or a company id.
Returns
booleanif not exports.nx_phone:GetCompanyOpen('mechanic') then
return -- nobody is working at LS Customs
endexports.nx_phone:GetOpenCompaniesserverEvery open company as a set, { [id] = true }. Never yields.
Returns
tableexports.nx_phone:GetCompaniesserverEvery company in the directory, sorted by name. Never yields.
Returns
tableEach entry is { id, job?, label, number?, open, alwaysOpen, coords? }:
jobis nil for a place without a job.numberis the company's phone number. A place without a job has none.coordsis{ x, y }, the point Directions sets the GPS to.
for _, company in ipairs(exports.nx_phone:GetCompanies()) do
print(company.label, company.number, company.open)
endexports.nx_phone:RegisterCompanyserverAdd a company to the directory from your own resource, or update one you added. The directory picks it up within a moment. Never yields.
Parameters
jobstring— The framework job whose employees answer. Letters, digits, _ and -, up to 50 characters.opts?table— The same fields as a Config.Companies.List entry. See below.
Returns
true | false, errlabel: the name shown, up to 32 characters. Without it, the framework's label for the job is used.coords: avector3or{ x, y, z }. Directions sets the GPS here.icon: a built-in emblem (police,sheriff,ambulance,mechanic,taxi,realestate,news,lawyer) or anhttps://ornui://image.description: up to 160 characters.alwaysOpen = true: open with nobody working, for a place that needs no staff.number: a fixed phone number. Without it the company gets a number that never changes.call = false,message = false: no calls, or no texts.minGrade: the lowest job grade that answers calls and texts.hidden = true: keep it out of the directory.
An entry for the same job in Config.Companies.List wins over your
options, field by field. A job in Config.Companies.Hidden or
HiddenTypes stays hidden.
The company belongs to your resource and is removed when it stops. When
nx_phone restarts it is gone too, so register again from
nx_phone:sdkReady.
Errors: invalid_job, invalid_options, invalid_coords, job_taken
(another resource registered that job).
local function register()
if GetResourceState('nx_phone') ~= 'started' then return end
exports.nx_phone:RegisterCompany('burgershot', {
label = 'Burger Shot',
coords = vector3(-1183.5, -884.2, 13.8),
icon = 'nui://my_assets/burgershot.png',
description = 'Burgers, fries and shakes.',
})
end
AddEventHandler('nx_phone:sdkReady', register)
AddEventHandler('onResourceStart', function(res)
if res == GetCurrentResourceName() then register() end
end)exports.nx_phone:RemoveCompanyserverRemove a company your resource registered. Errors: not_found, not_owner. Never yields.
Parameters
jobstring— A job your resource registered.
Returns
true | false, errWhen a company opens or closes, the server raises
nx_phone:companyOpenChanged.
The open set is also in GlobalState.nxpCompaniesOpen as
{ [id] = true }, which the client exports below read.
Home
exports.nx_phone:DoorbellserverRing every online owner and key holder of the property with a notification that opens the Home app's doorbell sheet. Returns how many phones were notified. Limited by Config.Home.Doorbell.Cooldown.
Parameters
propertyIdstring | number— Property id as your housing script names it.visitorSrc?number— The player ringing.
Returns
true, number | false, errYapp
exports.nx_phone:YappPostserverPost to Yapp as an existing profile, for example a city account. Returns the post id. Errors: disabled, not_found, and the post's own checks.
Parameters
handlestring— The profile that posts.bodystring— Post text.
Returns
number | false, errexports.nx_phone:YappSetVerifiedserverSet or remove a profile's verified badge. The same as the yapp_verify command. Errors: invalid_tier, not_found.
Parameters
handlestring— The profile.tierstring— 'none', 'blue' or 'gold'.
Returns
true | false, errexports.nx_phone:YappGetProfileserverThe public profile: id, handle, displayName, avatar, verified, bio, isPrivate, followers, following, posts and createdAt.
Parameters
handlestring— The profile.
Returns
table | nilTap to Pay
Needs nx_realbanking. A charge always pays the merchant's job society,
as nx_realbanking decides it; a resource can never name a payee. When
Config.TapToPay.ExportResources is a list, only those resources may
call these.
exports.nx_phone:CreateTerminalChargeserverOpen a charge on a merchant's phone, as if they keyed it in Wallet. The nearest customer with their phone out gets the Pay sheet. Returns the charge key. Errors: forbidden, invalid_request, rate_limited, unavailable, and the charge's own checks.
Parameters
merchantSrcnumber— The player whose phone is the terminal. Their job must be able to bill.optstable— { amount, label? }
Returns
string | false, errlocal key, err = exports.nx_phone:CreateTerminalCharge(source, { amount = 45, label = 'Burger meal' })exports.nx_phone:GetTerminalChargeserver{ status, amount, total, payerIdentifier?, refId? }. status is, for example, waiting, paid, cancelled, expired or refunded. Listen to nx_phone:taptopay:paid instead of polling.
Parameters
chargeKeystring— A key from CreateTerminalCharge.
Returns
table | nilFind My
nx_computer uses these to show a character's phones. actor is the
character identifier of the person asking; actions that change a phone
only work for its owner.
exports.nx_phone:FindMyListserver{ success, devices }: the character's phones and where each one is.
Parameters
actorstring— Character identifier.
Returns
tableexports.nx_phone:FindMyLocateserver{ success, device, live, location? }, or { success = false, error }.
Parameters
phoneIdstring— A handset id.actorstring— Character identifier.
Returns
tableexports.nx_phone:FindMyPlaySoundserverMake the phone ring for Config.Beacon.SoundSeconds. { success, where, seconds }, or { success = false, error }.
Parameters
phoneIdstring— A handset id.actorstring— Character identifier of the owner.
Returns
tableexports.nx_phone:FindMySetLostserverTurn Lost Mode on or off. { success, devices }, or { success = false, error }.
Parameters
phoneIdstring— A handset id.actorstring— Character identifier of the owner.onboolean— Turn Lost Mode on or off.message?string— Shown on the lock screen.contact?string— A number to call, shown on the lock screen.
Returns
tableRobocalls
exports.nx_phone:GetRobocallCampaignsserverRobocall campaigns, newest first, for police tools. Each has id, ownerIdentifier, accountId, callerId, agentNumber, pitch, status, targets, placed, answered, screened, pressed and createdAt.
Parameters
filter?table— { since? (epoch ms), number? (caller ID shown, or the agent line) }
Returns
tableClient
Phone control
These act on the local player. Opening always goes through the phone item: the player must carry a handset, and a locked phone still asks for the passcode.
exports.nx_phone:OpenPhoneclientOpen the phone. Returns true when it opened or was already open, false when it was refused (no phone, disabled, or a can-open hook said no).
Parameters
opts?table— { phoneId? }: open that carried handset instead of the first one.
Returns
booleanexports.nx_phone:OpenPhone()exports.nx_phone:ClosePhoneclientPut the phone away.
Parameters
reason?string— Passed to nx_phone:closed. Default 'api'.
Returns
trueexports.nx_phone:TogglePhoneclientClose the phone if it is open (returns false), otherwise open it (returns true when it opened).
Returns
booleanexports.nx_phone:IsPhoneOpenclientWhether the phone is open.
Returns
booleanexports.nx_phone:SetPhoneDisabledclientThe client version of the server export. Each calling resource holds its own disable, an open phone closes, and a disabled phone does not rise for calls or notifications. Returns whether the phone is disabled after the call.
Parameters
disabledboolean— true to add your hold, false to release it.reason?string— Shown when the player tries to open the phone. Up to 120 characters.
Returns
booleanexports.nx_phone:SetPhoneDisabled(true, 'Not while swimming.')exports.nx_phone:IsPhoneDisabledclientWhether the phone is disabled, and a map of holder to reason. A holder is a resource name, or 'server' for a hold set with the server export.
Returns
boolean, tableexports.nx_phone:RegisterCanOpenclientAdd a check that runs before the phone opens. Returns an id for RemoveCanOpen. The hook is removed when your resource stops.
Parameters
fnfunction— fn(ctx) returning false and an optional reason to refuse.
Returns
numberctx is { kind, phoneId, isOpen }. kind is 'open', 'focus'
(taking over an incoming-call or notification peek) or 'watchdog'.
The watchdog asks again about every half second while the phone is open,
and a refusal closes it. Only an explicit false refuses; an error in
the hook allows the open.
exports.nx_phone:RegisterCanOpen(function(ctx)
if IsPedSwimmingUnderWater(PlayerPedId()) then
return false, 'Your phone would drown.'
end
end)exports.nx_phone:RemoveCanOpenclientRemove one of your can-open hooks. Only the resource that registered it can remove it.
Parameters
idnumber— An id from RegisterCanOpen.
Returns
booleanexports.nx_phone:IsInCallclientWhether the player is in a connected call or an outgoing call that is ringing out, and the live call's state ('dialing', 'ringing', 'active' and so on) when there is one.
Returns
boolean, string?exports.nx_phone:GetOpenPhoneIdclientThe id of the handset that is open, or nil when the phone is closed.
Returns
string | nilexports.nx_phone:IsPhoneUnlockedclientWhether the phone is unlocked.
Returns
booleanexports.nx_phone:StartCallclientOpen the phone and call the number, for a button like Call customer in your own UI. The phone opens through the item as usual, a locked phone asks for the passcode first, and the call starts once it is unlocked. The call is dropped if the phone is put away first or is not unlocked within 60 seconds. Errors: invalid_number, disabled, in_call, cannot_open (no phone carried, or a can-open hook refused). A call the phone refuses (no SIM, airplane mode) shows its reason in the Phone app.
Parameters
numberstring | number— The number to call: digits, + * # - ( ) . and spaces, up to 32 characters.opts?table— { hideCallerId?, phoneId? }: withhold the caller id; call from that carried handset instead of the first one.
Returns
true | false, err-- a "Call customer" button in your NUI
RegisterNUICallback('callCustomer', function(data, cb)
local ok, err = exports.nx_phone:StartCall(data.phone)
cb({ ok = ok, error = err })
end)Live Activities
The same four exports as on the server, for the local player. The
calling resource owns the activity, and the
nx_phone:liveActivity:* events fire on
this client.
exports.nx_phone:StartLiveActivityclientStart an activity on the local player's phone. Returns its id.
Parameters
contenttable— title is required. See the content fields.
Returns
string | false, errlocal id = exports.nx_phone:StartLiveActivity({
title = 'Taxi fare',
glyph = 'taxi',
value = '$12',
timer = { startedAt = GetCloudTimeAsInt() * 1000 },
})exports.nx_phone:UpdateLiveActivityclientUpdate one of your activities.
Parameters
idstring— An id from StartLiveActivity.patchtable— Fields to change.
Returns
true | false, errexports.nx_phone:EndLiveActivityclientEnd one of your activities.
Parameters
idstring— An id from StartLiveActivity.opts?table— { content?, linger? }
Returns
true | false, errexports.nx_phone:GetLiveActivitiesclientYour resource's activities on this client as a list of { id, owner, title, startedAt, updatedAt }.
Returns
tableCompanies
The same open state as the server exports, read from a replicated state, so they cost no round trip to the server.
exports.nx_phone:GetCompanyOpenclientWhether the company is open right now.
Parameters
jobOrIdstring— A job name or a company id.
Returns
booleanlocal label = exports.nx_phone:GetCompanyOpen('taxi') and 'Open' or 'Closed'exports.nx_phone:GetOpenCompaniesclientEvery open company as a set, { [id] = true }.
Returns
tableSDK apps
exports.nx_phone:SendAppMessageclientSend data to your app's page (nxp.onMessage). Returns false and drops the message when the app is not on screen, the data is too large, or your resource passed 30 messages a second.
Parameters
namestring— The name your app was registered with.dataany— Anything JSON can carry, up to 32 KB.
Returns
booleanexports.nx_phone:SendAppMessage('dice', { kind = 'nearby', total = 7 })exports.nx_phone:IsAppOpenclientWhether your app is on the phone's screen.
Parameters
namestring— The name your app was registered with.
Returns
booleanSound
exports.nx_phone:AudioclientPlay the phone's own sounds on this client only. 'ring' loops a ringtone until 'stop'; 'isRinging' reports whether one is playing.
Parameters
actionstring— 'play', 'ring', 'stop', 'notify' or 'isRinging'.name?string— For play and ring: rim, pulse, chime, quiet or notify.
Returns
booleanItem hooks
These are wired into the ox_inventory item definitions from Installation and run when a player uses the item. Do not call them from your own code.
| Export | Side | Item |
|---|---|---|
usePhone | client | nx_phone |
nx_phone | server | nx_phone |
useSim | client | nx_sim |
nx_sim | server | nx_sim |
useProtector | server | nx_phone_protector |