API and Exports
This is the supported integration surface for resources that need to control or read src-phone. Client exports are called from a client script; server exports are called from a server script. Internal src-phone:client:* and src-phone:server:* transport events are not public API.
Result conventions
Commands normally return true on success or false, errorCode when validation fails. Read-only exports return the requested value or nil when it is unavailable. Database-backed client reads use a callback because FiveM client exports cannot synchronously wait for a server callback.
local ok, reason = exports['src-phone']:openApp('messages', { number = '555-1234' })
if not ok then
print(('phone request failed: %s'):format(reason))
endClient exports
Phone and application control
openPhone
options?
boolean, error?
Opens the phone. options.appId and options.params can launch an app.
closePhone
none
boolean, error?
Closes the phone and releases its normal focus flow.
togglePhone
none
boolean, error?
Toggles the phone; the boolean is the resulting open state.
isPhoneOpen
none
boolean
Returns current visibility.
getPhoneState
none
table
Returns open, locked, active app, call, airplane and online state.
openApp
appId, params?
boolean, error?
Opens an installed built-in or registered external app.
closeApp
none
boolean
Returns to the phone home screen.
getActiveApp
none
string?
Returns the active app ID.
refreshApp
appId
boolean, error?
Increments that app's refresh key.
isAppInstalled
appId
boolean
Checks the player's installed-app list.
getInstalledApps
none
string[]
Returns installed app IDs.
External application registry
registerApp
manifest
boolean, error?
Registers an external app owned by the calling resource.
unregisterApp
appId
boolean
Removes an app owned by the calling resource.
isAppRegistered
appId
boolean
Checks the runtime registry.
getRegisteredApps
none
manifest[]
Returns accepted, sanitized runtime manifests.
sendAppEvent
appId, action, payload?
boolean, error?
Delivers a typed event to an external app iframe.
Registration takes metadata from the external resource's root config.lua. See Custom applications for the complete manifest and resource lifecycle.
The owner is always GetInvokingResource(). Re-registering the same ID from the same resource updates it atomically; another resource cannot claim that ID. Registrations are removed automatically when their owner stops.
Registration errors:
manifest_must_be_a_table
The manifest is not a Lua table.
unsupported_api_version
apiVersion is missing or unsupported.
invalid_id
The ID fails the length or kebab-case rules.
reserved_id
The ID belongs to a built-in application.
id_already_registered
A different resource already owns the ID.
invalid_name
The display name is empty or longer than 18 Unicode characters.
invalid_ui_page / invalid_icon
An asset is not a safe relative resource path.
invalid_accent_color
The accent is not a six-digit hex color.
invalid_permissions / unsupported_permission
The permissions table is invalid or requests an unsupported capability.
invalid_icon_class
The fallback class contains unsupported characters or is too long.
external_resource_required
The call did not originate from another resource.
resource_not_started
The owner resource is not starting or started.
The external TypeScript app receives the event through the vendored SDK:
Notifications and badges
sendNotification
notification
id?, error?
removeNotification
id
boolean, error?
clearNotifications
appId?
boolean, error?
getUnreadNotificationCount
appId?
number
setAppBadge
appId, count
boolean, error?
clearAppBadge
appId
boolean, error?
Notification limits are 80 characters for title, 500 for body, and 1-15 seconds for duration. Pressing a public notification opens appId with params and emits src-phone:client:notificationPressed.
Identity and connectivity
getPhoneProfile
callback
boolean, error?
getPhoneNumber
callback
boolean, error?
getMailAddress
callback
boolean, error?
isPhoneAvailable
none
boolean
getConnectivity
none
{ online, airplaneMode }
isOnline
none
boolean
hasInternetAccess
none
boolean
getAirplaneMode
none
boolean
Calls
startCall
number, options?
boolean, error?
answerCall
number?
boolean, error?
declineCall
number?
boolean, error?
endCall
number?
boolean, error?
getCallState
none
table?
isInCall
none
boolean
setSpeakerEnabled
enabled
boolean, error?
options.video = true requests a video call. Call state is server-authoritative; a successful request does not guarantee that the receiver is online or accepts the call.
Messages and mail
sendMessage
number, message, options?
boolean, error?
openConversation
number
boolean, error?
markConversationRead
number
boolean, error?
getUnreadMessageCount
none
number
sendMail
address, { subject?, body }
boolean, error?
openMail
mailId?
boolean, error?
getUnreadMailCount
none
number
Contacts, pickers, media and location
getContacts
callback
boolean, error?
getContactByNumber
number, callback
boolean, error?
addContact
{ name, number, avatar? }
boolean, error?
editContact
id, { name, number }
boolean, error?
deleteContact
id
boolean, error?
openContactPicker
options, callback
boolean, requestId?
openPhotoPicker
options, callback
boolean, requestId?
openLocationPicker
options, callback
boolean, requestId?
openShareSheet
options, callback
boolean, requestId?
openConfirmDialog
options, callback
boolean, requestId?
openCamera
options?
boolean, error?
openGallery
params?
boolean, error?
saveSharedMedia
{ url, mediaType? }, callback
boolean, error?
getCurrentLocation
none
{ street, x, y, z }
setPhoneWaypoint
{ x, y }
boolean, error?
openMap
params?
boolean, error?
shareLocation
number, options?
boolean, error?
stopLocationShare
none
boolean
Pickers are asynchronous. Cancellation and the 60-second timeout both return nil to the callback.
Client lifecycle events
These are local client events. Listen with AddEventHandler; do not send them over the network.
src-phone:client:phoneOpened
none
src-phone:client:phoneClosed
none
src-phone:client:appOpened
appId
src-phone:client:appClosed
appId
src-phone:client:connectivityChanged
online
src-phone:client:callStateChanged
callState?
src-phone:client:notificationPressed
{ id, appId, params }
Server exports
Server exports always take an explicit player source when the operation acts on a player. This makes ownership visible at the call site and prevents accidental access through the event sender global.
Identity and state
getPhoneNumber
source
string?
getMailAddress
source
string?
getPhoneIdentity
source
table?, error?
getSourceByPhoneNumber
number
number?
getIdentifierByPhoneNumber
number
string?
isPhoneNumberOnline
number
boolean
isPhoneAvailable
source
boolean
getConnectivity
source
table?, error?
getPlayerPhoneState
source
table?
Player UI, notifications and app events
openApp
source, appId, params?
boolean, error?
sendNotification
source, notification
boolean, error?
removeNotification
source, id
boolean, error?
clearNotifications
source, appId?
boolean, error?
setAppBadge
source, appId, count
boolean, error?
clearAppBadge
source, appId
boolean, error?
sendAppEvent
source, appId, action, payload?
boolean, error?
Calls, messages and mail
requestCall
source, number, options?
boolean, error?
endPlayerCall
source
boolean, error?
getPlayerCallState
source
table?
isNumberBusy
number
boolean
sendMessage
source, number, message, options?
boolean, error?
sendSystemMessage
number, message, options?
boolean, id/error?
getUnreadMessageCount
source
number
sendMail
source, address, data
boolean, error?
sendSystemMail
address, data
boolean, id/error?
sendMailToPlayer
source, data
boolean, id/error?
getUnreadMailCount
source
number
sendMessage and sendMail act as the player and follow the normal player-owned validation path. sendSystemMessage and sendSystemMail create server-authored records and should only be called after the integrating resource has completed its own authorization checks.
Server contact access
getContacts
source
contacts, error?
addContact
source, contact
boolean, id/error?
editContact
source, id, contact
boolean, error?
deleteContact
source, id
boolean, error?
Contacts are scoped to the player's active MyCloud account. The exports do not accept an arbitrary account or owner key.
Validation and security boundary
The public API validates IDs, phone numbers, text lengths, URLs, badge ranges, player sources and external manifest ownership. It deliberately does not expose:
Bank balance mutations or transfers.
Another player's message history, mailbox contents or contacts without an explicit player source.
Raw SQL, KV storage or MyCloud owner keys.
RTC channels, call routing tables or raw internal network events.
Shell DOM access or unrestricted overlay creation.
An export confirms that a request was accepted; it does not replace authorization in the calling resource. Job checks, permissions, prices, inventory changes and rate limits remain the caller's server-side responsibility.
Start order
For an external application, also declare dependency 'src-phone' in its fxmanifest.lua. Registration is client-side and the boilerplate automatically re-registers when src-phone restarts.
Last updated

