Exports · origen_phone
All exports are registered under the resource name origen_phone.
Every server export validates its input and returns something checkable (a boolean, an id, a table with ok) instead of failing silently. A badly written resource cannot break anyone's phone.
Server-side exports
Identity
GetPhoneNumber(source) → string|nil
The number of the phone the player has switched on, if they carry one. The inventory is checked again, since the phone may have been taken in the meantime.
local number = exports['origen_phone']:GetPhoneNumber(source)| Parameter | Type | Description |
|---|---|---|
source | number | Player server ID |
GetSourceByNumber(number) → number|nil
The player carrying that number, if online.
local src = exports['origen_phone']:GetSourceByNumber('551234')| Parameter | Type | Description |
|---|---|---|
number | string | Phone number |
HasPhoneOpen(source) → boolean
Whether the player has a phone switched on right now.
local online = exports['origen_phone']:HasPhoneOpen(source)| Parameter | Type | Description |
|---|---|---|
source | number | Player server ID |
GetNumbersByCitizenId(citizenid) → string[]
Every number owned by a character, online or not. Useful to write to someone who is not playing.
local numbers = exports['origen_phone']:GetNumbersByCitizenId(citizenid)| Parameter | Type | Description |
|---|---|---|
citizenid | string | Character identifier |
GetDisplayName(number, viewer) → string
How a number appears for a given phone: the name saved in its contacts, or the formatted number.
local name = exports['origen_phone']:GetDisplayName('551234', '601111')| Parameter | Type | Description |
|---|---|---|
number | string | Number to resolve |
viewer | string|nil | Number whose contacts are used. Without it, returns the formatted number |
FormatNumber(number) → string
Formats a number with the server format (Config.PhoneNumber.Format).
local formatted = exports['origen_phone']:FormatNumber('551234') -- '55-1234'Messages and mail
SendMessage(data) → number|false
Sends a text message to a number. from can be a real number or an invented sender such as '900100' so a bank or a workshop can write; if it does not belong to a phone, the message arrives as an unknown number.
exports['origen_phone']:SendMessage({
to = number,
from = '5550100',
message = 'Hello',
})| Field | Type | Description |
|---|---|---|
to | string | Recipient number |
from | string|nil | Sender. Defaults to Config.Messages.SystemNumber |
message | string | Message text |
attachments | table|nil | Attachments |
GetEmailAddress(source) → string|nil
The player's email address, or nil if they have not created a mailbox yet.
local address = exports['origen_phone']:GetEmailAddress(source)GetEmailByNumber(number) → string|nil
The email address linked to a phone number.
local address = exports['origen_phone']:GetEmailByNumber('551234')SendMail(data) → table
Sends an email. Unlike a message, the sender can be any address even if it does not belong to a phone.
exports['origen_phone']:SendMail({
sender = 'dealership@ogn.net',
senderName = 'Premium Deluxe',
to = exports['origen_phone']:GetEmailAddress(source),
subject = 'Your invoice',
body = 'Thank you for your purchase.',
})| Field | Type | Description |
|---|---|---|
to | string | Recipient address |
sender | string | Sender address |
senderName | string | Sender display name |
subject | string | Subject |
body | string | Body |
attachments | table|nil | Attachments (links) |
Returns { ok, delivered, unknown, error }.
Notifications
The data table shared by the notification exports:
| Field | Type | Description |
|---|---|---|
app | string | App the notification is grouped under. Defaults to 'settings' |
title | string | Title |
content | string | Body text |
avatar | string|nil | Avatar URL |
thumbnail | string|nil | Thumbnail URL |
data | table|nil | Extra data |
silent | boolean|nil | Do not play a sound |
actions | table|nil | Action buttons |
SendNotification(data) → boolean
Notification to a number. Always stored, even if the player is offline or the phone is off.
exports['origen_phone']:SendNotification({
to = number,
app = 'bank',
title = 'Bank',
content = 'Payment received',
})| Field | Type | Description |
|---|---|---|
to | string | Recipient number |
NotifySource(source, data) → number
Notification to a player instead of a number. Reaches every phone they carry; if they carry none, it is stored on the phone in their name. Returns how many phones it reached (0 means the player has no phone).
exports['origen_phone']:NotifySource(source, {
app = 'services',
title = 'Dispatch',
content = '10-13 in Vinewood',
})NotifyJob(job, data) → number
Notification to every online player with that job. Returns how many players it reached.
exports['origen_phone']:NotifyJob('police', {
app = 'services',
title = 'Dispatch',
content = 'Alarm at the port',
})NotifyGang(gang, data) → number
Notification to every online player in that gang. Returns how many players it reached.
exports['origen_phone']:NotifyGang('ballas', {
app = 'darkchat',
title = 'Meeting',
content = 'Usual place',
})NotifyAll(data) → number
Notification to every online player carrying a phone. Returns how many players it reached.
exports['origen_phone']:NotifyAll({
app = 'news',
title = 'City Hall',
content = 'Power cut downtown',
})ClearNotifications(number, app) → boolean
Deletes the notifications of an app on a phone. Without app, deletes all of them.
exports['origen_phone']:ClearNotifications(number, 'delivery')| Parameter | Type | Description |
|---|---|---|
number | string | Phone number |
app | string|nil | App id |
Widgets and apps
SetWidgetData(target, widget, data) → boolean
Fills a widget whose source is 'fuera' (business till, radio frequency, work shift). Data is kept for five minutes; after that the widget shows no data again. Pass nil as data to clear it.
exports['origen_phone']:SetWidgetData(source, 'negocio', {
name = 'Bennys Motorworks',
caja = 12400,
empleados = 3,
stock = 62,
})| Parameter | Type | Description |
|---|---|---|
target | number|string | Player source or phone number |
widget | string | 'negocio', 'radio', 'trabajo'... |
data | table|nil | Widget data |
RegisterApp(data) → boolean
Adds an app from another resource. It appears on the home screen and opens its page full screen. It enters the catalogue like any other app, so the admin panel and SetAppRule can restrict it. It is removed automatically when its resource stops.
exports['origen_phone']:RegisterApp({
id = 'myworkshop',
name = 'My Workshop',
icon = 'lucide:wrench',
url = 'nui://my_workshop/ui/index.html',
slot = 30,
})| Field | Type | Description |
|---|---|---|
id | string | Lowercase letters, numbers and underscores. Must not exist already |
name | string | Display name (max 40 characters) |
icon | string|nil | Iconify icon. Defaults to 'lucide:app-window' |
url | string | Must be nui://<resource>/... or https://... |
slot | number|nil | Home-screen position |
default | boolean|nil | Installed by default. Defaults to true |
UnregisterApp(id) → boolean
Removes an external app without waiting for its resource to stop.
exports['origen_phone']:UnregisterApp('myworkshop')App access
CanUseApp(source, appId) → boolean
Whether the player can use that app right now.
if exports['origen_phone']:CanUseApp(source, 'bank') then
-- ...
endGetAppRule(appId) → table|nil
The rule set on an app: { mode, list }.
local rule = exports['origen_phone']:GetAppRule('bank')SetAppRule(appId, mode, list) → boolean
Changes who has an app. It stores exactly what the admin panel stores, so the change is visible there and can be undone by hand. Changes reach open phones immediately.
exports['origen_phone']:SetAppRule('bank', 'menos', {
{ kind = 'negocio', id = 'prisoner' },
})
exports['origen_phone']:SetAppRule('bank', 'todos')| Parameter | Type | Description |
|---|---|---|
appId | string | App id |
mode | string | 'todos' (everyone), 'nadie' (nobody), 'solo' (only the list), 'menos' (everyone except the list) |
list | table[]|nil | Entries, only used by solo and menos |
| Entry | Matches |
|---|---|
{ kind = 'civiles' } | Players with no job and no gang |
{ kind = 'bandas' } | Anyone in a gang |
{ kind = 'negocios' } | Anyone with a job |
{ kind = 'banda', id = 'ballas' } | A specific gang |
{ kind = 'negocio', id = 'mechanic' } | A specific job |
Contacts and content
AddContact(data) → boolean
Adds a contact to someone's phone. Not duplicated if it already exists.
exports['origen_phone']:AddContact({
owner = number,
number = '5550100',
name = 'Mechanic',
})| Field | Type | Description |
|---|---|---|
owner | string | Phone number that receives the contact |
number | string | Contact number |
name | string | Contact name |
avatar | string|nil | Avatar URL |
AddPhoto(data) → number|nil
Stores an already uploaded image in a phone's gallery. Returns the photo id.
exports['origen_phone']:AddPhoto({ owner = number, url = 'https://...' })| Field | Type | Description |
|---|---|---|
owner | string | Phone number |
url | string | Image URL |
isVideo | boolean|nil | Marks it as a video |
PublishNews(data) → number|nil
Publishes an article in the News app. Returns the article id.
exports['origen_phone']:PublishNews({
title = 'Headline',
content = 'What happened',
})| Field | Type | Description |
|---|---|---|
title | string | Title |
content | string | Body |
author | string|nil | Author |
image | string|nil | Image URL |
category | string|nil | Category |
pinned | boolean|nil | Pin the article |
PublishAd(number, data) → number|nil, string|nil
Publishes an ad on the classifieds board in someone's name. Returns the ad id, or nil and an error.
exports['origen_phone']:PublishAd(number, {
title = 'Car for sale',
body = 'Low mileage',
price = 40000,
})| Field | Type | Description |
|---|---|---|
title | string | Title |
body | string | Body |
price | number|nil | Price |
category | string|nil | One of Config.Ads.Categories |
AddNote(number, title, content) → number|nil
Leaves a note on someone's phone. Returns the note id.
exports['origen_phone']:AddNote(number, 'Reminder', 'Visit the garage')AddCalendarEvent(number, data) → number|nil
Adds an event to someone's calendar. Returns the event id.
exports['origen_phone']:AddCalendarEvent(number, {
title = 'Court',
date = '2026-09-04',
})| Field | Type | Description |
|---|---|---|
title | string | Title |
note | string|nil | Note |
date | string | Date (YYYY-MM-DD) |
time | string|nil | Time |
CreateDarkRoom(number, name, burn) → string|nil
Creates a DarkChat room and returns its code. The owner must already have a DarkChat alias; otherwise it returns nil.
local code = exports['origen_phone']:CreateDarkRoom(number, 'Harbour crew', 60)| Parameter | Type | Description |
|---|---|---|
number | string | Room owner |
name | string | Room name |
burn | number|nil | Minutes until messages self-destruct. 0 never |
Bills and fines
Paid from the phone through the Banking bridge.
CreateInvoice(data) → number|nil, string|nil
Issues an invoice. The player pays or refuses it from the Invoices app.
exports['origen_phone']:CreateInvoice({
to = number,
amount = 500,
reason = 'Repair',
issuer = 'Mechanic',
issuerAccount = 'mechanic',
})| Field | Type | Description |
|---|---|---|
to | number|string | Recipient source or phone number. With the number, it can be issued to an offline player |
amount | number | Positive amount |
reason | string | Reason |
issuer | string | Issuer name |
issuerAccount | string|nil | Account that receives the money. Without it the player is charged and the money goes nowhere |
reference | any | Your own id, returned when the bill is paid |
dueAt | number|nil | Seconds from now until it is due |
CreateFine(data) → number|nil, string|nil
Like an invoice, but it cannot be refused. The money goes to issuerAccount or, if omitted, to Config.FineAccount. Same fields as CreateInvoice.
exports['origen_phone']:CreateFine({
to = number,
amount = 250,
reason = 'Speeding',
issuer = 'LSPD',
})GetBills(number, kind, status) → table[]
Bills of a phone.
local bills = exports['origen_phone']:GetBills(number, 'fine', 'pending')| Parameter | Type | Description |
|---|---|---|
number | string | Phone number |
kind | string|nil | 'invoice', 'fine' or nil for both |
status | string|nil | 'pending', 'paid', 'refused' or 'cancelled' |
GetPendingBills(number, kind) → number, number
How many bills are pending and the total amount owed.
local count, total = exports['origen_phone']:GetPendingBills(number)CancelBill(id) → boolean
Withdraws a bill. Only works while it is still pending.
exports['origen_phone']:CancelBill(billId)Calls and device
IsInCall(number) → boolean
Whether that number is in a call right now.
local busy = exports['origen_phone']:IsInCall(number)GetDeviceState(number) → table|nil
What the server knows about a phone.
local state = exports['origen_phone']:GetDeviceState(number)
-- { airplane, dnd, battery, charging, signal }SetBattery(number, level) → boolean
Sets the battery level of a phone (0 to 100).
exports['origen_phone']:SetBattery(number, 100)SetCharging(number, charging) → boolean
Marks a phone as charging or not.
exports['origen_phone']:SetCharging(number, true)Deliveries
CreateSupplyDelivery(data) → number|nil, string|nil
Creates a supply delivery job in the Mandao app. When the courier confirms arrival, the phone calls DeliverOrder(businessId, orderId) on the Business bridge, which puts the goods where they belong. The courier never carries the goods.
local id, err = exports['origen_phone']:CreateSupplyDelivery({
businessId = 'burgershot',
orderId = 42,
label = 'Burger Shot',
coords = { x = -1193.0, y = -892.0, z = 14.0 },
items = { { name = 'bread', count = 20 } },
})| Field | Type | Description |
|---|---|---|
businessId | string | Business that receives the order |
orderId | any | Order id passed back to DeliverOrder |
label | string | Destination label |
coords | table | Destination { x, y, z }. Must be a plain table, not a vector3 |
items | table | Goods, stored as shown to the courier |
requester | any | Who requested it |
note | string|nil | Note for the courier |
The goods are picked up at the nearest depot in Config.Delivery.Depots.
Returns nil, 'err_delivery_off' when Config.Delivery.HandleBusinessOrders is off, and nil, 'err_delivery_no_couriers' when no courier is on duty. In both cases the business should deliver the order on its own.
SupplyActive(id) → boolean
Whether a supply delivery is still alive (pending, taken, picked or arrived). An expired or cancelled delivery will never arrive.
local alive = exports['origen_phone']:SupplyActive(deliveryId)IsCourierOnDuty(number) → boolean
Whether a number is registered as a courier and on duty.
local onDuty = exports['origen_phone']:IsCourierOnDuty(number)GetCurrentDelivery(number, asCourier) → table|nil
The delivery someone is handling, if any.
local delivery = exports['origen_phone']:GetCurrentDelivery(number, true)| Parameter | Type | Description |
|---|---|---|
number | string | Phone number |
asCourier | boolean | true looks it up as courier, false as customer |
Vehicles and properties
Normalized from the active Garage and Housing bridges, so callers do not need to know which resource is behind them.
GetVehicles(citizenid) → table[]
local vehicles = exports['origen_phone']:GetVehicles(citizenid)
-- { { plate, model, garage, state, fuel, engine, body, depotPrice } }GetProperties(citizenid) → table[]
The character's properties: owned and those they have a key to.
local houses = exports['origen_phone']:GetProperties(citizenid)
-- { { id, name, mine, price, kind, keys, where } }Client-side exports
UsePhoneItem(data, slot)
Opens the phone from an ox_inventory item. Reads the number from the slot metadata, so with two phones the one used is opened.
-- ox_inventory/data/items.lua
client = { export = 'origen_phone.UsePhoneItem' },UsePhone(item)
Same as UsePhoneItem, for inventories that pass the whole item instead of the slot.
exports['origen_phone']:UsePhone(item)Open()
Opens the first phone the player carries.
exports['origen_phone']:Open()Close()
Closes the phone.
exports['origen_phone']:Close()IsOpen() → boolean
Whether the phone is open.
local open = exports['origen_phone']:IsOpen()GetNumber() → string|nil
The number of the phone in use.
local number = exports['origen_phone']:GetNumber()IsInCall() → boolean
Whether the local player is in a call.
local busy = exports['origen_phone']:IsInCall()ShowNotification(data) → boolean
Shows a phone notification card without storing it: it does not count as unread and opens no app. Respects Custom.ShowNotification.
exports['origen_phone']:ShowNotification({
app = 'services',
title = 'Dispatch',
content = 'Alert in Vinewood',
})| Field | Type | Description |
|---|---|---|
app | string | App icon. Defaults to 'settings' |
title | string | Title |
content | string | Body |
avatar | string|nil | Avatar URL |
thumbnail | string|nil | Thumbnail URL |
silent | boolean|nil | Do not play a sound |
ShowServerNotification(data) → boolean
Echoes a server notification (origen_notify style) on the phone, when Config.ServerNotifications.Enabled is on and the player carries a phone. Returns true when the phone takes over the notification.
local handled = exports['origen_phone']:ShowServerNotification({
title = 'Bank',
description = 'You received $40',
type = 'success',
})| Field | Type | Description |
|---|---|---|
description | string | Text (text and content are also accepted) |
title | string|nil | Title. Defaults to the server title |
type | string|nil | Notification type, filtered by Config.ServerNotifications.Types |
app | string|nil | Icon. Defaults to the server icon |
openManagement()
Opens the admin panel. Used by origen_admin; the permission is checked on the server.
exports['origen_phone']:openManagement()