GKSHOP

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)

Mail

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 false

CreateCallNumber

-- 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")
end

RemoveCallNumber

local removecall, reason = exports['gksphone']:RemoveCallNumber("911")
if removecall then
    print("Call deleted successfully")
else
    print(reason)
end

HasCallNumber

--- 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 | nil

ClearLiveActivities

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 / false

PhoneOpen

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, reason

PhoneNumber

local phoneNumber = exports["gksphone"]:PhoneNumber()
print(phoneNumber) -- nil or 5555555

PhoneUniqueId

local phoneUniqId = exports["gksphone"]:PhoneUniqueId()
print(phoneUniqId) -- GKS2222222

Is Camera Open

local isCameraOpen = exports["gksphone"]:IsCameraOpen()
print(isCameraOpen) -- true or false

Services

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-100

SetPhoneBattery

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% battery

IsPhoneCharging

local isCharging = exports["gksphone"]:IsPhoneCharging()
print(isCharging) -- true or false

Signal

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)

On this page