Docs
RARX Phone cover

RARX Phone · Exports & events

Exports & events

Server and client exports, events, admin commands and service numbers of rarx_phone, with drop-in compatibility for exports['lb-phone'].

  • Qbox
  • QBCore
  • ESX
  • v2.0.0
  • rarx_phone

Overview#

This page lists the exports and events that rarx_phone offers to other resources, with their equivalent in LB Phone 2.9.0. Custom apps (AddCustomApp, RemoveCustomApp, SendCustomAppMessage, GetCustomApps) have their own page: External apps API.

LB Phone compatibility#

Scripts written for LB Phone usually call exports['lb-phone']:..., often after checking GetResourceState('lb-phone') == 'started'. rarx_phone keeps those scripts working without edits through two mechanisms.

provide 'lb-phone'#

The rarx_phone fxmanifest.lua includes provide 'lb-phone'. According to the FiveM manifest documentation, provide marks the resource as a replacement for the named resource: it starts in its place when something requests it and behaves as if it were that resource. As a result, GetResourceState('lb-phone') returns the state of rarx_phone.

FXServer first looks up the real resource name and, if it does not exist or is stopped, falls back to the started resource that provides it. So rarx_phone wins as long as the original lb-phone is not started.

Export aliases#

provide does not forward exports on its own: exports['lb-phone']:X() fires the local event __cfx_export_lb-phone_X, which nobody would handle. For this reason every compatible export is registered twice:

  • exports('X', fn) for exports.rarx_phone:X
  • AddEventHandler('__cfx_export_lb-phone_X', function(setCB) setCB(fn) end) for exports['lb-phone']:X

Both point to the same function, with the same arguments and return values as LB. GetInvokingResource() still returns the calling resource, so ownership of custom apps works the same way.

Hot restart of rarx_phone: each resource caches the export functions it has used and clears that cache on onServerResourceStop / onClientResourceStop of the real resource name. When rarx_phone stops, it also fires onServerResourceStop('lb-phone') and onClientResourceStop('lb-phone'), so scripts that use exports['lb-phone'] do not keep calling dead functions after a restart.

Compatibility options#

Options live in config/compat.lua:

Key Default What it does
Config.Compat.LbExports true Registers the exports['lb-phone'] aliases.
Config.Compat.LbEvents true Also fires events with LB names (lb-phone:*). The rarx_phone:* events are always fired.
Config.Compat.ResetCommand 'resetphonesecurity' Admin command that removes PIN and face unlock. '' disables it.
Config.Compat.VerifyCommand 'toggleverified' Admin command that toggles the verified badge of a social account. '' disables it.
Config.Compat.EmergencyApp / EmergencyTitle / EmergencyContent 'settings' and default texts App, title and default text for EmergencyNotification.
Config.Compat.MailSender / MailSubject / MailMaxActions 'Sistema', 'Aviso del sistema', 4 Default sender and subject for SendMail, and maximum number of buttons per mail.
Config.Compat.AppNames table Maps LB app names (Messages, Mail, Birdy, Wallet...) to rarx app identifiers (messages, mail, birdy, bank...). Names not in the table are used as-is (custom apps).
Config.Compat.LbChecks table Maps LB check names (postBirdy, sendDarkchatMessage, buyCrypto...) to the rarx RPC method.

Detect which phone is running#

If your script must work with both phones, check which resource is started and call that one:

Lua
local function startedPhone()
    if GetResourceState('rarx_phone') == 'started' then return 'rarx_phone' end
    if GetResourceState('lb-phone') == 'started' then return 'lb-phone' end
end

local phone = startedPhone()
if phone then
    pcall(function()
        exports[phone]:SendNotification(number, { title = 'Hello', content = '...' })
    end)
end

Check rarx_phone first: because of provide 'lb-phone', GetResourceState('lb-phone') also returns started when only rarx_phone is running. Every LB-named export listed below exists with the same name on exports.rarx_phone.

Server exports#

In the "LB" column, "yes" means the same name and signature is available on exports['lb-phone']; "rarx only" means it exists only on exports.rarx_phone.

Export Returns LB
GetEquippedPhoneNumber(source | citizenid) number (string) or nil yes
GetSourceFromNumber(phoneNumber) source or false yes
SendNotification(source | phoneNumber, data, cb?) notification id, true or false yes
EmergencyNotification(source, data) true / false yes
SendAmberAlert(source, data) true / false yes
NotifyEveryone('all' | 'online', data) true / false yes
SendMessage(sender, recipient, content?, attachments?, cb?) { channelId, messageId } or nil yes
SendMail(data) true, mailId or false, reason yes
GetEmailAddress(source | phoneNumber | citizenid) address or nil yes
ResetSecurity(phoneNumber) true / false yes
FactoryReset(phoneNumber) true / false yes
ChangePassword(app, username, password) true / false yes
AddCheck(event, fn) id yes
RemoveCheck(id) true / false yes
FormatNumber(number) formatted string yes
IsInCall(source) true / false yes
HasPhoneItem(source) true / false yes
HasAirplaneMode(phoneNumber | source) true / false yes
ToggleVerified(app, username, verified) true / false yes
IsVerified(app, username) true / false yes
GetSocialMediaUsername(phoneNumber | source, app) username or nil yes
CreateCall(caller, callee, options?) callId or nil yes
EndCall(source) true / false yes
GetCall(callId) call table or nil yes
GetPlayerCall(source) callId, call or nil yes
AddTransaction(phoneNumber | source, amount, company, logo?) entry id or false yes
ToggleDisabled(source, disabled) true / false rarx only (client export in LB)
GetNumberByCid(citizenid) number (created if missing) rarx only
GetPlayerNumber(source) number of the open session or nil rarx only
SendSystemMessage(phoneNumber, text) true / false rarx only
SetWidgetData(source | citizenid, widgetId, value) nothing rarx only

Numbers and notifications#

GetEquippedPhoneNumber(source | citizenid)server export → string|nil

With a source, returns the number of the player's phone session. If there is no session, it checks that the player carries a phone and reads its number from rarx_phone_numbers (it never creates one). Returns nil if the player has no phone on them or is offline.

With a string (citizenid), it uses the player if they are online and otherwise reads the database, like LB.

GetSourceFromNumber(phoneNumber)server export → number|false

Returns the server id of the online player who owns that number, or false.

SendNotification(target, data, cb?)server export → id|boolean

Same behaviour as LB:

  • If target is a player id (source), the notification is transient: it shows, plays a sound and is not stored.
  • If target is a phone number (string) and data.app is set, it is stored in rarx_phone_notifications and delivered instantly if the owner is online.

Accepted fields: app, title, content (or description), thumbnail, avatar, showAvatar, icon, customData (or data) and actions. Content is cut at 500 characters. cb(value) receives the same value that is returned.

Lua
exports['lb-phone']:SendNotification('551234', { app = 'alertadefcon', title = 'DEFCON 3', content = 'Moderate alert' })
exports.rarx_phone:SendNotification(source, { app = 'Messages', title = 'Dispatch', content = 'Unit on the way' })

Transient notification flagged critical = true, with icon = 'warning' and the app set in Config.Compat.EmergencyApp. Like LB it is per player; to reach everyone, loop over GetPlayers() or use NotifyEveryone.

SendAmberAlert is an alias with the same arguments and behaviour.

'online' only shows it to connected players. 'all' also stores it for everyone who has a phone number.

FormatNumber(number)server export → string

Returns the number formatted with Config.PhoneNumber.Format.

GetNumberByCid(citizenid)server export → string (rarx only)

Returns the character's number, creating one if they do not have any.

GetPlayerNumber(source)server export → string|nil (rarx only)

Returns the number of the player's open phone session, or nil.

SendSystemMessage(phoneNumber, text)server export → boolean (rarx only)

Sends a system message to that number.

Pushes data to a home screen widget of that player.

Messages and mail#

Number-to-number message. If the sender does not belong to any player (an NPC, a dispatch line), the message is still created with that number as sender.

  • attachments is a list of URLs (or tables with url). Only hosts allowed in Config.Messages.MediaHosts are accepted.
  • With attachments the message is an image message (or a video message if all are videos) and the text becomes the caption.
  • The recipient must have a rarx_phone number.
  • LB's sixth argument (channelId) is ignored: only direct messages are supported.
Lua
exports['lb-phone']:SendMessage('900200', '551234', 'Meet me at the docks.', { 'https://r2.fivemanage.com/x.jpg' })
SendMail({ to, sender?, subject?, message, attachments?, actions? })server export → true, mailId | false, reason

to can be an address ([email protected]), 'all' (every account), a source, a phone number or a citizenid. The recipient needs a mail account. sender is free text ('Car Dealer').

Buttons use the same format as LB, including data.qbMail:

Lua
actions = { { label = 'Accept', data = { event = 'my_resource:accept', isServer = true, data = { id = 4 } } } }

Buttons are stored in the actions column (migration 053_mail_actions.sql, applied automatically on start). Without that migration the mail is sent without buttons.

When the player presses a button, the server checks that the mail belongs to them and sends the action to their client, which runs TriggerServerEvent(event, mailId, data) if isServer, or TriggerEvent(event, mailId, data) otherwise, like LB.

Lua
exports['lb-phone']:SendMail({
    to = exports['lb-phone']:GetEmailAddress(source),
    sender = 'Premium Dealership',
    subject = 'Your car is ready',
    message = 'Come and pick it up.',
    actions = { { label = 'Set route', data = { event = 'dealership:route', isServer = false } } },
})

Returns the character's mail address, or nil.

Security and reset#

ResetSecurity(phoneNumber)server export → boolean

Removes the PIN and face unlock of the owner of that number (including the face enrollments of their phones) and clears the failed-attempts lock. If the owner is online, their session is unlocked and the phone closes so it starts again without PIN. Also accepts a source.

FactoryReset(phoneNumber)server export → boolean

Does the same as ResetSecurity, also wipes the settings and shows the setup wizard again. It does not delete messages, photos, notes or mail (LB does not either). If the owner is online, the phone is dropped and factoryReset and numberChanged are fired.

Changes the password of a phone account. The matching admin command (default /changepassword) accepts birdy, twitter, rrx, instagram, instapic, tiktok, trendy, mail and darkchat. Returns false if the account is not found.

Checks#

AddCheck(event, fn)server export → id

event can be any rarx RPC method ('calls:start', 'messages:send', 'bank:transfer', 'mail:send'...), '*' (all of them) or an LB name from Config.Compat.LbChecks. If the function returns false, the action is rejected with err_app_blocked.

The function always receives (source, params, method), not the individual arguments of each LB event. A check that throws an error is logged in the console and does not block. Checks are removed automatically when the resource that added them stops.

Lua
exports['lb-phone']:AddCheck('calls:start', function(source)
    return not Player(source).state.isHandcuffed
end)
RemoveCheck(id)server export → boolean

Removes a check added with AddCheck.

Player state#

IsInCall(source)server export → boolean

Whether the player is in a call.

HasPhoneItem(source)server export → boolean

Whether the player carries a phone.

HasAirplaneMode(phoneNumber | source)server export → boolean

With the player online it reads their session; otherwise their saved settings.

ToggleDisabled(source, disabled)server export → boolean (rarx only)

Server-side version of the client ToggleDisabled: closes the player's phone and prevents opening it while disabled is true.

Social accounts#

Sets the verified badge. app accepts the LB names: 'Twitter' / 'Birdy' map to Birdy accounts; 'Instagram', 'InstaPic', 'TikTok', 'Trendy' and 'rrx' map to RRX accounts (which also hold clips). username with or without @.

When verifying, the owner gets a notification, toggleVerified is fired and the action is written to the admin log. Returns false if the account does not exist or the app is not a social app.

IsVerified(app, username)server export → boolean

Reads the verified badge, with the same app names as ToggleVerified.

Username of the account used by the owner of that number: on Birdy the account of the active session (or the first one), on RRX their account. With app = 'Mail' it returns the mail address.

Calls#

CreateCall(caller, callee, options?)server export → callId|nil

caller is { source = id }, a source or the number of an online player. callee is a number or an emergency service ('police', '112'... from Config.Calls.Emergency); if missing, options.company is used.

It opens the caller's phone and dials from their screen, so it goes through the same checks as a normal call (airplane mode, coverage, busy, checks). Options: videoCall, requirePhone. hideNumber is not applied: the player's own "Hide my number" setting decides.

It waits up to 8 seconds for the call to start and returns its callId (or nil). Call it from a thread (CreateThread or an event handler).

Lua
CreateThread(function()
    local id = exports['lb-phone']:CreateCall({ source = source }, '552222')
end)
EndCall(source)server export → boolean

Hangs up (or rejects, if it is ringing) the player's call through the same path as the red button.

GetCall(callId)server export → table|nil

Returns the call table, the same one received by newCall and callEnded.

GetPlayerCall(source)server export → callId, call | nil

Returns the current call of that player and its call table.

Bank#

Admin commands#

All three use the same admin permissions as the rest of rarx_phone (Config.Admin.Ace, group.admin / group.god, framework or license-based admins) and also work from the server console.

Removes PIN and face unlock from the phone of the player with that server id. The action is written to the Discord log. The name is configurable in Config.Compat.ResetCommand.

Sets or removes the verified badge. app: birdy, lenz, rrx, twitter, instagram, tiktok, trendy, instapic. 1 (default) verifies, 0 removes, 2 gives the gold badge (Birdy only). The name is configurable in Config.Compat.VerifyCommand.

Changes the password of a phone account and closes its open sessions. app: birdy, twitter, rrx, instagram, instapic, tiktok, trendy, mail, darkchat. The name is configurable in Config.Compat.PasswordCommand.

Client exports#

Export Returns LB
ToggleOpen(open?) nothing yes (toggles without open)
IsOpen() true / false yes
ToggleDisabled(disabled) nothing yes
IsDisabled() true / false yes
AddCheck('openPhone', fn) id yes
RemoveCheck(id) true / false yes
GetEquippedPhoneNumber() number or nil yes
HasPhoneItem() true / false yes
SaveToGallery(url) true (saved in the background) yes
GetBattery() 0-100 (100 if battery is disabled) yes
SetBattery(level) nothing yes
IsCharging() true / false yes
ToggleCharging(state) nothing yes
IsPhoneDead() true / false yes
SendNotification({ app?, title, content, avatar?, thumbnail? }) true yes
UsePhoneItem(item, slot) nothing yes
IsInCall() true / false yes
IsPhoneOnScreen() true / false yes
GetSettings() settings table, or nil before boot yes
GetAirplaneMode() true / false yes
GetStreamerMode() true / false yes
GetFlashlight() true / false yes
OpenApp(app, metadata?) true / false yes
CloseApp({ app?, closeCompletely? }?) true / false yes
CreateCall({ number | company, videoCall?, name? }) true / false yes
SetServiceBars(bars | false) nothing yes
GetServiceBars() 0-4 rarx only
AddCustomApp / RemoveCustomApp / SendCustomAppMessage see External apps API yes
Open / Close / GetNumber / UsePhone / ShowNotification / ShowServerNotification / GetCustomApps / openManagement rarx only
ToggleOpen(open?)client export

Opens (true) or closes (false) the phone. Without arguments it toggles.

Closes the phone and prevents opening it while disabled is true. Can also be called from the server with exports.rarx_phone:ToggleDisabled(source, true).

AddCheck('openPhone', fn)client export → id

If fn returns false, the phone does not open. Remove it with RemoveCheck(id). Checks are removed automatically when the resource that added them stops.

SaveToGallery(url)client export → boolean

Saves the URL to the player's Gallery through the same path as the app: allowed hosts, photo limit, no duplicates. The player must carry the phone.

SetBattery(level)client export

Sets the battery level. Does nothing if the battery system is disabled, like LB.

Forces charging on or off. Applied on the next check (at most 3 seconds). false does not stop charging if the player is standing next to a charger placed from the panel.

Kept for older items configured with export = 'lb-phone.UsePhoneItem'.

OpenApp(app, metadata?)client export → boolean

Opens the phone if needed and then the app. The name goes through Config.Compat.AppNames ('Wallet' becomes bank, 'Birdy' becomes birdy...); names not in the table are used as-is (custom apps). metadata reaches the app as open parameters. An app that does not exist or is disabled is not opened.

CloseApp({ app?, closeCompletely? }?)client export → boolean

Without app it goes back to the home screen. With closeCompletely the app is also removed from recents.

Opens the phone and dials from the screen, as if the player had pressed call. Does nothing if the player is already in a call.

Forces the coverage bars to a value from 0 to 4. false or nil returns to automatic coverage.

Events#

All events are local (AddEventHandler), never network events. rarx_phone:* events are always fired; lb-phone:* events are fired when Config.Compat.LbEvents is enabled.

Server events#

rarx_phone:newCallserver event · LB: lb-phone:newCall

Arguments: call. A call starts ringing.

rarx_phone:callAnsweredserver event · LB: lb-phone:callAnswered

Arguments: call, plus the source of the player who answered (rarx only). The call is answered.

rarx_phone:callEndedserver event · LB: lb-phone:callEnded

Arguments: call, source. The call ends: hung up, rejected, unanswered, disconnect or maximum duration.

rarx_phone:messageSentserver event · LB: lb-phone:messages:messageSent

Arguments: { channelId, messageId, sender, recipient, message, attachments, type?, source? }. A message is sent from the app or with SendMessage. In groups recipient is nil.

rarx_phone:mailSentserver event · LB: lb-phone:mail:mailSent

Arguments: { id?, to, sender, subject, message, attachments, actions, read, timestamp, source? }. A mail is sent from the app or with SendMail.

rarx_phone:numberChangedserver event · LB: lb-phone:numberChanged

Arguments: source, number (or nil after a reset). An admin changes or approves a number, or FactoryReset runs.

rarx_phone:factoryResetserver event · LB: lb-phone:factoryReset

Arguments: source, number. FactoryReset runs with the player online.

rarx_phone:securityResetserver event (rarx only)

Arguments: source | nil, number. ResetSecurity or the reset command runs.

rarx_phone:newPostserver event

Arguments: kind, data. Fired for several apps; the LB events receive only the data table.

kind data LB event
'rrx' { id, source, citizenid, username, media, caption, isClip } lb-phone:instapic:newPost
'ads' { id, source, number, title, description, price, attachments } lb-phone:pages:newPost
'blackmarket' { id, source, title, description, price } lb-phone:marketplace:newPost
'news' { id, source, title, content, image } none
rarx_phone:darkchatMessageserver event · LB: lb-phone:darkchat:newMessage

Arguments: room, alias, content, plus source (rarx only). A message is posted in DarkChat.

rarx_phone:birdy:newPostserver event · LB: lb-phone:birdy:newPost

Arguments: { id, accountId, username, content, media, replyTo, quoteOf }. A post or reply is published on Birdy.

rarx_phone:toggleVerifiedserver event · LB: lb-phone:toggleVerified

Arguments: app, username, verified. rarx passes 'birdy' or 'rrx'; the LB event passes the app name that was received.

rarx_phone:onAddTransactionserver event · LB: lb-phone:onAddTransaction

Arguments: 'received' | 'paid', number, amount, description, logo. Fired by AddTransaction.

rarx_phone:settingsUpdatedserver event (rarx only)

Arguments: source, settings. The player saves their settings.

rarx_phone:customAppserver event (rarx only)

Arguments: source, identifier, action, data. See External apps API.

The call table#

The LB shape plus citizenid and emergency:

Lua
{
    callId = 'call_123_1', started = 1762400000, answered = false, answeredAt = nil, duration = nil,
    videoCall = false, hideCallerId = false, emergency = nil,
    caller = { source = 1, number = '551111', citizenid = 'ABC123' },
    callee = { source = 2, number = '552222', citizenid = 'DEF456' },
}

Client events#

rarx_phone:phoneToggledclient event · LB: lb-phone:phoneToggled

Arguments: open. The phone is opened or closed.

rarx_phone:numberChangedclient event · LB: lb-phone:numberChanged

Arguments: number | nil. Number change or reset.

rarx_phone:phoneDiedclient event · LB: lb-phone:phoneDied

No arguments. The battery reaches 0.

rarx_phone:settingsUpdatedclient event · LB: lb-phone:settingsUpdated

Arguments: settings. The player changes a setting (fired after it is saved on the server).

rarx_phone:customAppclient event (rarx only)

Arguments: identifier, action, data. See External apps API.

LB features not available#

Everything in the tables above is available. The following LB Phone exports and events are not implemented in rarx_phone:

  • Server: GetPin (the PIN is stored hashed and cannot be returned), CreateMailAccount, DeleteMail, GetSettings, ContainsBlacklistedWord, PostBirdy / GetBirdyPost, DeleteBirdyAccount / DeleteInstaPicAccount / DeleteTrendyAccount, the DarkChat exports (SendDarkChatMessage, CreateDarkChatChannel...), the Crypto exports (AddCrypto, GetCoin...), SentMoney, SendCoords, AddContact, AirShare, IsPhoneDead, SaveBattery / SaveAllBatteries.
  • Client: ToggleFlashlight, AddContact, IsLive, ToggleHomeIndicator, ToggleLandscape, SetPopUp, SetContextMenu, SetContactModal, ShowComponent, SetCameraComponent, the camera exports (EnableWalkableCam, ToggleSelfieCam...), SetAppHidden / SetAppInstalled, ReloadPhone, DeleteNotification, SetPhoneVariation and custom numbers (CreateCustomNumber, CreateDynamicCustomNumber, EndCustomCall). For scripted phone lines, use service numbers.
  • Events: lb-phone:phoneNumberGenerated, newCompanyMessage, trendy:newPost, deletedFromGallery, setOnScreen, keyPressed, jobUpdated, toggleHud.

Phone prop in hand#

Each client spawns the phone prop as a local (non-networked) object. When the player takes out the phone, the client sets the replicated player state bag rarxPhoneProp = <model hash> and sets it back to false when the phone is put away, dropped (death, item removed, cuffs, water) or the resource stops.

Other clients read that state bag and create their own local prop attached to that player's ped while it is streamed in and within Config.Animation.PropDistance metres (120 by default). They remove it when the player goes out of range, dies, disconnects or puts the phone away. Only models listed in Config.Animation.Prop and Config.Animation.PropByModel (optional table { [phone model] = prop model }) are accepted; any other state bag value is ignored.

Animations: standing cellphone@, passenger anim@cellphone@in_car@ps, driver anim@cellphone@in_car@ds (falls back to the passenger set if the driver one does not load).

Screen errors#

Phone UI errors are still printed in the player's F8 console and are also sent to the server (at most 5 per minute per player, text cut at 900 characters; the server checks both limits again). They are logged under the errors category ("Screen errors"), which can have its own webhook through Config.Logs.Categories.errors or the convar rarx_phone_logs_webhook_errors. With Config.Debug enabled they are also printed in the server console.

Service numbers#

Another resource can "answer" a phone number. When a player calls it, onCall runs with a keypad menu (IVR); when a player texts it from Messages, onMessage runs. The number does not need to exist in rarx_phone_numbers; if it matches a player's number, the service wins and a warning is printed in the console. Everything can be turned off with Config.ServiceNumbers.Enabled = false (config/extra/tecnico.lua).

All service number exports are rarx only.

RegisterServiceNumber(number, options)server export → true | false, reason

Registers a service number. On failure reason is numero (invalid number) or ocupado (another resource already owns it).

UnregisterServiceNumber(number)server export → boolean

Removes a service number. Only the resource that registered it can remove it.

Sends a message from the service number. target can be an online source or a phone number; the chat is created like any other conversation and the player can reply. image must be a URL from Config.Messages.MediaHosts.

Says a line in an active service call and optionally replaces the current menu.

EndServiceCall(callId)server export → boolean

Hangs up a service call.

GetServiceNumbers()server export → table

Returns { { number, name, avatar, resource } }.

Options#

Field Type Description
name string Name shown to the player in the call, call history, chat and notifications (unless they saved it as a contact).
avatar URL Avatar for the call and the chat.
menu menu Initial menu (see below). Spoken on answer unless onCall returns another one.
onCall function(source, number, callId) On answer (after Config.ServiceNumbers.AnswerDelay ms). May return a menu.
onKey function(source, key, callId) A key that is not in the current menu was pressed. May return a menu.
onHangup function(source, number, callId) End of the call (player hangs up, script hangs up or disconnect).
onMessage function(source, text, number, playerNumber) Text message received. If it returns a string, that string is sent as the reply.
maxDuration seconds Automatic cut-off (default Config.ServiceNumbers.MaxDuration, 600).
Lua
{
    title = 'City Hall',
    text = 'Welcome. Press 1 for licenses, 2 to talk to an agent or 9 to hang up.',
    invalid = 'Invalid option.',
    repeatKey = '*',
    options = {
        { key = '1', label = 'Licenses', menu = { text = 'Press 1 for weapons or # to go back.', options = { ... } } },
        { key = '2', label = 'Agent', action = function(src, key, callId) ... end },
        { key = '9', label = 'Hang up', say = 'Thanks for calling.', hangup = true, hangupDelay = 2500 },
    },
}

Each option accepts key (0-9, *, #), label, say (a line), menu (submenu), action (function, may return a menu) and hangup. The screen shows the title, the last line and the option buttons; the call keypad also sends keys. Lines are read aloud with the browser voice (speechSynthesis) if available and Config.ServiceNumbers.Speech is not false; otherwise only the text is shown.

Example#

Lua
exports.rarx_phone:RegisterServiceNumber('5550100', {
    name = 'Downtown Cab',
    menu = {
        text = 'Downtown Cab. Press 1 to order a cab or 2 to hear our fares.',
        options = {
            { key = '1', label = 'Order cab', action = function(src)
                TriggerEvent('my_taxi:order', src)
                return { text = 'A cab is on its way to you. Thank you!', options = {} }
            end, hangup = true },
            { key = '2', label = 'Fares', say = 'Flag fall is 20 dollars plus 3 per kilometre.' },
        },
    },
    onMessage = function(src, text)
        if text:lower():find('taxi') then
            TriggerEvent('my_taxi:order', src)
            return 'Got it, a cab is on its way.'
        end
        return 'Type "taxi" to order one.'
    end,
})

exports.rarx_phone:SendServiceMessage('5550100', source, 'Your cab has arrived.', { location = { x = 215.0, y = -810.0, z = 30.0, label = 'Here' } })