Client Exports
Client-side exports for GKSPHONE V2 — notifications, calls, custom apps and widgets, phone health, and GPS.
Notification
Send Notification
local NotifData = {
title = "Notification header", -- Notification header
message = "Notification Message", -- Notification content message
icon = '/html/img/icons/messages.png', -- Icon of the notification
duration = 5000, -- specify how many seconds,
type = "success", -- the home screen will also appear on the notification side.
buttonactive = false, -- Activate if you want to use the button function
button = {
buttonEvent = "gksphone:client:Test", -- event name to use if the button approves
buttonData = "test", -- If you want to transfer any data in the button
}
}
exports["gksphone"]:Notification(NotifData)Send Mail
local waitingDelivery = { location = { x = 0, y = 0, z = 0 }, name = "Test Location" }
local MailData = {
sender = 'GKSHOP',
image = '/html/img/icons/mail.png',
subject = "GKSPHONE",
message = 'TEST',
button = { --- If you don't want it to be a button, please remove it.
enabled = true,
buttonEvent = "gksphone:client:mailtest",
buttonData = waitingDelivery, -- data
buttonname = "Test Button"
}
}
exports["gksphone"]:SendNewMail(MailData)
------
RegisterNetEvent("gksphone:client:mailtest", function (data)
debugprint("gksphone:client:mailtest")
print(data.name) -- Test Location
print(data.location) -- { x = 0, y = 0, z = 0 }
end)SendPlayerMail
Sends mail as the local player, to another player's mail address — unlike SendNewMail, which delivers a system mail to the player themselves.
--- @param mailData table
--- to_mail string required. Aliases: to, recipient_mail, email
--- subject string required. Alias: title
--- message string required unless attachments are given. Aliases: body, content
--- attachments table optional. Aliases: attachment, photos, images
--- image string optional icon. Alias: icon
--- reply_to string optional. Alias: replyTo
--- buttons table optional. Aliases: actions, button
exports["gksphone"]:SendPlayerMail({
to_mail = "[email protected]",
subject = "Invoice",
message = "Attached."
})The server-side SendPlayerMail returns a result table; this client version does not.
Call
Create Call
-- Number
exports['gksphone']:CreateCall({ number = "5551234" })
-- Hide number
exports['gksphone']:CreateCall({ number = "5551234", hideNumber = true })
-- Job / company (Config.JOBServices key)
exports['gksphone']:CreateCall({ job = "police" })
-- Video
exports['gksphone']:CreateCall({ number = "5551234", videoCall = true })
local ok, reason = exports['gksphone']:CreateCall({ job = "police" })
-- false reasons: invalid_data | already_in_call | invalid_job | missing_number
End Call
exports["gksphone"]:EndCall()Is In Call
local inCall = exports["gksphone"]:IsInCall()
print(inCall) -- true or falseCreateCallNumber
-- Do not use the number in the service application
---@class IncomingCall
---@field id string
---@field accept fun()
---@field deny fun()
local createcall, reason = exports['gksphone']:CreateCallNumber("911", {
displayName = "Police",
onCall = function(incomingCall)
print("Incoming call from: " .. incomingCall.id)
Wait(6000) -- 6sn
incomingCall.accept() -- Automatically accept the call
end,
onEnd = function()
print("Call ended")
end
})
if createcall then
print("Create call created successfully")
else
print("Failed to create custom call")
endRemoveCallNumber
local removecall, reason = exports['gksphone']:RemoveCallNumber("911")
if removecall then
print("Call deleted successfully")
else
print(reason)
endHasCallNumber
--- Whether a custom call number is currently registered
--- @param number string
--- @return boolean success
--- @return string? reason "Number does not exist"
local exists, reason = exports['gksphone']:HasCallNumber("911")CallEndCustom
exports['gksphone']:CallEndCustom()Custom App
Add Custom App
exports['gksphone']:AddCustomApp({
name = "MyApp", -- (required) Unique app name
appurl = "https://cfx-nui-my-resource/ui/index.html", -- (required) App iframe URL
icons = "https://cfx-nui-my-resource/ui/icon.png", -- App icon
description = "My awesome app", -- App Store description
show = true, -- Show in App Store (default: true)
startapp = false, -- Auto-add to home page (default: false)
signal = false, -- Require phone signal
allowjob = {}, -- Job whitelist e.g. { "police", "ambulance" }
blockedjobs = {}, -- Job blacklist e.g. { "unemployed" }
labelLangs = { -- Localized app name (optional, falls back to name)
tr = "Uygulamam",
en = "My App",
de = "Meine App"
},
-- ▶ Lifecycle Callbacks (optional)
onOpen = function(phoneUniqueId, phoneNumber)
print(("App opened | Phone: %s | Number: %s"):format(phoneUniqueId, phoneNumber))
end,
onClose = function(phoneUniqueId, phoneNumber)
print(("App closed | Phone: %s | Number: %s"):format(phoneUniqueId, phoneNumber))
end
})Custom Widget
Add Custom Widget
exports['gksphone']:AddCustomWidget({
id = "demo-widget", -- (required) Unique widget id
widgetUrl = "https://cfx-nui-custom-widget/ui/widget.html", -- (required) Widget iframe URL
title = "Demo Widget", -- Gallery title
description = "Example custom home widget", -- Gallery subtitle
icon = "", -- icon
size = "2x2", -- "1x1" | "2x2" | "4x2" | "4x4"
show = true, -- Show in gallery (default: true)
labelLangs = { -- Localized title (optional)
tr = "Demo Widget",
en = "Demo Widget",
de = "Demo Widget"
}
})Remove Custom Widget
exports['gksphone']:RemoveCustomWidget("demo-widget")Live Activity
Glanceable status cards shown on the Dynamic Island and lock screen
StartLiveActivity
local activity = {
id = "delivery:1234", -- required, unique per activity
app = "courier", -- source app key
title = "Delivery", -- card headline
subtitle = "Heading to drop", -- optional line under the title
icon = "/html/img/icons/courier.png", -- optional image url
color = "#34C759", -- optional accent color (hex)
state = "active", -- pending | active | paused | success | failed | canceled
progress = 0, -- optional 0-100
duration = 300, -- optional countdown in seconds (preferred on client)
timeout = 330000, -- optional lifetime in ms before auto-expiry
peek = true, -- optional, keeps the island visible after the phone closes
action = { -- optional button
label = "Open",
event = "myresource:openDelivery", -- client event triggered on press
data = { id = 1234 }, -- payload for that event
route = "/courier/" -- optional, opens the phone at this route
}
}
exports["gksphone"]:StartLiveActivity(activity) -- returns true, or false if the phone is off/unset (cached and shown later)UpdateLiveActivity
--- Only the supplied fields change. An unknown id is started instead of dropped.
exports["gksphone"]:UpdateLiveActivity("delivery:1234", { progress = 60, subtitle = "2 stops left" })EndLiveActivity
--- @param state string|nil success | failed | canceled -- the card lingers briefly to show the outcome
--- @param opts table|nil { subtitle = string, immediate = boolean }
exports["gksphone"]:EndLiveActivity("delivery:1234", "success", { subtitle = "Delivered" })
exports["gksphone"]:EndLiveActivity("delivery:1234", "canceled", { immediate = true })GetLiveActivity
local activity = exports["gksphone"]:GetLiveActivity("delivery:1234") -- table | nilClearLiveActivities
exports["gksphone"]:ClearLiveActivities()ResyncLiveActivities
--- Re-sends every cached activity to the UI. Call after the phone UI reloads.
exports["gksphone"]:ResyncLiveActivities()Full example
local id = "mechanic:" .. plate
exports["gksphone"]:StartLiveActivity({
id = id,
app = "mechanic",
title = "Repair",
subtitle = "Starting",
state = "active",
progress = 0,
duration = 60,
peek = true
})
CreateThread(function()
for step = 1, 60 do
Wait(1000)
if not exports["gksphone"]:GetLiveActivity(id) then return end
exports["gksphone"]:UpdateLiveActivity(id, { progress = math.floor(step / 60 * 100) })
end
exports["gksphone"]:EndLiveActivity(id, "success", { subtitle = "Repair complete" })
end)Screen Damage
local health = exports["gksphone"]:GetScreenHealth() -- number
local severity = exports["gksphone"]:GetScreenSeverity() -- none | light | medium | heavy
local wet = exports["gksphone"]:IsPhoneWaterDamaged() -- boolean
local condition = exports["gksphone"]:GetScreenCondition()
-- { health, severity, waterDamage, damaged }
--- Starts a repair for the local player. Shows a Live Activity card for
--- Config.ScreenDamage.Repair.Duration, then asks the server.
--- The outcome arrives on gksphone:client:screenRepairResult
exports["gksphone"]:RepairPhoneScreen(targetSource) -- targetSource optional
--- Asks the server to resend health. Call after the phone item is equipped.
exports["gksphone"]:RefreshScreenHealth()Phone Health
local battery = exports["gksphone"]:GetBatteryHealth() -- number
local condition = exports["gksphone"]:GetBatteryCondition() -- good | fair | poor | service
local health = exports["gksphone"]:GetPhoneHealth()
-- { batteryHealth, batteryCondition, chargeCycles }
--- Drain rate scaling from battery wear: 1.0 at full health, rising toward
--- Config.PhoneHealth.Battery.DrainScaling.MaxMultiplier
local multiplier = exports["gksphone"]:GetBatteryDrainMultiplier() -- number
local interval = exports["gksphone"]:GetBatteryDrainInterval(60000) -- base ms, scaled by wear
--- Asks the server to replace the battery. The outcome arrives on
--- gksphone:client:batteryReplaceResult
exports["gksphone"]:ReplacePhoneBattery(targetSource) -- targetSource optional
--- Asks the server to resend battery health.
exports["gksphone"]:RefreshBatteryHealth()Phone
isPhoneOpen
local isPhoneOpen = exports["gksphone"]:isPhoneOpen()
print(isPhoneOpen) -- true / falsePhoneOpen
In order for the phone to be open, a player must first open it from the inventory.
exports["gksphone"]:PhoneOpen()PhoneClose
exports["gksphone"]:PhoneClose()PhoneOpenBlock
Prevent the phone from turning on
local reason = "Phone cannot be used while handcuffed"
exports["gksphone"]:PhoneOpenBlock(reason)PhoneOpenUnBlock
If you have blocked the phone from turning on, you can activate it again with this export.
exports["gksphone"]:PhoneOpenUnBlock()PhoneOpenBlockStatus
local status, reason = exports["gksphone"]:PhoneOpenBlockStatus()
print(status, reason) -- true/false, reasonPhoneNumber
local phoneNumber = exports["gksphone"]:PhoneNumber()
print(phoneNumber) -- nil or 5555555PhoneUniqueId
local phoneUniqId = exports["gksphone"]:PhoneUniqueId()
print(phoneUniqId) -- GKS2222222Is Camera Open
local isCameraOpen = exports["gksphone"]:IsCameraOpen()
print(isCameraOpen) -- true or falseServices
Send Report
local reportMessage = "Report Message"
local reportPhoto = "Image Link" or nil
local job = "ambulance" -- job code
local anonymous = false -- or true
exports["gksphone"]:SendReport(reportMessage, reportPhoto, job, anonymous)SendDispatch
--- Alias of SendReport — same parameters, same behaviour.
--- Both attach the caller's coordinates and street automatically.
--- @param message string
--- @param photo string|nil
--- @param job string Job code
--- @param isAnonymous boolean
exports["gksphone"]:SendDispatch(reportMessage, reportPhoto, job, anonymous)Battery
GetPhoneBattery
local battery = exports["gksphone"]:GetPhoneBattery()
print(battery) -- The battery percentage, 0-100SetPhoneBattery
local battery = 100 -- The battery percentage, 0-100
exports["gksphone"]:SetPhoneBattery(battery)SavePhoneBattery
local battery = 100 -- The battery percentage, 0-100
exports["gksphone"]:SavePhoneBattery(battery)ToggleCharging
local charging = true -- true or false
exports["gksphone"]:ToggleCharging(charging)IsPhoneBatteryDead
local isBatteryDead = exports["gksphone"]:IsPhoneBatteryDead()
print(isBatteryDead) -- true or false / If the phone has 0% batteryIsPhoneCharging
local isCharging = exports["gksphone"]:IsPhoneCharging()
print(isCharging) -- true or falseSignal
You must enable Config.Signal (gksphone/config/signal/config.lua) to use Export. Signal requires polyzone
addSignal
local coord = vec3(-1378.91, -74.53, 51.29) -- v3
local radius = 5
local signalId = exports["gksphone"]:addSignal(coord, radius)
print(signalId) -- This information is required to remove the region.destroySignal
exports["gksphone"]:destroySignal(signalId)Map/GPS
AddMapLocation
exports['gksphone']:AddMapLocation({ id = 'biz_1', position = vector2(x, y), name = '24/7', description = 'Open' })RemoveMapLocation
exports['gksphone']:RemoveMapLocation('biz_1')UpdateMapLocation
exports['gksphone']:UpdateMapLocation('biz_1', { position = vector2(x, y) })Misc
heavyJammer
This export renders the phone unusable and only a message section appears in the middle of the screen.
local status = true -- true or false
local message = "The message you want to write on the screen"
local phoneUniqueId = "GKS22222" -- No Required
exports["gksphone"]:heavyJammer(status, message, phoneUniqueId)
-- Jam local player's phone (non-persistent, client-side only)
exports['gksphone']:heavyJammer(true, "No signal")
-- Jam a specific phone (persistent)
exports['gksphone']:heavyJammer(true, "Signal blocked", "TARGET_PHONE_ID")ToogleFocus
local status = true -- true or false
exports["gksphone"]:ToggleFocus(status)
