Exports
All money, item and ownership checks happen on the server. Server exports are trusted callers, so they skip checks like the image host allow-list.
Core
server
| Export | Returns | Description |
|---|---|---|
notify(src, n) | boolean | Notification for one player: { app, title, body, icon?, data?, sound? } |
notifyNumber(number, n) | boolean | Same, for whoever holds that number. false if nobody online has it |
notifyAll(n) | count | Every player with a loaded phone |
push(src, event, data) | boolean | Raw push to a player's NUI. event = '<area>:<event>' |
pushAll(event, data) | boolean | Broadcast push |
getNumber(src) | '555-0142' | nil | |
getNumberByIdentifier(identifier) | number | Created on first use |
getSourceByNumber(number) | src | nil | |
getIdentifierByNumber(number) | identifier | nil | |
close(src) | Close a player's phone (for example, when a script cuffs them) | |
registerStoreApp(entry) | boolean | Add or replace an App Store entry (see below) |
Notification fields
| Field | Notes |
|---|---|
app | App id (≤ 32), default 'system'. Tapping the notification opens this app |
title / body | ≤ 120 / ≤ 600 characters |
icon | Optional Font Awesome class |
data | Table passed to the app as deep-link params when the notification is tapped |
sound | Default true |
lua
exports.np_phone:notify(src, { app = 'mail', title = 'Bank', body = 'Your loan was approved.' })
exports.np_phone:notifyNumber('555-0142', { app = 'messages', title = 'Tony C.', body = 'Where are you?' })App Store
lua
exports.np_phone:registerStoreApp({
id = 'racing', name = 'Racing', icon = 'fa-solid fa-flag-checkered', tint = 'violet',
description = 'Street races', url = 'https://cfx-nui-myracing/web/index.html',
category = 'app', featured = true,
-- optional gate
item = 'racing_tablet', job = 'police', grade = 2, onduty = true,
})Apps without a web manifest in np_phone (third-party apps) open url in an iframe.
Apps
server
| Export | Area | Description |
|---|---|---|
sendMail(srcOrIdentifier, mail) | City | Mail with optional waypoint or event action buttons |
createInvoice(srcOrIdentifier, data) | City | Business invoice or fine → id |
logTransaction(srcOrIdentifier, amount, title, meta) | City | Add a line to the wallet history (signed amount) |
groupGetByPlayer(src) | City | → groupId, info |
groupGetMembers(groupId) | → { src, … } | |
groupStartJob(groupId, job) | Starts a staged job with a Live Activity | |
groupSetStage(groupId, stage, patch?) | ||
groupSetProgress(groupId, done, total) | ||
groupUpdateJob(groupId, patch) | ||
groupFinishJob(groupId, { payoutEach }) | Pays every member to bank, plus a transaction log entry | |
groupNotify(groupId, n) | Notifies every member | |
sendSystemMessage(fromLabel, toNumber, text) | Comms | Read-only business thread in Messages |
addPhoto(srcOrIdentifier, url, meta?) | Media | Skips the host allow-list |
addNote(srcOrIdentifier, body) | Media | |
addFile(srcOrIdentifier, file) | Media | Document, contract, image, licence or receipt |
createContract(src, targetSrc, data) | Two-party contract → id. The target gets a "Sign" button | |
getContract(id) | ||
addCalendarEvent(srcOrIdentifier, event) | Social | → event id |
lua
exports.np_phone:sendMail(src, {
sender = 'Lester', subject = 'Job', body = 'Meet me here.',
actions = { { label = 'Set waypoint', type = 'waypoint', data = { x = 1275.0, y = -1710.0 } } },
})
exports.np_phone:createInvoice(src, { sender = 'LSPD', senderJob = 'police', amount = 350,
reason = 'Citation #4412 · Speeding', dueAt = os.time() + 3 * 86400 })
exports.np_phone:addFile(src, { type = 'document', title = 'Property Deed — 3671 Whispymound Dr',
body = 'This deed certifies ...', issuer = 'Dynasty 8 Real Estate' })Client
client
| Export | Returns | Description |
|---|---|---|
open() | Open the phone | |
close() | Close the phone | |
isOpen() | boolean | |
setPose(pose) | 'text' or 'call' holding animation | |
ring(opts) | boolean | Ring / peek mode: the phone slides up without focus. false while the phone is open |
stopRing(id?) | boolean | |
isRinging() | id | nil | |
isCameraActive() | boolean | The camera app is in use |
lua
exports.np_phone:ring({ id = 'dispatch', app = 'phone', title = 'Dispatch', subtitle = 'Incoming call',
openOnAccept = true, timeout = 30000 })ring callbacks through the export
Phone.ring only accepts onAccept / onDecline when they are Lua functions. Functions passed through an export arrive as callable function-reference tables, so they're currently ignored. The key press is forwarded to the NUI as the push phone:ringKey { id, action } instead.
Internal Lua API
Inside np_phone (new apps, bridges), use the Phone.* API directly: Phone.rpc.register, Phone.push, Phone.getPlayer, Phone.fw, Phone.schema, … See Architecture §2–3.