How Integrations Work#
When the server starts, the phone looks for the resources below by name and uses the first one it finds running. You do not set anything. Each one is wired through a small, unencrypted bridge file you can read and edit.
Layer | Supported | Without it |
|---|---|---|
Framework | es_extended · qb-core · qbx_core · vrp (experimental) | Standalone: no jobs or framework money. |
Inventory | qs-inventory · ox_inventory · qb-inventory · l2s-inventory · ps-inventory · origen_inventory · core_inventory · codem-inventory · tgiann-inventory · ak47_inventory · ak47_qb_inventory | Phone data is tied to the character instead of the item. |
Garages | qs-advancedgarages · jg-advancedgarages · cd_garage · loaf_garage · okokGarage · codem-garage · vms_garagesv2 · lunar_garage · RxGarages | The Garages app uses a basic vehicle list. |
Vehicle keys | qs-vehiclekeys · qb-vehiclekeys · mono_carkeys · vehicle_keys · wasabi_carlock · mk_vehiclekeys · okokGarage | Valet and rental cars are handed over without keys. |
Billing | qs-billing · okokBilling · esx_billing · codem-billingv2 · RxBilling | Built-in billing and the |
Ambulance | qs-medical-creator · esx_ambulancejob · qb-ambulancejob · qbx_medical · wasabi_ambulance · ars_ambulancejob · p_ambulancejob · ak47_ambulancejob · ak47_qb_ambulancejob | Death is detected from the ped's health for the SOS. |
Housing | qs-housing (5.3.2 or newer) · qb-houses | The Home app is empty and there are no house chargers. |
Voice | pma-voice · mumble-voip · other Mumble-based systems. The Radio DLC also supports tokovoip, saltychat and yaca-voice. | No voice in calls. |
Sound | qs-3dsound · mx-surround · xsound | xsound is required as the fallback. |
Target | ox_target · qb-target, when | Markers and a key press. |
Notifications | qs-interface, otherwise ox_lib | ox_lib notifications. |
3D text | qs-textui | Plain GTA 3D text. |
A resource is only found if it started before the phone. If a garage or billing script is not detected, move its
ensureline aboveensure qs-smartphone.
Overriding Detection#
Each layer has a small table at the top of config/main.lua that maps a resource name to its bridge. If you renamed a resource, add its new name to the table and point it at the same bridge:
local frameworks = {
['es_extended'] = 'esx',
['qb-core'] = 'qb',
['qbx_core'] = 'qb',
['my-renamed-core'] = 'qb', -- your renamed resource
}
local inventories = {
['qs-inventory'] = 'qs-inventory',
['ox_inventory'] = 'ox_inventory',
['my-inventory'] = 'qb-inventory', -- a qb-inventory fork
-- ...
}The same pattern is used for keys, billings, ambulances, garages and housingProviders.
Adding a Backend#
For a script that is not on the list, copy the closest bridge and adapt it. Every bridge is plain Lua and outside the escrow.
Layer | Folder |
|---|---|
Framework |
|
Inventory |
|
Garages |
|
Billing |
|
Vehicle keys |
|
Ambulance |
|
Notifications, text UI, HUD |
|
Copy the closest bridge
For example copy
server/custom/garage/cd_garage.luatomy_garage.luaand change the exports and queries inside.Register the resource name
Add
['my_garage'] = 'my_garage'to the matching table inconfig/main.lua.Restart
Start your script before the phone and restart the server.
Discord Logs#
Social activity can be posted to Discord. Paste a webhook URL per app in server/webhooks.lua; an empty app URL falls back to global.
Config.ShowWebhookIP = false -- show the player's IP in the embed
_G.webhook = {
global = '',
pictagram = '', -- posts, comments, likes, follows, stories, live
tweedle = '', -- Chirpy: posts, replies, likes, reposts, follows
beatzy = '', -- videos, comments, likes, follows, account deletion
yellowpages = '', -- Findly listings created and deleted
weazel = '', -- News articles created, edited and deleted
}Free DLC Apps#
The [addons] folder holds free apps. Each one is its own resource: copy the ones you want and ensure them after qs-smartphone. They appear in the App Store; each has its own config.lua.

Standalone apps#
These work with the phone alone.
Resource | App | What it does |
|---|---|---|
| Aura AI | Voice assistant: hold B and speak to call, text or open apps. |
| Radio | Talk on frequencies; private channels from |
| Recorder | Voice memos. |
| Phone Booths | Public booths for anonymous paid calls, with or without a phone. |
| Disaster Alarm | City-wide emergency alerts sent by an admin with |
| QChat | Real-time chat for both FiveM and mobile. |
Integrated apps#
These need the Quasar script they connect to; without it, the resource does not start.
Resource | App | Requires |
|---|---|---|
| Biohazard | qs-biohazard |
| Bunker HQ | qs-bunkers-creator |
| Careers HQ | qs-jobcenter |
| Crime | qs-crime-creator |
| Deathmatch | qs-deathmatch |
| Diamond Casino | qs-diamondcasino |
| LTD Gasoline | qs-fuelstations |
| Monkey Lotto | qs-lottery-creator |
| Moteling | qs-motels-creator |
| Racing | qs-advancedracing |
| Foodadvisor | qs-restaurant-creator |
| Tuneshop | qs-mechanic-creator |
| VendGo | qs-vendingmachine-creator |
| Yacht | qs-yacht |
ensure qs-smartphone
# DLC apps, always after the phone
ensure qs-smartphone-aura
ensure phone-radio
ensure phone-booth
ensure phone-motels # needs qs-motels-creator started aboveAura AI#
Aura is not part of the base phone: voice transcription runs in qs-smartphone-aura. While that resource is stopped, Aura is hidden on every phone and the server console prints a warning. Its options are in the phone's own config:
Config.Aura = {
enabled = true, -- false = hide Aura even with the add-on installed
pushToTalkKey = 'B',
maxRecordingSeconds = 30,
whisperLanguage = 'english', -- language hint, or 'auto'
}Your Own Apps#
Any resource can add an app to the phone. The app is a web page shown inside the phone, and it talks to the phone through a JavaScript bridge. Two starter projects are included in [template]: phone-custom-react and phone-custom-jquery.
Registering the app#
Call this from your resource's client after the phone has started:
local ui = 'https://cfx-nui-' .. GetCurrentResourceName() .. '/ui/build/'
local ok, err = exports['qs-smartphone']:addCustomApp({
id = 'my_app',
label = 'My App',
icon = ui .. 'icon.webp',
category = 'Utilities',
creator = 'Your Name',
description = 'Short description for the store.',
appStoreOnly = true, -- false = installed on the home screen
price = 450, -- optional; > 0 = paid download
sizeMb = 5,
iframe = { url = ui .. 'index.html' },
custom = {
enabled = true,
bridge = {
enabled = true,
allowedOrigins = { 'https://cfx-nui-' .. GetCurrentResourceName() },
},
},
})
if not ok then
print('addCustomApp failed:', err)
endThe app is removed automatically when your resource stops. updateCustomApp, removeCustomApp and the rest are in Commands and Exports.
The JavaScript bridge#
Load the bridge in your app's HTML:
<script src="https://cfx-nui-qs-smartphone/web/build/bridge/qs-phone-bridge.js"></script>const { bridge, api } = QSPhoneBridge.create({
appId: 'my_app',
targetOrigin: 'https://cfx-nui-qs-smartphone',
})
api.onReady(() => console.log('Phone bridge ready'))
const state = await api.getPhoneState() // { visible, mode, activeApp, screen }
const locale = await api.getPhoneLocale() // 'en', 'es', ...
const theme = await api.getThemeMode()
await api.showToastNotification({ title: 'Done', text: 'Saved successfully' })
const name = await api.openTextPrompt({ title: 'Rename', placeholder: 'Name' })
const media = await api.pickGalleryMedia({ mediaFilter: 'photos' })
const photo = await api.pickCameraMedia() // null if cancelledMethod | Does |
|---|---|
| Read the phone's state, language and dark/light mode. |
| A phone translation in the player's language. |
| Open a built-in app, or close yours. |
| Native phone toasts, text inputs and option lists. |
| Let the player pick a photo, take one, or choose a GIF. |
| Record a voice clip. |
| Your own Dynamic Island, from the app. |
Send your own events between the app and your Lua with bridge.emit('my:event', data) and bridge.onEvent((event, data) => {}). The phone also sends phone.theme.changed, phone.locale.changed, app:opened and app:closed.
Languages#
The phone ships in 45 languages, one JSON file each in qs-smartphone/web/locales/. Config.Locale (or setr qs:locale) is the default, and each player can pick their own in Settings → General → Language.
To add a language, copy en.json, rename it with the language code, translate the values (never the keys), and add the code to Config.PhoneUiLocales.



