Browse docs
Events
Events
Free Phone separates public observer events from internal transport events. Public observer events may be handled by another resource. Internal events coordinate Free Phone itself and must not be triggered as integration shortcuts.
Public CrewLink observer events
These events run locally on the server. They can be observed with AddEventHandler and are safe for
logging, dispatch synchronization, or read-only reactions.
| Event | Parameters | Fired when |
|---|---|---|
sky_phone:crewlink:memberJoined | (group_id, profile_id) | A profile joins a group. |
sky_phone:crewlink:memberLeft | (group_id, profile_id) | A profile leaves a group. |
sky_phone:crewlink:activeChanged | (profile_id, group_id) | A profile changes its active group. |
sky_phone:crewlink:pingCreated | (group_id, ping) | A group ping is created. |
sky_phone:crewlink:pingRemoved | (group_id, ping_id) | A group ping is removed. |
Observe membership changes
AddEventHandler("sky_phone:crewlink:memberJoined", function(group_id, profile_id)
print(("[crew-audit] Profile %s joined group %s"):format(
profile_id,
group_id
))
end)
AddEventHandler("sky_phone:crewlink:memberLeft", function(group_id, profile_id)
print(("[crew-audit] Profile %s left group %s"):format(
profile_id,
group_id
))
end)
Observe the active group
AddEventHandler("sky_phone:crewlink:activeChanged", function(profile_id, group_id)
if group_id then
print(("[crew-audit] Profile %s activated group %s"):format(
profile_id,
group_id
))
else
print(("[crew-audit] Profile %s has no active group"):format(profile_id))
end
end)
Observe shared pings
AddEventHandler("sky_phone:crewlink:pingCreated", function(group_id, ping)
print(("[crew-audit] Ping %s created in group %s at %.2f, %.2f, %.2f"):format(
ping.id,
group_id,
ping.coords.x,
ping.coords.y,
ping.coords.z
))
end)
AddEventHandler("sky_phone:crewlink:pingRemoved", function(group_id, ping_id)
print(("[crew-audit] Ping %s removed from group %s"):format(
ping_id,
group_id
))
end)
Do not call TriggerEvent with these names to change CrewLink state. Use CreateCrewLinkPing and
RemoveCrewLinkPing for supported mutations.
Custom-app iframe messages
Custom apps communicate with the phone host through a versioned postMessage contract. Every
message is validated against its iframe source, origin, app ID, request ID, permission, and payload
limit.
App to phone
| Message type | Required data | Purpose |
|---|---|---|
sky-phone-app:ready | appId, protocolVersion | Tell the host that the iframe bridge is ready. |
sky-phone-app:request | appId, protocolVersion, requestId, method, payload? | Request a permission-bound host method. |
Announce bridge readiness
window.parent.postMessage({
type: 'sky-phone-app:ready',
appId: 'dispatch-board',
protocolVersion: 1
}, '*')
Use the exact allowed host origin instead of * when the custom app knows it.
Request app storage
const requestId = crypto.randomUUID()
window.parent.postMessage({
type: 'sky-phone-app:request',
appId: 'dispatch-board',
protocolVersion: 1,
requestId,
method: 'device.storage.get',
payload: {
key: 'filters'
}
}, '*')
Supported request methods in ABI 1 are:
app.closeapp.opendevice.storage.getdevice.storage.setnotification.create
The matching permission must be declared in the client definition and server policy.
Phone to app
| Message type | Data | Purpose |
|---|---|---|
sky-phone-app:context | context | Supplies app ID, capabilities, locale, theme, scale, and safe area. |
sky-phone-app:open | data? | Supplies opening data from OpenCustomApp. |
sky-phone-app:message | payload | Delivers SendCustomAppMessage data. |
sky-phone-app:response | requestId, success, data?, error? | Resolves a bridge request. |
Receive host messages
window.addEventListener('message', (event) => {
const message = event.data
if (!message || typeof message !== 'object') return
switch (message.type) {
case 'sky-phone-app:context':
document.documentElement.dataset.theme = message.context.theme
break
case 'sky-phone-app:open':
openIncident(message.data?.incidentId)
break
case 'sky-phone-app:message':
applyPhoneMessage(message.payload)
break
case 'sky-phone-app:response':
resolveBridgeRequest(message)
break
}
})
Validate event.origin for remote apps before processing a message.
Custom-app lifecycle hooks
Client definitions may include lifecycle functions. They run inside the resource that registered the app and are useful for local cleanup or telemetry.
local definition = {
id = "dispatch-board",
name = "Dispatch Board",
ui = "web/index.html",
onInstall = function()
print("[my_dispatch] App installed")
end,
onOpen = function(data)
print(("[my_dispatch] App opened for incident %s"):format(
tostring(data and data.incidentId)
))
end,
onReady = function()
print("[my_dispatch] Iframe bridge ready")
end,
onClose = function()
print("[my_dispatch] App closed")
end,
}
exports["sky_phone"]:AddCustomApp(definition)
Internal push and net events
The following event families are private implementation details. They are listed so developers can recognize them in logs, not as a compatibility promise.
| Area | Internal examples |
|---|---|
| Device | sky_phone:device:open, sky_phone:device:updated, sky_phone:device:invalidated |
| Calls | sky_phone:call:incoming, sky_phone:call:state, sky_phone:calls:changed |
| Messages | sky_phone:messages:changed, sky_phone:messages:new |
| Billing | sky_phone:billing:changed, sky_phone:billing:new |
| Social apps | sky_phone:picstagram:new, sky_phone:feather:new, sky_phone:fliptok:new |
| Gallery and media | sky_phone:gallery:changed, sky_phone:media:upload-ready, sky_phone:media:upload-result |
| Voice Memos | sky_phone:memos:upload-ready, sky_phone:memos:upload-result |
| Radio | sky_phone:radio:members, sky_phone:radio:notification |
| Companies | sky_phone:companies:changed, sky_phone:companies:notification |
| Marketplace | sky_phone:marketplace:changed, sky_phone:marketplace:new-message |
Client-local lifecycle events
These events coordinate Free Phone's own focus, animations, and camera state:
sky_phone:client:nuiReady
sky_phone:nuiClosed
sky_phone:client:forceClose
sky_phone:client:setSuspended
sky_phone:client:setCameraFocus
sky_phone:client:cameraFocusApplied
sky_phone:client:setPayphoneFocus
sky_phone:animation:phone
sky_phone:animation:call
sky_phone:animation:camera
sky_phone:animation:reset
They are not public integration points. A custom app receives lifecycle through its hooks and the iframe bridge instead.