API Reference
Complete API reference for Tommy's Radio exports, functions, and events.
Server Exports
Channel Information
getSpeakersInChannel(frequency)
Get all users connected to transmit on a specific channel.
Parameters:
frequency(string/number) - Channel frequency (e.g., "154.755")
Returns: table - Array of server IDs actively speaking on the channel
local speakers = exports['radio']:getSpeakersInChannel("154.755")
for _, serverId in ipairs(speakers) do
local name = exports['radio']:getPlayerName(serverId)
print(name .. " can transmit on dispatch")
endgetListenersInChannel(frequency)
Get all users listening to a channel without transmit capability.
Parameters:
frequency(string/number) - Channel frequency
Returns: table - Array of server IDs listening to the channel
local listeners = exports['radio']:getListenersInChannel("154.755")
print("Channel has " .. #listeners .. " listeners")getAllUsersInChannel(frequency)
Get all users on a channel regardless of role (speakers + listeners combined).
Parameters:
frequency(string/number) - Channel frequency
Returns: table - Array of server IDs on the channel
local allUsers = exports['radio']:getAllUsersInChannel("154.755")
print("Total users on channel: " .. #allUsers)getActiveTalkersInChannel(frequency)
Get users actively talking right now on the channel (PTT pressed).
Parameters:
frequency(string/number) - Channel frequency
Returns: table - Array of server IDs currently transmitting
local talkers = exports['radio']:getActiveTalkersInChannel("154.755")
if #talkers > 0 then
print("Someone is transmitting on dispatch!")
endgetActiveChannels()
Get list of all channels with active users.
Returns: table - Array of active channel frequencies
local activeChannels = exports['radio']:getActiveChannels()
for _, frequency in ipairs(activeChannels) do
print("Channel " .. frequency .. " is active")
endgetChannelInfo(channel)
Get detailed information about a specific channel.
Parameters:
channel(string/number) - Channel frequency
Returns: table - Channel object with speakers, listeners, activeTalkers arrays, or nil
local channelInfo = exports['radio']:getChannelInfo("154.755")
if channelInfo then
print("Speakers: " .. #channelInfo.speakers)
print("Listeners: " .. #channelInfo.listeners)
print("Active Talkers: " .. #channelInfo.activeTalkers)
endgetAllChannels()
Get complete list of channels from config with zones, names, types, etc.
Returns: table - Array of all configured channels with full details
local channels = exports['radio']:getAllChannels()
for _, channel in ipairs(channels) do
print(channel.name .. " - " .. channel.frequency .. " (" .. channel.zone .. ")")
endAlert & Signal Management
getChannelAlert(frequency)
Get active alert on a channel (e.g., "Signal 100").
Parameters:
frequency(string/number) - Channel frequency
Returns: table - Alert object {name, color, tone, isPersistent} or nil if no alert
local alert = exports['radio']:getChannelAlert("154.755")
if alert then
print("Alert active: " .. alert.name)
endsetChannelSignal(frequency, enabled)
Activate or clear an alert on a channel.
Parameters:
frequency(string/number) - Channel frequencyenabled(boolean) - Alert state
Returns: boolean - true on success
-- Activate Signal 100 on dispatch
exports['radio']:setChannelSignal("154.755", true)
-- Clear signal after 60 seconds
Wait(60000)
exports['radio']:setChannelSignal("154.755", false)getChannelPanic(frequency)
Get all users with panic activated on a channel.
Parameters:
frequency(string/number) - Channel frequency
Returns: table - Table of server IDs with active panic buttons
local panicUsers = exports['radio']:getChannelPanic("154.755")
for serverId, _ in pairs(panicUsers) do
print("User " .. serverId .. " has panic active!")
endsetChannelPanic(frequency, serverId, enabled)
Set or clear panic button for a specific user on a channel.
Parameters:
frequency(string/number) - Channel frequencyserverId(number) - Player's server IDenabled(boolean) - Panic state
Returns: boolean - true on success
-- Activate panic for officer
exports['radio']:setChannelPanic("154.755", 1, true)
-- Clear panic
exports['radio']:setChannelPanic("154.755", 1, false)getUserPanicState(serverId, frequency)
Check if a specific user has panic active on a channel.
Parameters:
serverId(number) - Player's server IDfrequency(string/number) - Channel frequency
Returns: boolean - Panic state
if exports['radio']:getUserPanicState(1, "154.755") then
print("Officer has emergency activated!")
endUser Management
setUserChannel(serverId, frequency)
Move a user to a specific channel (handles trunked channels automatically).
Parameters:
serverId(number) - Player's server IDfrequency(string/number) - Target channel frequency
Returns: boolean - true on success
-- Move officer to tactical channel
local success = exports['radio']:setUserChannel(1, "155.475")
if success then
print("Officer moved to tactical channel")
enddisconnectUser(serverId)
Disconnect a user from the radio system.
Parameters:
serverId(number) - Player's server ID
Returns: boolean - true on success
-- Remove user from radio (e.g., when going off-duty)
exports['radio']:disconnectUser(1)isUserTalking(serverId, frequency)
Check if a specific user is actively transmitting on a channel.
Parameters:
serverId(number) - Player's server IDfrequency(string/number) - Channel frequency
Returns: boolean - Transmit state
if exports['radio']:isUserTalking(1, "154.755") then
print("User 1 is transmitting on dispatch")
endUser Information
getPlayerName(serverId)
Get the display name of a player.
Parameters:
serverId(number) - Player's server ID
Returns: string - Player name
local name = exports['radio']:getPlayerName(1)
print("Player name: " .. name)getUserNacId(serverId)
Get a user's Network Access Code ID.
Parameters:
serverId(number) - Player's server ID
Returns: string - NAC ID or nil
local nacId = exports['radio']:getUserNacId(1)
if nacId then
print("NAC ID: " .. nacId)
endgetUserInfo(serverId)
Get both name and NAC ID for a user.
Parameters:
serverId(number) - Player's server ID
Returns: table - User info {name, nacId}
local userInfo = exports['radio']:getUserInfo(1)
print(userInfo.name .. " - NAC: " .. (userInfo.nacId or "N/A"))hasRadioAccess(serverId)
Check if a user has radio system access.
Parameters:
serverId(number) - Player's server ID
Returns: boolean - Access state
if exports['radio']:hasRadioAccess(source) then
-- Grant radio permissions
endAudio Control
playToneOnChannel(frequency, tone)
Play a tone to all users on a specific channel.
Parameters:
frequency(string/number) - Channel frequencytone(string) - Tone name (e.g., "beep", "chirp")
-- Play tone to entire dispatch channel
exports['radio']:playToneOnChannel("154.755", "alert")playToneOnSource(serverId, tone)
Play a tone to a specific user only.
Parameters:
serverId(number) - Player's server IDtone(string) - Tone name
-- Play confirmation tone to specific officer
exports['radio']:playToneOnSource(1, "beep")Client Exports
Radio Control
setCurrentChannel(channel)
Set your main speaking channel.
Parameters:
channel(string/number) - Channel frequency
-- Switch to dispatch channel
exports['radio']:setCurrentChannel("154.755")getCurrentFrequency()
Get your current channel frequency.
Returns: string/number - Current frequency, or -1 if not connected
local freq = exports['radio']:getCurrentFrequency()
if freq ~= -1 then
print("Current frequency: " .. freq)
endgetCurrentChannel()
Get complete information about your current channel.
Returns: table - Channel object with full details
local channel = exports['radio']:getCurrentChannel()
if channel then
print("On: " .. channel.name .. " (" .. channel.frequency .. ")")
endopenRadio(shouldFocus)
Open the radio interface UI with optional focus control.
Parameters:
shouldFocus(boolean, optional) - Whether to focus the radio UI. Defaults totrueif not provided.true- Opens and focuses the radio (or focuses if already open)false- Opens without focusing (only if radio is closed)
Returns: void
-- Open and focus radio (default behavior)
exports['radio']:openRadio()
-- Explicitly open and focus
exports['radio']:openRadio(true)
-- Open without focusing
exports['radio']:openRadio(false)
-- Focus an already-open radio
if exports['radio']:isRadioOpen() and not exports['radio']:isRadioFocused() then
exports['radio']:openRadio(true)
endcloseRadio()
Close the radio interface UI.
exports['radio']:closeRadio()Listening Channels
addListeningChannel(channel)
Add a channel to your listening list (scan).
Parameters:
channel(string/number) - Channel frequency
-- Add tactical channel to scan
exports['radio']:addListeningChannel("155.475")removeListeningChannel(channel)
Remove a channel from your listening list.
Parameters:
channel(string/number) - Channel frequency
exports['radio']:removeListeningChannel("155.475")getListeningChannels()
Get all channels you're currently scanning.
Returns: table - Array of channel frequencies
local listening = exports['radio']:getListeningChannels()
print("Scanning " .. #listening .. " channels")Transmission Control
startTransmitting()
Start PTT transmission (same as pressing PTT key).
exports['radio']:startTransmitting()stopTransmitting()
Stop PTT transmission (same as releasing PTT key).
exports['radio']:stopTransmitting()isTransmitting()
Check if you're currently transmitting.
Returns: boolean - Transmit state
if exports['radio']:isTransmitting() then
print("You are transmitting")
endgetActiveTalker()
Get information about who's currently talking on your frequency.
Returns: table - Talker info {serverId, name, frequency} or nil
local talker = exports['radio']:getActiveTalker()
if talker then
print(talker.name .. " is talking on " .. talker.frequency)
endsetTalking(talking)
Set talking state for UI feedback.
Parameters:
talking(boolean) - Talking state
exports['radio']:setTalking(true)Radio State
isConnected()
Check if connected to a channel.
Returns: boolean - Connection state
if exports['radio']:isConnected() then
print("Radio connected")
endisPowerOn()
Check if radio is powered on.
Returns: boolean - Power state
if exports['radio']:isPowerOn() then
print("Radio is on")
endisRadioOpen()
Check if radio UI is currently open.
Returns: boolean - UI state
if exports['radio']:isRadioOpen() then
print("Radio UI is displayed")
endisRadioFocused()
Check if radio UI is currently focused (has NUI focus).
Returns: boolean - Focus state
if exports['radio']:isRadioFocused() then
print("Radio UI is focused")
end
-- Check if radio is open but not focused
if exports['radio']:isRadioOpen() and not exports['radio']:isRadioFocused() then
print("Radio is open but not focused")
endAudio & Volume
setVolume(volume)
Set radio voice volume.
Parameters:
volume(number) - Volume level 0.0 to 1.0
exports['radio']:setVolume(0.75)getVolume()
Get current radio voice volume.
Returns: number - Volume level 0.0 to 1.0
local volume = exports['radio']:getVolume()
print("Voice volume: " .. (volume * 100) .. "%")setToneVolume(volume)
Set tone/SFX volume.
Parameters:
volume(number) - Volume level 0.0 to 1.0
exports['radio']:setToneVolume(0.35)getToneVolume()
Get current tone/SFX volume.
Returns: number - Volume level 0.0 to 1.0
local toneVol = exports['radio']:getToneVolume()
print("Tone volume: " .. (toneVol * 100) .. "%")set3DVolume(volume)
Set 3D audio volume.
Parameters:
volume(number) - Volume level 0.0 to 1.0
exports['radio']:set3DVolume(0.5)get3DVolume()
Get current 3D audio volume.
Returns: number - Volume level 0.0 to 1.0
local vol3D = exports['radio']:get3DVolume()
print("3D audio volume: " .. (vol3D * 100) .. "%")playTone(tone)
Play a tone locally.
Parameters:
tone(string) - Tone name (e.g., "beep", "chirp")
exports['radio']:playTone("beep")Appearance & Layout
setRadioLayout(layout)
Change radio style/model.
Parameters:
layout(string) - Radio model name (e.g., "ATX-8000")
exports['radio']:setRadioLayout("ATX-8000")getRadioLayout()
Get current radio layout.
Returns: string - Radio model name
local layout = exports['radio']:getRadioLayout()
print("Radio model: " .. layout)setRadioTheme(theme)
Change radio UI theme.
Parameters:
theme(string) - Theme name (e.g., "Dark", "Auto")
exports['radio']:setRadioTheme("Dark")getRadioTheme()
Get current radio theme.
Returns: string - Theme name
local theme = exports['radio']:getRadioTheme()
print("Theme: " .. theme)setAnimationId(animId)
Set radio animation.
Parameters:
animId(number) - Animation index
exports['radio']:setAnimationId(2)getAnimationId()
Get current animation ID.
Returns: number - Animation index
local animId = exports['radio']:getAnimationId()
print("Animation ID: " .. animId)Settings
setEarbudsEnabled(enabled)
Enable/disable earbuds (disables 3D audio).
Parameters:
enabled(boolean) - Earbuds state
exports['radio']:setEarbudsEnabled(true)getEarbudsEnabled()
Get earbuds state.
Returns: boolean - Earbuds enabled
if exports['radio']:getEarbudsEnabled() then
print("Earbuds mode active")
endsetGPSEnabled(enabled)
Enable/disable GPS tracking.
Parameters:
enabled(boolean) - GPS state
exports['radio']:setGPSEnabled(true)getGPSEnabled()
Get GPS state.
Returns: boolean - GPS enabled
if exports['radio']:getGPSEnabled() then
print("GPS tracking enabled")
endAlerts & Panic
triggerAlertOnChannel(frequency, enabled)
Trigger or clear an alert on a channel (sends to server).
Parameters:
frequency(string/number) - Channel frequencyenabled(boolean) - Alert state
-- Trigger Signal 100
exports['radio']:triggerAlertOnChannel("154.755", true)panicButton(frequency, enabled)
Activate or clear panic button on a channel.
Parameters:
frequency(string/number) - Channel frequencyenabled(boolean) - Panic state
-- Activate panic
exports['radio']:panicButton("154.755", true)User Information
getCurrentName()
Get your own player name.
Returns: string - Your player name
local myName = exports['radio']:getCurrentName()
print("You are: " .. myName)getCurrentNacId()
Get your own NAC ID.
Returns: string - Your NAC ID or nil
local myNac = exports['radio']:getCurrentNacId()
if myNac then
print("Your NAC: " .. myNac)
endgetZone()
Get your current zone name.
Returns: string - Zone name
local zone = exports['radio']:getZone()
print("Current zone: " .. zone)System
getBatteryLevel()
Get radio battery level percentage.
Returns: number - Battery level 0-100
local battery = exports['radio']:getBatteryLevel()
print("Battery: " .. battery .. "%")getSignalStrength()
Get current signal strength.
Returns: number/boolean - Signal strength value
local signal = exports['radio']:getSignalStrength()
print("Signal: " .. signal)getConnectionDiagnostics()
Get detailed connection diagnostics for troubleshooting.
Returns: table - Diagnostic information
local diagnostics = exports['radio']:getConnectionDiagnostics()
for key, value in pairs(diagnostics) do
print(key .. ": " .. tostring(value))
endUsage Examples
Basic Channel Management
-- Get all users on dispatch
local speakers = exports['radio']:getSpeakersInChannel("154.755")
local listeners = exports['radio']:getListenersInChannel("154.755")
print("Dispatch channel has:")
print("- " .. #speakers .. " units with transmit")
print("- " .. #listeners .. " supervisors listening")
-- Move officer to tactical
exports['radio']:setUserChannel(1, "155.475")
-- Check who's talking
local talkers = exports['radio']:getActiveTalkersInChannel("154.755")
if #talkers > 0 then
for _, serverId in ipairs(talkers) do
local name = exports['radio']:getPlayerName(serverId)
print(name .. " is transmitting")
end
endEmergency System Integration
-- Server-side emergency response
RegisterNetEvent('radio:handlePanic')
AddEventHandler('radio:handlePanic', function(frequency)
local serverId = source
local name = exports['radio']:getPlayerName(serverId)
local nacId = exports['radio']:getUserNacId(serverId)
-- Activate panic
exports['radio']:setChannelPanic(frequency, serverId, true)
-- Trigger Signal 100 on dispatch
if frequency == "154.755" then
exports['radio']:setChannelSignal(frequency, true)
print("EMERGENCY: " .. name .. " (" .. nacId .. ") activated panic!")
-- Play alert tone to all dispatch users
exports['radio']:playToneOnChannel(frequency, "emergency")
-- Auto-clear after 2 minutes
SetTimeout(120000, function()
exports['radio']:setChannelSignal(frequency, false)
exports['radio']:setChannelPanic(frequency, serverId, false)
end)
end
end)Radio State Monitoring
-- Check radio status before transmitting
Citizen.CreateThread(function()
while true do
Wait(5000)
if exports['radio']:isConnected() and exports['radio']:isPowerOn() then
local freq = exports['radio']:getCurrentFrequency()
local channel = exports['radio']:getCurrentChannel()
if freq ~= -1 then
print("Connected to: " .. channel.name)
-- Check who's talking
local talker = exports['radio']:getActiveTalker()
if talker then
print(talker.name .. " is speaking")
end
-- Monitor battery
local battery = exports['radio']:getBatteryLevel()
if battery < 20 then
print("WARNING: Low battery (" .. battery .. "%)")
end
end
else
print("Radio offline")
end
end
end)Multi-Channel Scanning
-- Setup multi-channel monitoring for supervisors
local function setupSupervisorRadio()
-- Set primary channel to dispatch
exports['radio']:setCurrentChannel("154.755")
-- Add listening channels for all divisions
exports['radio']:addListeningChannel("155.475") -- Tactical 1
exports['radio']:addListeningChannel("155.490") -- Tactical 2
exports['radio']:addListeningChannel("154.920") -- Fire/EMS
-- Configure volumes
exports['radio']:setVolume(0.8)
exports['radio']:setToneVolume(0.3)
exports['radio']:set3DVolume(0.5)
print("Supervisor radio configured")
local listening = exports['radio']:getListeningChannels()
print("Monitoring " .. #listening .. " channels")
endAdvanced Channel Info
-- Get comprehensive channel status
local function getChannelStatus()
local activeChannels = exports['radio']:getActiveChannels()
for _, frequency in ipairs(activeChannels) do
local info = exports['radio']:getChannelInfo(frequency)
local alert = exports['radio']:getChannelAlert(frequency)
local panicUsers = exports['radio']:getChannelPanic(frequency)
print("=== Channel " .. frequency .. " ===")
print("Speakers: " .. #info.speakers)
print("Listeners: " .. #info.listeners)
print("Active Talkers: " .. #info.activeTalkers)
if alert then
print("ALERT: " .. alert.name)
end
local panicCount = 0
for _ in pairs(panicUsers) do panicCount = panicCount + 1 end
if panicCount > 0 then
print("PANIC: " .. panicCount .. " units")
end
end
endError Handling
Important Notes
- Always check return values from export functions
- Frequencies can be passed as strings ("154.755") or numbers (154.755)
- Volume values are normalized to 0.0-1.0 range (not 0-100)
- Server exports return
nilorfalseon failure - Client exports return
-1ornilwhen radio is disconnected - NAC ID checks should handle
nilreturns gracefully
Web API Routes
Tommy's Radio includes a REST API and Socket.IO server for external integrations, allowing dispatch panels, third-party tools, and automation systems to interact with the radio system.
Configuration
Set these values in config.lua:
Config = {
serverPort = 7777, -- API server port
authToken = "CHANGE_ME_TO_RANDOM_TEXT", -- YOU CREATE THIS - any random secure string
dispatchNacId = "CHANGE_ME", -- YOU CHOOSE THIS - your dispatch panel password
}Security Warning
YOU must create these values yourself:
- authToken: Create any random string for encryption (e.g.,
"MyServer_Token_2024!") - dispatchNacId: Choose your dispatch panel password (e.g.,
"DISPATCH2024")
The values shown above (CHANGE_ME_TO_RANDOM_TEXT and CHANGE_ME) are placeholders. Never use default values in production. Anyone with these credentials can control your radio system.
Authentication
Step 1: Authenticate and Get Session
Endpoint: POST /radio/dispatch/auth
Request:
{
"nacId": "141",
"callsign": "Dispatcher-01"
}Response (Success):
{
"success": true,
"sessionId": "dispatch_1234567890_abc123",
"authToken": "changeme"
}Response (Failure):
{
"error": "Invalid NAC ID"
}Notes:
nacIdmust matchdispatchNacIdin yourconfig.luacallsignis your display name in the dispatch panel- Sessions expire after 24 hours
Step 2: Make Authenticated Requests
All protected endpoints require these headers:
Authorization: Bearer changeme
x-session-id: dispatch_1234567890_abc123Example:
curl -X GET http://192.0.2.100:7777/api/status \
-H "Authorization: Bearer changeme" \
-H "x-session-id: dispatch_1234567890_abc123"REST API vs Socket.IO
REST API Only:
- Use for triggering actions (broadcasts, alerts, tones, channel switches)
- Does NOT make you visible in the dispatch panel
- Does NOT receive real-time updates
REST API + Socket.IO:
- Use for full dispatcher presence and monitoring
- Appears in the dispatch panel as a connected dispatcher
- Receives real-time voice data, panic alerts, and user updates
- Required for voice communication features
Integration Note
Most integrations only need the REST API.
Public Endpoints
Health Check
Endpoint: GET /api/health
Response:
{
"status": "ok",
"service": "radio-dispatch",
"timestamp": "2024-01-01T00:00:00.000Z",
"version": "2.9"
}Protected Endpoints
All endpoints below require authentication headers.
Get Configuration
Endpoint: GET /radio/dispatch/config
Response:
{
"serverPort": 7777,
"authToken": "changeme",
"zones": {...},
"alerts": {...},
"logLevel": 3,
"playTransmissionEffects": true,
"analogTransmissionEffects": true
}Get System Status
Endpoint: GET /api/status
Response:
{
"userCount": 5,
"channelCount": 3,
"channels": {
"460.125": {
"frequency": 460.125,
"speakers": [1, 2, 3],
"listeners": [4, 5],
"activeTalkers": [2],
"empty": false
}
},
"users": {
"1": {
"name": "John Doe",
"nacId": "PD001",
"currentChannel": 460.125,
"listening": [460.125, 460.150],
"isTalking": false
}
},
"panicStatus": {
"460.125": {
"active": true,
"serverId": 2,
"playerName": "Officer Smith"
}
},
"activeAlerts": {},
"version": "2.9"
}Get Dispatch Status
Endpoint: GET /radio/dispatch/status
Response:
{
"channels": {
"460.125": {
"speakers": [1, 2],
"listeners": [3],
"activeTalkers": [1]
}
},
"users": {
"1": {
"name": "John Doe",
"nacId": "PD001"
}
},
"panicStatus": {},
"activeAlerts": {},
"serverTimestamp": 1234567890
}Get Available Tones
Endpoint: GET /radio/dispatch/tones
Response:
{
"tones": [
{
"name": "BEEP",
"file": "beep.wav"
},
{
"name": "PANIC",
"file": "panic.wav"
}
]
}Trigger Broadcast
Endpoint: POST /api/trigger-broadcast
Request:
{
"message": "All units respond to 123 Main St",
"frequency": 460.125,
"type": "Emergency"
}Parameters:
message(required): Broadcast message textfrequency(optional): Target frequency. If omitted, broadcasts to all userstype(optional): Alert type (e.g., "Emergency", "General")
Response:
{
"success": true,
"userCount": 5,
"users": [1, 2, 3, 4, 5]
}Play Tone on Channel
Endpoint: POST /api/play-tone
Request:
{
"frequency": 460.125,
"tone": "beep"
}Valid Tones:
beep,boop,chirp,panicalert_a,alert_b,alert_c
Response:
{
"success": true
}Trigger Alert
Endpoint: POST /radio/dispatch/alert/trigger
Request:
{
"frequency": 460.125,
"alertType": "Emergency",
"alertConfig": {
"name": "EMERGENCY",
"color": "#FF0000",
"isPersistent": true,
"tone": "PANIC"
}
}Response:
{
"success": true
}Clear Alert
Endpoint: POST /radio/dispatch/alert/clear
Request:
{
"frequency": 460.125
}Response:
{
"success": true
}One-Shot Alert
Endpoint: POST /radio/dispatch/alert/oneshot
Triggers a temporary alert that doesn't persist.
Request:
{
"frequency": 460.125,
"alertConfig": {
"name": "TEST",
"tone": "BEEP"
}
}Response:
{
"success": true
}Dispatch Broadcast
Endpoint: POST /radio/dispatch/broadcast
Request:
{
"message": "All units, be advised...",
"frequency": 460.125,
"type": "General",
"tone": "alert_a"
}Parameters:
message(required): Broadcast messagefrequency(required): Target frequencytype(optional): Alert typetone(optional): Tone to play (none,beep,alert_a, etc.)
Response:
{
"success": true
}Dispatch Tone
Endpoint: POST /radio/dispatch/tone
Request:
{
"frequency": 460.125,
"tone": "BEEP"
}Response:
{
"success": true
}Switch User Channel
Endpoint: POST /radio/dispatch/switchChannel
Move a user to a different radio channel.
Request:
{
"serverId": 1,
"frequency": 460.150,
"oldFrequency": 460.125
}Parameters:
serverId(required): In-game player server IDfrequency(required): Target frequencyoldFrequency(optional): Previous frequency for logging
Response:
{
"success": true
}Send Alert to User
Endpoint: POST /dispatch/user/alert
Send an alert message to a specific user.
Request:
{
"userId": 1,
"message": "Respond to 123 Main St",
"frequency": 460.125
}Parameters:
userId(required): In-game player server IDmessage(required): Alert message textfrequency(optional): Associated frequency
Response:
{
"success": true
}Disconnect User
Endpoint: POST /dispatch/user/disconnect
Disconnect a user from the radio system.
Request:
{
"userId": 1
}Parameters:
userId(required): In-game player server ID
Response:
{
"success": true
}Update Dispatch Callsign
Endpoint: POST /dispatch/user/update-callsign
Update the callsign for a dispatch user.
Request:
{
"callsign": "Dispatcher-02",
"userId": -5000
}Parameters:
callsign(required): New callsign/display nameuserId(optional): Dispatch user ID (negative). If omitted, updates the first dispatch user found
Response:
{
"success": true,
"callsign": "Dispatcher-02",
"userId": -5000
}Socket.IO Connection
To appear as a dispatcher in the dispatch panel, establish a Socket.IO connection.
Authentication
const io = require('socket.io-client');
const dispatchUserId = -(1000 + Math.floor(Math.random() * 10000));
const socket = io('http://YOUR_IP:7777', {
auth: {
authToken: 'changeme',
serverId: dispatchUserId // Must be negative
}
});Events to Emit
Set Session ID:
socket.emit('setDispatchSession', 'YOUR_SESSION_ID');Set User Info:
socket.emit('updateUserInfo', {
name: 'API-Bot',
nacId: 'API'
});Set Speaker Channel:
socket.emit('setSpeakerChannel', 460.125);Add Listening Channel:
socket.emit('addListeningChannel', 460.150);Remove Listening Channel:
socket.emit('removeListeningChannel', 460.150);Set Talking State:
socket.emit('setTalking', true); // or falseHeartbeat:
socket.emit('heartbeat', Date.now());Events to Listen For
Connection:
socket.on('connect', () => {
console.log('Connected to dispatch');
});Heartbeat Response:
socket.on('heartbeat_response', (serverTime) => {
console.log('Server time:', serverTime);
});Voice Data:
socket.on('voice', (data) => {
// data: { serverId, channel, voiceData, isTalking }
});Panic Alert:
socket.on('panicAlert', (data) => {
// data: { frequency, serverId, playerName }
});Server Tone:
socket.on('serverTone', (data) => {
// data: { tone, frequency }
});Transmission Effects:
socket.on('transmissionEffect', (data) => {
// data: { serverId, effectType, distance }
});Example Usage
cURL Examples
Authenticate:
curl -X POST http://192.0.2.100:7777/radio/dispatch/auth \
-H "Content-Type: application/json" \
-d '{"nacId": "141", "callsign": "API-Test"}'Get Status:
curl -X GET http://192.0.2.100:7777/api/status \
-H "Authorization: Bearer changeme" \
-H "x-session-id: dispatch_123_abc"Trigger Broadcast:
curl -X POST http://192.0.2.100:7777/api/trigger-broadcast \
-H "Authorization: Bearer changeme" \
-H "x-session-id: dispatch_123_abc" \
-H "Content-Type: application/json" \
-d '{"message": "Test alert", "frequency": 460.125, "type": "Test"}'Node.js Example
const axios = require('axios');
const io = require('socket.io-client');
const API_URL = 'http://192.0.2.100:7777';
const AUTH_TOKEN = 'changeme';
const NAC_ID = '141';
async function main() {
// Authenticate
const authResponse = await axios.post(`${API_URL}/radio/dispatch/auth`, {
nacId: NAC_ID,
callsign: 'API-Bot'
});
const { sessionId, authToken } = authResponse.data;
// Setup headers
const headers = {
'Authorization': `Bearer ${authToken}`,
'x-session-id': sessionId,
'Content-Type': 'application/json'
};
// Connect Socket.IO
const dispatchUserId = -(1000 + Math.floor(Math.random() * 10000));
const socket = io(API_URL, {
auth: { authToken: AUTH_TOKEN, serverId: dispatchUserId }
});
socket.on('connect', () => {
console.log('Connected as dispatcher');
socket.emit('setDispatchSession', sessionId);
socket.emit('updateUserInfo', { name: 'API-Bot', nacId: 'API' });
});
// Get status
const status = await axios.get(`${API_URL}/api/status`, { headers });
console.log('Users online:', status.data.userCount);
// Trigger broadcast
await axios.post(`${API_URL}/api/trigger-broadcast`, {
message: 'Test broadcast',
frequency: 460.125,
type: 'Test'
}, { headers });
}
main();Python Example
import requests
import socketio
import random
API_URL = 'http://192.0.2.100:7777'
AUTH_TOKEN = 'changeme'
NAC_ID = '141'
# Authenticate
auth_response = requests.post(f'{API_URL}/radio/dispatch/auth', json={
'nacId': NAC_ID,
'callsign': 'API-Bot'
})
session_id = auth_response.json()['sessionId']
auth_token = auth_response.json()['authToken']
# Setup headers
headers = {
'Authorization': f'Bearer {auth_token}',
'x-session-id': session_id,
'Content-Type': 'application/json'
}
# Connect Socket.IO
dispatch_user_id = -(1000 + random.randint(0, 10000))
sio = socketio.Client()
@sio.on('connect')
def on_connect():
print('Connected as dispatcher')
sio.emit('setDispatchSession', session_id)
sio.emit('updateUserInfo', {'name': 'API-Bot', 'nacId': 'API'})
sio.connect(API_URL, auth={'authToken': AUTH_TOKEN, 'serverId': dispatch_user_id})
# Get status
status = requests.get(f'{API_URL}/api/status', headers=headers)
print(f"Users online: {status.json()['userCount']}")
# Trigger broadcast
requests.post(f'{API_URL}/api/trigger-broadcast', headers=headers, json={
'message': 'Test broadcast',
'frequency': 460.125,
'type': 'Test'
})Error Responses
401 Unauthorized:
{
"error": "Authentication required"
}400 Bad Request:
{
"error": "Message and frequency required"
}404 Not Found:
{
"error": "Channel not found"
}500 Internal Server Error:
{
"error": "Error message here"
}API Notes
Important Information
- Replace
192.0.2.100:7777with your server's actual IP address and port - Replace
changemeand141with your actualauthTokenanddispatchNacIdvalues - All frequencies should be decimal numbers (e.g.,
460.125) - Server IDs for dispatchers must be negative (e.g.,
-5000) - Player server IDs are positive integers (e.g.,
1,2,3) - Session IDs expire after 24 hours
- CORS is enabled for all origins by default
- The API uses HTTP; consider using a reverse proxy with HTTPS for production