Home › Documentation › 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.
Identity
GetPhoneNumber(source) → string|nilThe 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|nilThe player carrying that number, if online.
local src = exports['origen_phone']:GetSourceByNumber('551234')| Parameter | Type | Description |
|---|---|---|
number | string | Phone number |
HasPhoneOpen(source) → booleanWhether 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) → stringHow 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) → stringFormats a number with the server format (Config.PhoneNumber.Format).
local formatted = exports['origen_phone']:FormatNumber('551234') -- '55-1234'Messages and mail
SendMessage(data) → number|falseSends 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|nilThe player's email address, or nil if they have not created a mailbox yet.
local address = exports['origen_phone']:GetEmailAddress(source)GetEmailByNumber(number) → string|nilThe email address linked to a phone number.
local address = exports['origen_phone']:GetEmailByNumber('551234')SendMail(data) → tableSends 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) → booleanNotification 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) → numberNotification 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) → numberNotification 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) → numberNotification 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) → numberNotification 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) → booleanDeletes 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) → booleanFills 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) → booleanAdds 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) → booleanRemoves an external app without waiting for its resource to stop.
exports['origen_phone']:UnregisterApp('myworkshop')App access
CanUseApp(source, appId) → booleanWhether the player can use that app right now.
if exports['origen_phone']:CanUseApp(source, 'bank') then
-- ...
endGetAppRule(appId) → table|nilThe rule set on an app: { mode, list }.
local rule = exports['origen_phone']:GetAppRule('bank')SetAppRule(appId, mode, list) → booleanChanges 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) → booleanAdds 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|nilStores 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|nilPublishes 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|nilPublishes 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|nilLeaves a note on someone's phone. Returns the note id.
exports['origen_phone']:AddNote(number, 'Reminder', 'Visit the garage')AddCalendarEvent(number, data) → number|nilAdds 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|nilCreates 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|nilIssues 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|nilLike 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, numberHow many bills are pending and the total amount owed.
local count, total = exports['origen_phone']:GetPendingBills(number)CancelBill(id) → booleanWithdraws a bill. Only works while it is still pending.
exports['origen_phone']:CancelBill(billId)Calls and device
IsInCall(number) → booleanWhether that number is in a call right now.
local busy = exports['origen_phone']:IsInCall(number)GetDeviceState(number) → table|nilWhat the server knows about a phone.
local state = exports['origen_phone']:GetDeviceState(number)
-- { airplane, dnd, battery, charging, signal }SetBattery(number, level) → booleanSets the battery level of a phone (0 to 100).
exports['origen_phone']:SetBattery(number, 100)SetCharging(number, charging) → booleanMarks a phone as charging or not.
exports['origen_phone']:SetCharging(number, true)Deliveries
CreateSupplyDelivery(data) → number|nil, string|nilCreates 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) → booleanWhether 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) → booleanWhether a number is registered as a courier and on duty.
local onDuty = exports['origen_phone']:IsCourierOnDuty(number)GetCurrentDelivery(number, asCourier) → table|nilThe 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 } }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() → booleanWhether the phone is open.
local open = exports['origen_phone']:IsOpen()GetNumber() → string|nilThe number of the phone in use.
local number = exports['origen_phone']:GetNumber()IsInCall() → booleanWhether the local player is in a call.
local busy = exports['origen_phone']:IsInCall()ShowNotification(data) → booleanShows 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) → booleanEchoes 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()