Agency-Phone
Téléphone multi-framework moderne pour FiveM avec détection automatique de QBCore et ESX. Suite complète d'applications incluant Dialer, Buzz SMS, Snap Camera, Pulse Social Feed, Postbox Mail, Wallet avec AgencyPay, Galerie et Contacts.

🧩 Build a custom app for Agency-Phone
There are two ways to put an app on the phone. Pick the one that fits you:
| Method | Best for | What you touch |
|---|---|---|
| A. SDK (external resource) | Vendors and scripts adding an app with their own icon | Only your own resource. Zero edits to agency-phone. |
| B. Drop-in (inside agency-phone) | Server owners adding a native screen inside the phone UI | config.lua + apps/ (+ html/js for a screen) |
If you ship an app to other servers, use Method A. It needs no source from us and keeps working across updates.
A SDK — register an app from your own resource
agency-phone exposes exports that let any resource put an app (with its own icon) on the phone home screen at runtime. Your app's UI lives in your own resource; the phone shows the icon and hands control to you when the user taps it. No edits to agency-phone, no unlocked build.
Minimal example
-- client side of YOUR resource
CreateThread(function()
Wait(1500) -- let agency-phone boot
exports['agency-phone']:RegisterApp({
id = 'myshop',
label = 'My Shop',
icon = 'fa-solid fa-cart-shopping', -- FontAwesome 6 class
-- image = 'https://cfx-nui-myresource/icon.png', -- or your own image instead
color = '#ff9500',
colorDark = '#e08600',
})
end)
-- fired when the user taps your icon on the phone
AddEventHandler('agency-phone:client:openApp', function(appId)
if appId ~= 'myshop' then return end
exports['agency-phone']:ClosePhone()
-- open your own NUI here
end)
RegisterApp config
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Unique id, lowercase, no spaces. |
label | string | yes | Name shown under the icon. |
icon | string | — | FontAwesome 6 class. Ignored if image is set. |
image | string | — | Custom icon image, e.g. https://cfx-nui-<resource>/icon.png. |
color / colorDark | string | — | Gradient behind the icon (hex). |
slot | number | — | Home-screen position. Auto-placed if omitted. |
SDK exports (client)
| Export | Description |
|---|---|
RegisterApp(config) | Add or update your app. Returns true, or false, err. |
UnregisterApp(id) | Remove your app. Also happens automatically when your resource stops. |
GetRegisteredApps() | Table of all registered external apps. |
ClosePhone() | Close the phone UI, e.g. before opening your own NUI. |
The tap event
When the user taps your icon, agency-phone fires both of these on the client (use whichever you prefer):
| Event | Args |
|---|---|
agency-phone:client:openApp | appId (filter by your id) |
agency-phone:client:openApp:<yourId> | none (already scoped to your app) |
Add dependency 'agency-phone' to your fxmanifest.lua so the start order is correct.
B Drop-in — a native screen inside the phone
For a screen rendered by the phone itself (not your resource). This edits agency-phone's own files.
| Step | File | Editable in escrow build? |
|---|---|---|
1. App entry in AgencyConfig.Apps | config.lua | ✓ yes |
2. RegisterNUICallback logic | apps/<id>_client.lua (+ _server.lua) | ✓ yes |
3. Screen <div id="app-<id>"> | html/index.html | ✗ encrypted |
4. case '<id>': in openApp() + loader | html/js/phone.js | ✗ encrypted |
Steps 3 and 4 live in escrow-encrypted files, so a native screen needs a source / unlocked build. If you only have the escrow version, use Method A instead (your screen lives in your own resource). Reference files inside the resource: apps/testapp_client.lua and apps/dispatch_client.lua.
📋 Other phone exports
Client
| Export | Returns | Description |
|---|---|---|
IsPhoneOpen() | boolean | Phone currently open. |
IsPhonePoweredOn() | boolean | Device powered on. |
HasPhone() | boolean | Player owns a phone item. |
HasCarrierContract() | boolean | Active eSIM / contract. |
GetPhoneData() | table | Snapshot of phone state. |
PhoneNotification(title, text, icon, color, timeout, acceptIcon, denyIcon) | void | In-phone notification. |
StartCall(contact) | void | Call { name, number }. |
StartAgencyPayCheckout(opts) | void | AgencyPay checkout (see below). |
Server
| Export | Description |
|---|---|
SendMail(identifier, mailData) | Deliver mail to a player (online or offline). |
AddChirp(data) | Post a message to the Pulse social feed. |
⇄ NUI ↔ Lua bridge (Method B)
| Direction | JS side | Lua side |
|---|---|---|
| JS → Lua | fetchNUI('callbackName', data) | RegisterNUICallback('callbackName', fn) |
| Lua → JS | listen for action in window.onmessage | SendNUIMessage({ action = '...' }) |
💳 AgencyPay checkout
Charge the player's wallet from any resource.
exports['agency-phone']:StartAgencyPayCheckout({
merchant = 'My Shop', title = 'Agency Pay', subtitle = 'Checkout',
label = 'Premium Coffee', amount = 24.50,
icon = 'fa-solid fa-mug-hot', accent = '#ff9f0a',
clientEvent = 'myshop:client:agencyPayResult',
serverEvent = 'myshop:server:agencyPayResult',
metadata = { orderId = 'order_2048' },
})
RegisterNetEvent('myshop:client:agencyPayResult', function(result)
if result.status == 'paid' then
-- deliver the goods
end
end)
The result payload includes status (paid / cancelled / failed), amount, cardId, balance and your metadata.
🐛 Troubleshooting
| Symptom | Fix |
|---|---|
| Icon doesn't appear (SDK) | Update agency-phone to the latest Keymaster build, Wait(1500) before RegisterApp, and add dependency 'agency-phone' to your manifest. |
| Tapping does nothing (SDK) | Your agency-phone:client:openApp handler must match your id exactly (case-sensitive). |
| Custom image not showing | Use a full NUI URL https://cfx-nui-<your-resource>/path/icon.png and list the file in files { } in your manifest. |
| Icon doesn't appear (drop-in) | enabled = true and installed = true in the config entry. |
Questions, or need a hand? Join the Agency Scripts Discord.