GKSHOP

Server Exports

Server-side exports for GKSPHONE V2 — messages, numbers, mail, map locations, and player phone data.

Messages

SendMessage

--- Sends a message from any number to another number
--- @param fromNumber string Sender phone number
--- @param toNumber string Receiver phone number
--- @param message string|table|vector2 Message content, table with coords {x,y}, or vector2 for GPS
--- @param options table|nil { skipBlockCheck = boolean, skipSIMUsage = boolean, saveSenderCopy = boolean }
---        skipBlockCheck  -- deliver even if the receiver blocked the sender
---        skipSIMUsage    -- do not count the message against the sender's SIM package
---        saveSenderCopy  -- keep a copy in the sender's thread (default true)
--- @return table { status = boolean, messageId = number|nil, error = string|nil }
exports["gksphone"]:SendMessage("101-11111", "101-22222", "Hello!")

exports["gksphone"]:SendMessage("101-11111", "101-22222", "Hello!", {
    skipBlockCheck = true,
    saveSenderCopy = false
})

SendSystemMessage

--- Sends a system/virtual message to a player
--- @param targetPhoneNumber string Receiver phone number
--- @param message string|table|vector2 Message content, table with coords {x,y}, or vector2 for GPS
--- @param senderNumber string Sender number/name (e.g. "Delivery", "LSCustom", "Police")
--- @return table { status = boolean, messageId = number|nil, error = string|nil }
local result = exports["gksphone"]:SendSystemMessage("101-22222", "Test Message", "Delivery")
-- result = { status = true, messageId = 12345 }
-- Error: { status = false, error = "Missing parameters" }

BroadcastSystemMessage

--- Broadcasts a system message to all online players
--- @param message string|table Message content
--- @param senderNumber string Sender number/name
--- @return table { status = boolean, sentCount = number }
exports["gksphone"]:BroadcastSystemMessage("Hello!", "Police")

Calls

CreateCall

local data = {
    number = "5551234",  -- Phone number (required if no job/company)
    job = "police",
    hideNumber = false   --  Anonymous / hidden caller ID
}

exports['gksphone']:CreateCall(source, data)
-- returns false, "invalid_source" | "invalid_data" (job checks run on client)

IsInCall

local inCall, callId, call = exports['gksphone']:IsInCall(source)
-- returns true, 5000, callData

GetCall

local call = exports['gksphone']:GetCall(callId)
-- returns
{
    callerSource = 1,
    callerPhone = '1111',
    callerPhoneUniq = 'GKS11111',
    calltype = 'calling',  -- calling or vidmeet
    time = 0, -- The os.time() when the call started
    status = true,
    is_anonymous = true,
    isJob = false,
    receivers = {} -- {receiverSource = 2, receiverPhone, receiverPhoneUniq, is_accepts}
}

EndCall

exports['gksphone']:EndCall(source)

Mail

Send Mail

local src = source or -1
local MailData = {
  sender = 'GKSHOP',
  image = '/html/img/icons/mail.png',
  subject = "GKSPHONE",
  message = 'TEST',
  buttons = {   --- If you don't want it to be a button, please remove it.
        {
            label = "Accept",
            type = "client_event",
            event = "myresource:client:accept",
            data = { id = 123 },
            color = "green",
            clearOnClick = true
        },
        {
            label = "Reject",
            type = "server_event",
            event = "myresource:server:reject",
            data = { id = 123 },
            color = "red",
            clearOnClick = false
        }
  },
  attachments = {
    "https://example.com/photo1.png",
    "https://example.com/photo2.png"
  }
}
exports["gksphone"]:SendNewMail(src, MailData)

Send Offline Mail

-- citizenID => QB Citizen Id
local xPlayer = QBCore.Functions.GetPlayer(source)
local citizenID = xPlayer.PlayerData.citizenid
local MailData = {
  sender = 'GKSHOP',
  image = '/html/img/icons/mail.png',
  subject = "GKSPHONE",
  message = 'TEST',
  buttons = {   --- If you don't want it to be a button, please remove it.
        {
            label = "Accept",
            type = "client_event",
            event = "myresource:client:accept",
            data = { id = 123 },
            color = "green"
        },
        {
            label = "Reject",
            type = "server_event",
            event = "myresource:server:reject",
            data = { id = 123 },
            color = "red",
            clearOnClick = false
        }
  },
  attachments = {
    "https://example.com/photo1.png",
    "https://example.com/photo2.png"
  }
}
exports["gksphone"]:SendNewMailOffline(citizenID, MailData)

SendPlayerMail

Sends mail from one player to another, as that player — unlike SendNewMail, which delivers a system mail.

--- @param source number Sender. Must have a phone with a mail address
--- @param payload 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
--- @return table { status = boolean, inboxId = number, sentId = number, sentMail = table }
---         error = "no_phone" | "invalid_to" | "invalid_subject" | "empty_body"
local result = exports["gksphone"]:SendPlayerMail(source, {
    to_mail = "[email protected]",
    subject = "Invoice",
    message = "Attached."
})

The recipient address is lowercased and stripped of whitespace before lookup.

Billing

New Billing

-- src => Player's ID
-- label => Billing description
-- society => By which job the billingwas created
-- senderBilling => Who is the person sending the billing?
-- senderID => ESX Identifier of the billing originator
-- amount => Billing price
local src = source
local label = "Excessive Speed"
local society = "police"
local senderBilling = "GKSHOP XENKNIGHT"  -- Player Name
local senderID = "char1:4b110a7811" -- xPlayer.identifier
local amount = 500
exports["gksphone"]:NewBilling(src, label, society, senderBilling, senderID, amount)

Is Unpaid Bill

To inquire if the player has any outstanding bills

local xPlayer = QBCore.Functions.GetPlayer(source)
local citizenID = xPlayer.PlayerData.citizenid
local isBills = exports["gksphone"]:IsUnpaidBillsbyCid(citizenID)
print(isBills) -- true or false

Misc

Send Notification

-- src => Player's ID
local src = source
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"]:sendNotification(src, NotifData)

New Number

local src = source -- player id
local phoneID = uniqID or nil
local NewNumber = "555555" -- example
local newNumber = exports["gksphone"]:NewNumber(src, phoneID, NewNumber)
print(newNumber) -- true/false

Change Number

Change phone number

local phoneID = "GKSXXXXXXX" -- Phone Uniq ID
local oldNumber = "5555"
local newNumber = "2222"
local updateContacts = true -- Whether to update contacts with the new number
local changeNumber = exports["gksphone"]:ChangeNumber(phoneID, oldNumber, newNumber, updateContacts)
print(changeNumber) -- true or false

Emergency Alert

local title = "Emergency Alert"
local message = "Test Alert"
exports["gksphone"]:EmergencyAlert(title, message)

GetPhoneResetTarget

--- The phone a reset would act on, so you can confirm before committing
--- @param source number
--- @return string|nil phoneUniqueId
local phoneId = exports["gksphone"]:GetPhoneResetTarget(source)

ResetPhoneData

--- Resets a phone to its as-new state and issues a new number
--- @param target number|string Player source or phone unique_id (works while offline)
--- @param options table|nil { keepNumber = boolean } Wipe content but keep the current number
--- @return boolean ok, string|nil reason, table|nil result
--- reason = "bad_target" | "no_phone" | "unknown_phone"
local ok, reason, result = exports["gksphone"]:ResetPhoneData(source)
-- result = { phoneId = "PHONE-1234", identifier = "char1:xxx", oldNumber = "101-22222", newNumber = "101-55555" }

exports["gksphone"]:ResetPhoneData("PHONE-1234")                  -- offline player
exports["gksphone"]:ResetPhoneData(source, { keepNumber = true }) -- wipe content, keep the number

WipePhoneData

--- Deletes phone content only (messages, contacts, gallery, voice memos, notes)
--- Keeps the number and all settings — used when a phone-cracking attempt fails
--- @param phoneUniqID string
exports["gksphone"]:WipePhoneData("PHONE-1234")

Services

Send Report

local src = source
local ped = GetPlayerPed(src)

local reportMessage = "Dispatch Message"
local reportPhoto = "Image Link" or nil
local job = "police" -- job code 
local anonymous = false -- or true
local playerCoords = GetEntityCoords(ped)
local streedZone = "Street name" or "Unknown"
local sendDispatch = exports["gksphone"]:SendReport(src, reportMessage, reportPhoto, job, anonymous, playerCoords, streedZone)
print(sendDispatch) -- true/false

Job Status Change

The job in the Dispatch section is for opening and closing

local job = "police" -- job code
local status = true -- true or false
exports["gksphone"]:JobStatusChange(job, status)

Job Status

local job = "police" -- job code
local jobStatus = exports["gksphone"]:IsJobStatus(job)
print(jobStatus) -- true or false

Bank App

BankSaveHistory

--- Writes a transaction to a player's bank history, by source
--- @param source number
--- @param type number 1 = outgoing (-), 2 = incoming (+)
--- @param amount number
--- @param description string
--- @return boolean
local ok = exports["gksphone"]:BankSaveHistory(source, 1, 500, "Bank History Desc")

bankHistorySave

--- The same write, addressed by phone number instead of source.
--- Use it when you have the number rather than a source.
--- @param phoneNumber string
--- @param type number 1 = outgoing (-), 2 = incoming (+)
--- @param amount number
--- @param description string
--- @param phoneUniqID string
--- @return boolean true only when the phone is loaded in memory.
---         The row is inserted either way, so a false here does not mean the
---         transaction was lost. The live in-app update is pushed only if the
---         player is online.
local ok = exports["gksphone"]:bankHistorySave("101-22222", 2, 500, "Salary", "GKS111111")

Custom App

Add Custom App

local appData = {
	name = "mdt", --- A unique name
	icons = "/html/img/icons/mdt.png",  -- logo url
	description = "",  -- App description that will appear in the app store
	appurl = "https://cfx-nui-gksphone-app/ui/index.html",  -- custom app url, required
	blockedjobs = {},
	allowjob = {},
	show = true,
	labelLangs = {   -- App name by languages
		af = "MDT",
		ar = "MDT",
		cs = "MDT",
		de = "MDT",
		en = "MDT",
		es = "MDT",
		fr = "MDT",
		id = "MDT",
		nl = "MDT",
		["pt-PT"] = "MDT",
		ro = "MDT",
		sv = "MDT",
		th = "MDT",
		tr = "MDT",
		uk = "MDT",
		["zh-TW"] = "MDT"
	}
}
exports["gksphone"]:AddCustomApp(appData)

Stock market

Asset ids come from Config.Stocks in config.lualifeinvader, mazebank, postop, vangelico and flyus ship by default.

stockMarketAdd

--- Credits an asset to a phone's holdings
--- @param source number
--- @param coinid string Asset id from Config.Stocks
--- @param amount number
--- @param phoneUniqID string
--- @return boolean
local ok = exports["gksphone"]:stockMarketAdd(source, "lifeinvader", 5, "GKS111111")

stockMarketRemove

--- Debits an asset from a phone's holdings
--- @param source number
--- @param coinid string Asset id from Config.Stocks
--- @param amount number
--- @param phoneUniqID string
--- @return boolean
local ok = exports["gksphone"]:stockMarketRemove(source, "lifeinvader", 5, "GKS111111")

stockMarketTransfer

--- Moves an asset from one phone's holdings to another's, writing a history entry on both
--- @param source number
--- @param coinid string Asset id from Config.Stocks
--- @param amount number
--- @param phoneUniqID string Sender's phone unique id
--- @param TransferPhoneData table Receiver's phone data, e.g. from GetPhoneDataByNumber
--- @return boolean false when the asset is unknown or the sender holds less than amount
local target = exports["gksphone"]:GetPhoneDataByNumber("101-22222")
local ok = exports["gksphone"]:stockMarketTransfer(source, "lifeinvader", 5, "GKS111111", target)

getstockmarket

--- Every asset with its current price
--- @return table
local market = exports["gksphone"]:getstockmarket()

getstockmarketprice

--- @param symbol string Asset id, or its symbol in lowercase (e.g. "liv")
--- @return number|nil nil when the asset is unknown
local price = exports["gksphone"]:getstockmarketprice("lifeinvader")

setstockprice

--- Sets a price and pushes it to every client. Rounded to 2 decimals.
--- @param symbol string Asset id, or its symbol in lowercase
--- @param newPrice number
exports["gksphone"]:setstockprice("lifeinvader", 175.5)

updatemarket

--- Runs one price tick immediately, instead of waiting for Config.StockUpdateInterval
exports["gksphone"]:updatemarket()

setstockprice and updatemarket are only registered when Config.StockMarket = true. The two getters are always available.

Live Stream

Add Cheer

local src = source 
local amount = 5000
local addCheer = exports["gksphone"]:AddLiveStreamCheer(src, amount) 
print(addCheer) -- true or false

Add Coin

local src = source 
local amount = 5000
local addCoin = exports["gksphone"]:AddLiveStreamCoin(src, amount) 
print(addCoin) -- true or false

Social Media

Toggle Verified

local app = 'squawk' -- 'squawk' or 'snapgram'
local username = '...' -- username in the app
local verified = 1 -- 0 = none / 1 = blue / 2 = yellow(only squawk)
local res = exports["gksphone"]:ToggleVerified(app, username, verified)
print(res) -- true or false

Heavy Jammer

heavyJammerByPhone

local phoneUniqueId = "GKS2026AAAAA" -- The unique identifier of the phone
local status = true -- true to enable jammer, false to disable
local message = "Test" -- Custom message displayed on the jammed phone
exports['gksphone']:heavyJammerByPhone(phoneUniqueId, status, message)

--- @return boolean Returns true if successful, false if phoneUniqueId is nil

heavyJammerByPhones

Jam or unjam multiple phones at once.

-- Jam multiple phones
local phoneIds = {"PHONE_001", "PHONE_002", "PHONE_003"}
local count = exports['gksphone']:heavyJammerByPhones(phoneIds, true, "Signal blocked by authorities")
print(count .. " phones jammed")

-- Unjam all phones in the list
exports['gksphone']:heavyJammerByPhones(phoneIds, false, "")

isPhoneJammed

Check if a specific phone is currently jammed.

-- phoneUniqueId (The unique identifier of the phone)
local isJammed = exports['gksphone']:isPhoneJammed("ABC123XYZ")
-- return boolean|nil
if isJammed == nil then
    print("Invalid phone ID")
elseif isJammed then
    print("Phone is currently jammed")
else
    print("Phone has normal signal")
end

getJammedPhones

Get a list of all currently jammed phones.

local jammedPhones = exports['gksphone']:getJammedPhones()
--[[ Return table |  { phoneUniqueId = { status, message, jammedAt }, ... }
{
    ["PHONE_ID_1"] = {
        status = true,           -- Always true for jammed phones
        message = "...",         -- The jammer message
        jammedAt = 1704067200    -- Unix timestamp when jammed
    },
    ["PHONE_ID_2"] = { ... }
}
]] --
for phoneId, data in pairs(jammedPhones) do
    local duration = os.time() - data.jammedAt
    print(string.format(
        "Phone: %s | Message: %s | Jammed for: %d seconds",
        phoneId,
        data.message,
        duration
    ))
end

clearAllJammers

local cleared = exports['gksphone']:clearAllJammers()
-- return number | Count of jammers that were cleared
print(cleared .. " phones have been unjammed")

Map / GPS

AddMapLocation

-- Add location to specific player
exports['gksphone']:AddMapLocation(playerSource, { 
    id = 'business_247_1', 
    position = { x = 25.7, y = -1346.7 },
    name = '24/7 Store',
    description = 'Open Now',
    icon = 'https://...',
    category = 'business'
})

-- Add location to all players (source = -1)
exports['gksphone']:AddMapLocation(-1, { id = 'event_party', position = { x = 100, y = 200 }, name = 'Party' })

RemoveMapLocation

exports['gksphone']:RemoveMapLocation(playerSource, 'business_247_1')
exports['gksphone']:RemoveMapLocation(-1, 'event_party')

UpdateMapLocation

exports['gksphone']:UpdateMapLocation(playerSource, 'vehicle_123', { position = { x = 150, y = -300 } })

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(source, 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(source, "delivery:1234", { progress = 60 })

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(source, "delivery:1234", "success", { subtitle = "Delivered" })
exports["gksphone"]:EndLiveActivity(source, "delivery:1234", "canceled", { immediate = true })

Screen Damage

ApplyScreenDamage

--- Applies damage using the config entry for that trigger; the chance roll and
--- the amount are decided server-side
--- @param trigger string death | crash | fall | water
--- @return boolean applied -- false if disabled, no phone, or the roll failed
exports["gksphone"]:ApplyScreenDamage(source, "crash")

DamageScreen

--- Removes an exact amount, no chance roll. For explosions, melee, etc.
--- @param amount number positive
--- @param reason string|nil shows up in the event payload
--- @return number|nil newHealth
local health = exports["gksphone"]:DamageScreen(source, 25, "explosion")

SetScreenHealth

--- Sets health to an exact value. For partial repairs or admin tooling
--- @param value number 0-100
--- @return number|nil appliedHealth
exports["gksphone"]:SetScreenHealth(source, 100, "admin")

SetWaterDamage

--- Toggles the water damage flag on its own, leaving health alone
--- @param state boolean
--- @return boolean
exports["gksphone"]:SetWaterDamage(source, false) -- e.g. a rice-bowl drying mechanic

GetScreenHealth

local health = exports["gksphone"]:GetScreenHealth(source) -- number | nil

GetScreenCondition

local condition = exports["gksphone"]:GetScreenCondition(source)
-- { phoneUniqueId, health, severity, waterDamage, lastDamageAt, lastRepairAt }

GetScreenRepairQuote

--- Price without charging, so a shop UI can show it
--- @param targetSource number|nil the phone owner when a mechanic quotes someone else
local quote = exports["gksphone"]:GetScreenRepairQuote(source, targetSource)
-- { health, severity, waterDamage, price, selfRepair }

RepairPhoneScreen

--- Repairs to 100, clears water damage and charges the bank
--- @param targetSource number|nil nil = self repair
--- @return boolean ok, string|nil reason, number|nil price
--- reason = "disabled" | "self_repair_disabled" | "no_permission" | "cooldown" | "no_phone" | "not_damaged" | "no_money"
local ok, reason, price = exports["gksphone"]:RepairPhoneScreen(source, targetSource)

Phone Health

Battery health, charge-cycle wear and paid battery replacements. The client reports how much it charged; the server decides the wear.


GetPhoneHealth

local health = exports["gksphone"]:GetPhoneHealth(source)
-- { phoneUniqueId, batteryHealth, batteryCondition, chargeCycles, screenHealth, waterDamage, lastRepairAt }

GetBatteryHealth

local battery = exports["gksphone"]:GetBatteryHealth(source) -- number | nil

SetBatteryHealth

--- Sets battery health directly. For admin tooling or custom mechanics
--- @param value number 0-100
--- @return number|nil appliedHealth
exports["gksphone"]:SetBatteryHealth(source, 100)

AddBatteryChargeProgress

--- Records charge progress and applies cycle wear once a full cycle completes
--- @param points number percentage points charged since the last report
--- @return number|nil batteryHealth
exports["gksphone"]:AddBatteryChargeProgress(source, 20)

GetBatteryReplacementQuote

--- Price without charging, so a shop UI can show it
--- @param targetSource number|nil the phone owner when a mechanic quotes someone else
local quote = exports["gksphone"]:GetBatteryReplacementQuote(source, targetSource)
-- { health, condition, cycles, price, selfReplace }

ReplacePhoneBattery

--- Resets battery health to 100, clears the cycle counter and charges the bank
--- @param targetSource number|nil nil = self replace
--- @return boolean ok, string|nil reason, number|nil price
--- reason = "disabled" | "self_replace_disabled" | "no_permission" | "cooldown" | "no_phone" | "not_worn" | "no_money"
local ok, reason, price = exports["gksphone"]:ReplacePhoneBattery(source, targetSource)

Phone Data Exports

GetPhoneBySource

Find phone number with Source

local src = source
local phoneNumber = exports["gksphone"]:GetPhoneBySource(src)
print(phoneNumber)

-- OR

local xPlayer = QBCore.Functions.GetPlayer(source)
local phoneNumber = xPlayer.PlayerData.charinfo.phone
print(phoneNumber)

GetSourceByPhone

Finding a source by phone number

local phoneNumber = number
local source = exports["gksphone"]:GetSourceByPhone(phoneNumber)
print(source)

-- OR

local phoneNumber = number
local xPlayer = QBCore.Functions.GetPlayerByPhone(phoneNumber)
local source = xPlayer.PlayerData.source
print(source)

GetPhoneDataBySource

Access the data of the phone used with the Source number

local src = source
local phoneData = exports["gksphone"]:GetPhoneDataBySource(src)
print(json.encode(phoneData))

GetPhoneDataByNumber

Accessing the phone's data with the phone number

local phoneNumber = number
local phoneData = exports["gksphone"]:GetPhoneDataByNumber(phoneNumber)
print(json.encode(phoneData))

GetPhoneDataBySetupOwner

Accessing all phone data of the player

local xPlayer = QBCore.Functions.GetPlayer(source)
local citizenID = xPlayer.PlayerData.citizenid
local phoneData = exports["gksphone"]:GetPhoneDataBySetupOwner(citizenID)
print(json.encode(phoneData))

GetPhoneDataByPhoneUniqID

Access phone data with the phone's UniqID

local phoneUniqID = id
local phoneData = exports["gksphone"]:GetPhoneDataByPhoneUniqID(phoneUniqID)
print(json.encode(phoneData))

GetPhoneDataByCitizenID

Access phone data with the user's ID (You will access the data of the last phone the user opened)

local xPlayer = QBCore.Functions.GetPlayer(source)
local citizenID = xPlayer.PlayerData.citizenid
local phoneData = exports["gksphone"]:GetPhoneDataByCitizenID(citizenID)
print(json.encode(phoneData))

GetPhoneLangBySource

The language the user chooses on the phone

local src = source
local PhoneLang = exports["gksphone"]:GetPhoneLangBySource(src)
print(PhoneLang)

On this page