Skip to main content

Remote Control API

info

This page documents the technical details of the vacs remote control WebSocket API. If you're just looking to use vacs from a browser or different device, see Remote Control instead.

vacs exposes a WebSocket-based remote control API for programmatic interaction by external clients. The built-in browser frontend communicates through the same API - there is no separate internal protocol.

This reference covers the wire format, available commands, subscribable events, and connection lifecycle. It is intended for developers building custom integrations, alternative frontends, or automation tooling against a running vacs instance.

note

While this API can be consumed by any external client, it is heavily geared towards the Tauri/Preact desktop frontend and mirrors the internal interactions used there. Some commands, events, and payload structures may reflect frontend-specific concerns that are not immediately relevant to third-party integrations.

As of now, there are no guarantees of backward compatibility for this API. If you are building a third-party integration, please reach out to the developers to discuss your use case and ensure it can be supported in future releases.

Connectionโ€‹

Endpointโ€‹

ws://<host>:<port>/ws

The default port is 9600. The remote control server must be explicitly enabled in the vacs configuration - see Remote Control - Enabling remote control.

Transportโ€‹

  • Standard WebSocket (RFC 6455)
  • All application messages are JSON text frames - binary frames are not used
  • WebSocket protocol-level Ping/Pong frames are supported - the server responds to any WebSocket Ping frame with a matching Pong frame
  • No sub-protocols are required

Authenticationโ€‹

The WebSocket endpoint does not perform any authentication or authorization. Access control is the responsibility of the network environment.

Security

Do not expose the remote server on untrusted networks. The connection is unencrypted (no TLS).
If you require additional security or must expose the server on a public network, forward the port through a reverse proxy that provides TLS termination and authentication.


Message Formatโ€‹

Every message is a JSON object with a type field that identifies the message kind.

Client โ†’ Server:

TypePurpose
invokeExecute a command on the desktop host
subscribeRegister for a named event stream
unsubscribeDeregister from a named event stream
pingConnection keepalive (expects pong)

Server โ†’ Client:

TypePurpose
responseResult of a preceding invoke
eventForwarded event matching an active subscription
pongKeepalive acknowledgement
Field naming convention

The wire protocol uses snake_case for command names (audio_get_volumes, signaling_start_call, etc.). However, command return values and event payloads use camelCase for their fields (e.g. callId, positionId, clientPageSettings). This is intentional - the payload schema matches the format used by the default Preact frontend.


Client Messagesโ€‹

invokeโ€‹

Dispatches a command to the desktop application. The server responds with a response message carrying the same id.

{
"type": "invoke",
"id": "1",
"cmd": "audio_get_volumes",
"args": {}
}
FieldTypeRequiredDescription
idstringyesClient-assigned opaque identifier, echoed in the corresponding response. Used to correlate requests and responses when multiple commands are in flight concurrently.
cmdstringyesCommand name in snake_case. See Commands.
argsobjectyesJSON object containing command arguments. Must be {} for commands that accept no arguments.

Timeout: Commands that do not resolve within 10 seconds produce an error response with "type": "urn:vacs:error:remote:timeout".

subscribeโ€‹

Registers a subscription for the specified event. Once subscribed, the server forwards all matching event messages to this connection.

{
"type": "subscribe",
"event": "signaling:client-list"
}
FieldTypeRequiredDescription
eventstringyesEvent name to subscribe to. See Events.

No response is sent. Unrecognized event names are silently ignored.

unsubscribeโ€‹

Removes an active event subscription.

{
"type": "unsubscribe",
"event": "signaling:client-list"
}

pingโ€‹

Keepalive heartbeat. The server responds with a pong message.

{
"type": "ping"
}

Server Messagesโ€‹

responseโ€‹

Returned for every invoke message. Exactly one response is produced per invoke.

Success:

{
"type": "response",
"id": "1",
"ok": true,
"data": { "input": 80, "output": 100 }
}

Error:

{
"type": "response",
"id": "1",
"ok": false,
"error": {
"type": "urn:vacs:error:remote:desktop-only",
"title": "Desktop only",
"detail": "This operation is only available on the desktop application",
"isNonCritical": true
}
}
FieldTypeDescription
idstringEchoed from the originating invoke message.
okbooleantrue if the command completed successfully, false otherwise.
dataany or absentPresent when ok is true. Contains the command's return value. null for commands with no return value. Absent when ok is false.
errorProblemDetails or absentPresent when ok is false. RFC 7807-compatible error object. Absent when ok is true.

ProblemDetailsโ€‹

Error responses use RFC 7807 Problem Details. The type URI uniquely identifies the error category.

FieldTypeDescription
typeProblemType (URI)URI identifying the problem type.
titlestringShort human-readable summary.
detailstringLonger human-readable explanation.
isNonCriticalbooleantrue for expected/recoverable errors, false for unexpected failures.
timeoutMsnumber | absentAuto-dismiss timeout in milliseconds. Absent when not applicable.

eventโ€‹

Emitted when the desktop application produces an event matching an active subscription.

{
"type": "event",
"name": "signaling:client-list",
"payload": [{ "id": "1234567", "displayName": "LOVV_CTR", "frequency": "133.800", "positionId": "LOVV_CTR" }]
}
FieldTypeDescription
namestringEvent identifier matching the subscribed event name.
payloadanyEvent-specific payload. Schema depends on the event type.

pongโ€‹

Keepalive acknowledgement in response to a ping. Contains no additional fields.

{
"type": "pong"
}
WebSocket-level Ping/Pong

In addition to the application-level ping/pong messages above, the server also responds to WebSocket protocol-level Ping frames (RFC 6455 ยง5.5.2) with a matching Pong frame. Most WebSocket client libraries handle this transparently. Either mechanism can be used for keepalive.


Commandsโ€‹

Commands are grouped by domain. All command names use snake_case.

Commands that accept arguments expect them as a JSON object in the args field of the invoke message. Commands that take no arguments require args: {}.

note

Some commands are marked as desktop only1 and are unavailable over the remote API.

Applicationโ€‹

CommandArgsReturnsDescription
app_frontend_ready-nullSignal that the frontend has loaded. No-op over remote.
app_open_folder 1--Open a native folder dialog.
app_check_for_update-UpdateInfoCheck for application updates.
app_quit 1--Quit the application.
app_update 1--Apply a pending update.
app_platform_capabilities-CapabilitiesGet platform capability flags.
app_set_always_on_top 1--Set always-on-top window state.
app_set_fullscreen 1--Toggle fullscreen mode.
app_reset_window_size 1--Reset window to default size.
app_get_call_config-CallConfigGet the current call configuration.
app_set_call_configcallConfig: CallConfignullUpdate call configuration.
app_load_test_profilepath: string?string | nullLoad or reload a test profile. Over remote, path must be provided - null opens a file dialog (desktop only).
app_unload_test_profile-nullUnload the active test profile.
app_get_client_page_settings-ClientPageSettingsGet client page settings.
app_set_selected_client_page_configconfigName: string?nullSet the active client page config.
app_load_extra_client_page_config 1--Load extra client page config from a file.

Audioโ€‹

CommandArgsReturnsDescription
audio_get_hosts-AudioHostsList available audio backends.
audio_set_hosthostName: stringnullSwitch audio backend.
audio_get_devicesdeviceType: DeviceTypeAudioDevicesList audio devices for the given type.
audio_set_devicedeviceType: DeviceType, deviceName: stringAudioDevicesSet the active audio device. Returns updated device list.
audio_get_volumes-AudioVolumesGet current volume levels.
audio_set_volumevolumeType: VolumeType, volume: numbernullSet a volume level.
audio_play_ui_click-nullPlay the UI click sound.
audio_start_input_level_meter-nullStart input level monitoring. Subscribe to audio:input-level to receive updates.
audio_stop_input_level_meter-nullStop input level monitoring.
audio_set_radio_prioprio: booleannullSet the radio priority flag.

Authenticationโ€‹

CommandArgsReturnsDescription
auth_open_oauth_url 1--Start the VATSIM OAuth flow. Opens the authorization URL on the desktop host.
auth_check_session-nullCheck the current session validity.
auth_logout-nullLog out the current user.

Keybindsโ€‹

CommandArgsReturnsDescription
keybinds_get_transmit_config-TransmitConfigGet transmit configuration (mode, PTT keys, etc.).
keybinds_set_transmit_configtransmitConfig: TransmitConfignullUpdate transmit configuration.
keybinds_get_keybinds_config-KeybindsConfigGet keybind configuration.
keybinds_set_bindingcode: string?, keybind: KeybindnullSet a specific keybind.
keybinds_get_radio_config-RadioConfigGet radio integration configuration.
keybinds_set_radio_configradioConfig: RadioConfignullUpdate radio integration configuration.
keybinds_get_radio_state-RadioStateGet current radio state.
keybinds_get_external_bindingkeybind: Keybindstring | nullGet the external (system-level) binding for a keybind.
keybinds_open_system_shortcuts_settings 1--Open the OS shortcut settings.
keybinds_reconnect_radio-nullReconnect the radio integration.

Remoteโ€‹

CommandArgsReturnsDescription
remote_broadcast_store_syncstore: string, state: anynullBroadcast a state sync event to all connected remote clients.
remote_get_session_state-SessionStateSnapshotGet a full snapshot of the current session.

Signalingโ€‹

CommandArgsReturnsDescription
signaling_connectpositionId: stringnullConnect to the signaling server.
signaling_disconnect-nullDisconnect from the signaling server.
signaling_terminate-nullTerminate the signaling session.
signaling_start_calltarget: string, source: string, prio: booleanstringStart a call. Returns the call ID (UUID).
signaling_accept_callcallId: stringnullAccept an incoming call.
signaling_end_callcallId: stringnullEnd an active or pending call.
signaling_get_ignored_clients-string[]Get the ignore list. Returns an array of CIDs.
signaling_add_ignored_clientclientId: stringbooleanAdd a CID to the ignore list. Returns whether the CID was newly added.
signaling_remove_ignored_clientclientId: stringbooleanRemove a CID from the ignore list. Returns whether the CID was present.

Session State Snapshotโ€‹

The remote_get_session_state command returns a complete snapshot of the current application state. This is the recommended mechanism for initializing a newly connected client. See SessionStateSnapshot in the Type Reference for the full schema and a JSON example.


Eventsโ€‹

Subscribe to events to receive real-time updates. Event names use a domain:name format with kebab-case.

Audio Eventsโ€‹

EventPayloadDescription
audio:implicit-radio-priobooleanRadio priority was implicitly changed (e.g. by an incoming priority call).
audio:input-levelnumberInput audio level sample (0.0โ€“1.0). Emitted at a regular interval while the input level meter is active.
audio:radio-priobooleanRadio priority state changed.
audio:stop-input-level-meternullThe input level meter was stopped.

Authentication Eventsโ€‹

EventPayloadDescription
auth:authenticatedstringThe user successfully authenticated. Payload is the VATSIM CID.
auth:errornullAn authentication error occurred.
auth:unauthenticatednullThe user was logged out or the session expired.

Signaling Eventsโ€‹

EventPayloadDescription
signaling:accept-incoming-callstringAn incoming call was accepted. Payload is the CallId.
signaling:add-incoming-to-call-listIncomingCallListEntryA new incoming call was added to the pending list.
signaling:ambiguous-positionstring[]The selected position matched multiple entries. Payload is an array of position IDs.
signaling:call-endstringA call ended. Payload is the CallId.
signaling:call-inviteCallInviteA new call invitation was received.
signaling:call-rejectstringA call was rejected by the remote party. Payload is the CallId.
signaling:client-connectedClientInfoA client connected to the signaling server.
signaling:client-disconnectedstringA client disconnected from the signaling server. Payload is the CID.
signaling:client-listClientInfo[]The full client list was updated (replaces previous list).
signaling:client-not-foundstringA client lookup failed. Payload is the CID.
signaling:client-page-configClientPageSettingsThe client page configuration was updated.
signaling:connectedSessionInfoSuccessfully connected to the signaling server.
signaling:disconnectednullDisconnected from the signaling server.
signaling:force-call-endstringA call was forcefully terminated (e.g. by the server). Payload is the CallId.
signaling:outgoing-call-acceptedCallAcceptAn outgoing call was accepted by the remote party.
signaling:reconnectingnullThe signaling connection is being re-established.
signaling:station-changesStationChange[]One or more stations changed.
signaling:station-listStationInfo[]The full station list was updated (replaces previous list).
signaling:test-profileobjectA test profile was loaded or unloaded.
signaling:update-call-listCallListUpdateThe call list was updated.

WebRTC Eventsโ€‹

EventPayloadDescription
webrtc:call-connectedstringA voice call was established (media flowing). Payload is the CallId.
webrtc:call-disconnectedstringA voice call was disconnected. Payload is the CallId.
webrtc:call-errorCallErrorA voice call encountered an error.

Other Eventsโ€‹

EventPayloadDescription
errorFrontendErrorA general application error.
radio:stateRadioStateRadio integration state changed.
store:syncobjectA store state synchronization broadcast. Contains { "store": <string>, "state": <any> }.
update:progressnumberApplication update download progress (0โ€“100).

Type Referenceโ€‹

This section documents all complex types used in command return values and event payloads. Field names are serialized in camelCase unless noted otherwise.

Identifiersโ€‹

Several identifier types appear throughout the API. All are serialized as plain JSON strings.

TypeFormatDescription
ClientIdnumeric stringVATSIM CID (e.g. "1234567").
PositionIdstringPosition callsign (e.g. "LOVV_CTR").
StationIdstringStation identifier (e.g. "LOVV_CTR").
CallIdUUID stringUnique call identifier (UUIDv7, e.g. "019cc8de-50f0-7624-a89c-61ba0b5cb784").

ProblemTypeโ€‹

Well-known problem type URIs used in ProblemDetails error responses.

URITitleDescription
urn:vacs:error:remote:desktop-onlyDesktop onlyThe command is only available on the desktop application.
urn:vacs:error:remote:invalid-argumentInvalid argumentOne or more command arguments were invalid or missing.
urn:vacs:error:remote:timeoutTimeoutThe command did not complete within the time limit.
urn:vacs:error:remote:application(varies)An application-level error originating from the backend.

SessionStateSnapshotโ€‹

Returned by remote_get_session_state. Provides a complete snapshot of the current application state for bootstrapping a newly connected client.

{
"connectionState": "disconnected",
"sessionInfo": null,
"stations": [],
"clients": [],
"clientId": null,
"callConfig": {
"highlightIncomingCallTarget": true,
"enablePriorityCalls": true,
"enableCallStartSound": true,
"enableCallEndSound": true
},
"clientPageSettings": {
"selected": null,
"configs": {}
},
"capabilities": {
"alwaysOnTop": true,
"keybindListener": true,
"keybindEmitter": false,
"platform": "LinuxWayland"
},
"incomingCalls": [],
"outgoingCall": null
}
FieldTypeDescription
connectionStateConnectionStateSignaling connection state.
sessionInfoSessionInfo | nullCurrent signaling session metadata.
stationsStationInfo[]Available stations in the current session.
clientsClientInfo[]Other clients visible in the current session.
clientIdstring | nullThe authenticated user's VATSIM CID.
callConfigCallConfigActive call configuration.
clientPageSettingsClientPageSettingsActive client page layout/settings.
capabilitiesCapabilitiesPlatform capabilities of the desktop host.
incomingCallsCallInvite[]Pending incoming call invitations.
outgoingCallCallInvite | nullThe pending outgoing call, if any.

Capabilitiesโ€‹

Platform capability flags returned by app_platform_capabilities and included in the session state snapshot.

{
"alwaysOnTop": true,
"keybindListener": true,
"keybindEmitter": false,
"platform": "LinuxWayland"
}
FieldTypeDescription
alwaysOnTopbooleanWhether the platform supports always-on-top window mode.
keybindListenerbooleanWhether the platform supports global keybind listening.
keybindEmitterbooleanWhether the platform supports emitting key events.
platformPlatformThe host platform identifier.

Platformโ€‹

"Unknown" | "Windows" | "MacOs" | "LinuxX11" | "LinuxWayland" | "LinuxUnknown"

UpdateInfoโ€‹

Returned by app_check_for_update.

{
"currentVersion": "2.0.0",
"newVersion": "2.1.0",
"required": false
}
FieldTypeDescription
currentVersionstringCurrently installed application version.
newVersionstring | absentAvailable update version. Absent when no update is available.
requiredbooleanWhether the update is mandatory.

CallConfigโ€‹

Returned by app_get_call_config. Accepted by app_set_call_config.

{
"highlightIncomingCallTarget": true,
"enablePriorityCalls": true,
"enableCallStartSound": true,
"enableCallEndSound": true
}
FieldTypeDescription
highlightIncomingCallTargetbooleanHighlight the caller in the client list on incoming call.
enablePriorityCallsbooleanAllow sending and receiving priority calls.
enableCallStartSoundbooleanPlay a sound when a call connects.
enableCallEndSoundbooleanPlay a sound when a call ends.

ClientPageSettingsโ€‹

Returned by app_get_client_page_settings and the signaling:client-page-config event.

{
"selected": "default",
"configs": {
"default": {
"include": [],
"exclude": [],
"priority": [],
"frequencies": "ShowAll",
"grouping": "FirAndIcao"
}
}
}
FieldTypeDescription
selectedstring | nullName of the active client page configuration.
configsobjectMap of configuration name โ†’ ClientPageConfig.

ClientPageConfigโ€‹

FieldTypeDescription
includestring[]Position ID patterns to include in the client list.
excludestring[]Position ID patterns to exclude from the client list.
prioritystring[]Position ID patterns to prioritise in the client list.
frequenciesFrequencyDisplayModeHow to display frequencies.
groupingClientGroupModeHow to group clients.

FrequencyDisplayModeโ€‹

"HideAll" | "ShowAll"

ClientGroupModeโ€‹

"None" | "Fir" | "Icao" | "FirAndIcao"

AudioHostsโ€‹

Returned by audio_get_hosts.

{
"selected": "ALSA",
"all": ["ALSA", "JACK"]
}
FieldTypeDescription
selectedstringCurrently active audio backend.
allstring[]All available audio backends on the host.

AudioDevicesโ€‹

Returned by audio_get_devices and audio_set_device.

{
"preferred": "Headset (USB Audio)",
"picked": "Headset (USB Audio)",
"default": "Built-in Audio",
"all": ["Built-in Audio", "Headset (USB Audio)"]
}
FieldTypeDescription
preferredstringUser-preferred device name (from config).
pickedstringActually selected device (may differ from preferred if unavailable).
defaultstringSystem default device.
allstring[]All available devices of the requested type.

AudioVolumesโ€‹

Returned by audio_get_volumes.

{
"input": 80,
"output": 100,
"click": 50,
"chime": 70
}
FieldTypeDescription
inputnumberMicrophone input volume.
outputnumberAudio output volume.
clicknumberUI click sound volume.
chimenumberNotification chime volume.

VolumeTypeโ€‹

Used as the volumeType argument for audio_set_volume:

"input" | "output" | "click" | "chime"

DeviceTypeโ€‹

Used as the deviceType argument for audio_get_devices and audio_set_device:

"Input" | "Output"

TransmitConfigโ€‹

Returned by keybinds_get_transmit_config. Accepted by keybinds_set_transmit_config.

{
"mode": "PushToTalk",
"pushToTalk": "Space",
"pushToMute": null,
"radioPushToTalk": null
}
FieldTypeDescription
modeTransmitModeActive transmit mode.
pushToTalkstring | nullKey code for push-to-talk.
pushToMutestring | nullKey code for push-to-mute.
radioPushToTalkstring | nullKey code for radio push-to-talk.

TransmitModeโ€‹

"VoiceActivation" | "PushToTalk" | "PushToMute" | "RadioIntegration"

KeybindsConfigโ€‹

Returned by keybinds_get_keybinds_config.

{
"acceptCall": "KeyA",
"endCall": "KeyE",
"toggleRadioPrio": null
}
FieldTypeDescription
acceptCallstring | nullKey code for accepting an incoming call.
endCallstring | nullKey code for ending the active call.
toggleRadioPriostring | nullKey code for toggling radio priority.

Keybindโ€‹

Used as the keybind argument for keybinds_set_binding and keybinds_get_external_binding:

"PushToTalk" | "PushToMute" | "RadioIntegration" | "AcceptCall" | "EndCall" | "ToggleRadioPrio"

RadioConfigโ€‹

Returned by keybinds_get_radio_config. Accepted by keybinds_set_radio_config.

{
"integration": "AudioForVatsim",
"audioForVatsim": {
"emit": "F1"
},
"trackAudio": {
"endpoint": "ws://localhost:49080"
}
}
FieldTypeDescription
integrationRadioIntegrationActive radio integration backend.
audioForVatsimobject | nullAudioForVATSIM-specific configuration. Contains emit (key code, string | null).
trackAudioobject | nullTrackAudio-specific configuration. Contains endpoint (WebSocket URL, string | null).

RadioIntegrationโ€‹

"AudioForVatsim" | "TrackAudio"

RadioStateโ€‹

Returned by keybinds_get_radio_state and emitted with the radio:state event.

"NotConfigured" | "Disconnected" | "Connected" | "VoiceConnected" | "RxIdle" | "RxActive" | "TxActive" | "Error"
ValueDescription
NotConfiguredNo radio integration is configured.
DisconnectedConfigured but not connected to the radio backend.
ConnectedConnected to the radio backend, no voice session.
VoiceConnectedVoice session established.
RxIdleReceiving capable, no active reception.
RxActiveActively receiving audio.
TxActiveActively transmitting audio.
ErrorThe radio integration encountered an error.

ConnectionStateโ€‹

Used in the session state snapshot to indicate signaling connection state.

"disconnected" | "connecting" | "connected" | "test"

ClientInfoโ€‹

Represents a connected client visible in the session.

{
"id": "1234567",
"displayName": "LOVV_CTR",
"frequency": "133.800",
"positionId": "LOVV_CTR"
}
FieldTypeDescription
idstringVATSIM CID.
displayNamestringDisplay name (typically the callsign).
frequencystringActive frequency.
positionIdstring | absentPosition ID, if available. Absent when not applicable.

SessionInfoโ€‹

Emitted with the signaling:connected event and included in the session state snapshot.

{
"client": {
"id": "1234567",
"displayName": "LOVV_CTR",
"frequency": "133.800",
"positionId": "LOVV_CTR"
},
"profile": {
"type": "Unchanged"
}
}
FieldTypeDescription
clientClientInfoThe authenticated user's client entry.
profileobjectProfile state. { "type": "Unchanged" } when the profile was not modified, or { "type": "Changed", "activeProfile": ... } when it was.

StationInfoโ€‹

Represents a station in the current session.

{
"id": "LOVV_CTR",
"own": true
}
FieldTypeDescription
idstringStation identifier.
ownbooleanWhether this station belongs to the authenticated user's position.

StationChangeโ€‹

Contained in the signaling:station-changes event payload. Each entry is an externally-tagged enum with one of three variants:

Online:

{ "Online": { "stationId": "LOVV_CTR", "positionId": "LOVV_CTR" } }

Handoff:

{ "Handoff": { "stationId": "LOVV_CTR", "fromPositionId": "LOVV_N1", "toPositionId": "LOVV_N2" } }

Offline:

{ "Offline": { "stationId": "LOVV_CTR" } }

CallInviteโ€‹

Represents an incoming or outgoing call invitation.

{
"callId": "01916f6a-7b3c-7d4e-8f1a-2b3c4d5e6f70",
"source": {
"clientId": "7654321",
"positionId": "LOWW_APP",
"stationId": "LOWW_APP"
},
"target": { "Client": "1234567" },
"prio": false
}
FieldTypeDescription
callIdstringUnique call identifier (UUID).
sourceCallSourceThe originator of the call.
targetCallTargetThe intended recipient of the call.
priobooleanWhether this is a priority call.

CallSourceโ€‹

{
"clientId": "7654321",
"positionId": "LOWW_APP",
"stationId": "LOWW_APP"
}
FieldTypeDescription
clientIdstringVATSIM CID of the caller.
positionIdstring | absentPosition ID of the caller. Absent when unknown.
stationIdstring | absentStation ID of the caller. Absent when unknown.

CallTargetโ€‹

An externally-tagged enum identifying the call recipient. Exactly one variant is present:

{ "Client": "1234567" }
{ "Position": "LOWW_APP" }
{ "Station": "LOWW_APP" }

CallAcceptโ€‹

Emitted with the signaling:outgoing-call-accepted event.

{
"callId": "01916f6a-7b3c-7d4e-8f1a-2b3c4d5e6f70",
"acceptingClientId": "1234567"
}
FieldTypeDescription
callIdstringThe accepted call's identifier.
acceptingClientIdstringVATSIM CID of the client that accepted the call.

CallErrorโ€‹

Emitted with the webrtc:call-error event.

{
"callId": "01916f6a-7b3c-7d4e-8f1a-2b3c4d5e6f70",
"reason": "Local connection failure"
}
FieldTypeDescription
callIdstringThe call that experienced the error.
reasonstringHuman-readable error description.

FrontendErrorโ€‹

Emitted with the error event.

{
"title": "Connection failed",
"detail": "Unable to reach the signaling server",
"isNonCritical": true
}
FieldTypeDescription
titlestringError category identifier.
detailstringHuman-readable error description.
isNonCriticalbooleantrue for expected/recoverable errors, false for unexpected failures.
timeoutMsnumber | absentAuto-dismiss timeout in milliseconds. Absent when not applicable.

IncomingCallListEntryโ€‹

Emitted with the signaling:add-incoming-to-call-list event.

{
"callId": "01916f6a-7b3c-7d4e-8f1a-2b3c4d5e6f70",
"source": {
"clientId": "7654321",
"positionId": "LOWW_APP"
}
}
FieldTypeDescription
callIdstringThe incoming call's identifier.
sourceCallSourceThe originator of the call.

CallListUpdateโ€‹

Emitted with the signaling:update-call-list event.

{
"callId": "01916f6a-7b3c-7d4e-8f1a-2b3c4d5e6f70",
"clientId": "1234567"
}
FieldTypeDescription
callIdstringThe affected call's identifier.
clientIdstring | nullThe client involved in the update, or null.

Example: Client Session Lifecycleโ€‹

The following sequence illustrates a typical client lifecycle, from connection establishment through to placing a call.

1. Subscribe to eventsโ€‹

Immediately after the WebSocket connection is established, register subscriptions for the required event streams:

โ†’ { "type": "subscribe", "event": "auth:authenticated" }
โ†’ { "type": "subscribe", "event": "auth:unauthenticated" }
โ†’ { "type": "subscribe", "event": "signaling:connected" }
โ†’ { "type": "subscribe", "event": "signaling:disconnected" }
โ†’ { "type": "subscribe", "event": "signaling:client-list" }
โ†’ { "type": "subscribe", "event": "signaling:station-list" }
โ†’ { "type": "subscribe", "event": "signaling:call-invite" }
โ†’ { "type": "subscribe", "event": "signaling:call-end" }
โ†’ { "type": "subscribe", "event": "webrtc:call-connected" }
โ†’ { "type": "subscribe", "event": "webrtc:call-disconnected" }
โ†’ { "type": "subscribe", "event": "store:sync" }
โ†’ { "type": "subscribe", "event": "error" }

2. Bootstrap stateโ€‹

Signal client readiness and retrieve the current application state:

โ†’ { "type": "invoke", "id": "1", "cmd": "app_frontend_ready", "args": {} }
โ† { "type": "response", "id": "1", "ok": true, "data": null }

โ†’ { "type": "invoke", "id": "2", "cmd": "remote_get_session_state", "args": {} }
โ† {
"type": "response",
"id": "2",
"ok": true,
"data": {
"connectionState": "disconnected",
"sessionInfo": null,
"stations": [],
"clients": [],
"clientId": null,
"callConfig": { ... },
"clientPageSettings": { ... },
"capabilities": { ... },
"incomingCalls": [],
"outgoingCall": null
}
}

See SessionStateSnapshot for the full schema.

3. Authenticateโ€‹

Check whether a valid session already exists:

โ†’ { "type": "invoke", "id": "3", "cmd": "auth_check_session", "args": {} }
โ† { "type": "response", "id": "3", "ok": true, "data": { ... } }

If no auth:authenticated event follows, the user must authenticate on the desktop host first (auth_open_oauth_url is desktop-only). Once the OAuth flow completes on the desktop, the server emits:

โ† { "type": "event", "name": "auth:authenticated", "payload": "1234567" }

4. Connect and callโ€‹

โ†’ { "type": "invoke", "id": "4", "cmd": "signaling_connect", "args": { "positionId": "LOVV_CTR" } }
โ† { "type": "response", "id": "4", "ok": true, "data": null }

โ† { "type": "event", "name": "signaling:connected", "payload": { ... } }
โ† { "type": "event", "name": "signaling:station-list", "payload": [ ... ] }
โ† { "type": "event", "name": "signaling:client-list", "payload": [ ... ] }

Initiate a call to another client:

โ†’ { "type": "invoke", "id": "5", "cmd": "signaling_start_call", "args": { "target": "1234569", "source": "LOVV_N1", "prio": false } }
โ† { "type": "response", "id": "5", "ok": true, "data": "019cc8de-50f0-7624-a89c-61ba0b5cb784" }
โ† { "type": "event", "name": "webrtc:call-connected", "payload": "019cc8de-50f0-7624-a89c-61ba0b5cb784" }

5. Keepaliveโ€‹

Maintain the connection with periodic keepalive pings. Either application-level or WebSocket protocol-level pings may be used:

โ†’ { "type": "ping" }
โ† { "type": "pong" }

Alternatively, send a WebSocket Ping frame - the server replies with a Pong frame carrying the same payload. Most WebSocket libraries send these automatically.


Implementation Notesโ€‹

  • Message ordering: The server imposes no ordering constraints on subscribe and invoke messages. Clients may interleave them freely.
  • Concurrency: Multiple invoke requests may be in flight simultaneously. Clients must use distinct id values to correlate responses.
  • Event buffering: The server maintains an internal per-connection event buffer of 256 messages. If a client cannot consume events at the rate they are produced, older events are dropped and a warning is logged server-side.
  • Desktop-only commands: Commands marked as desktop-only are unconditionally rejected over the remote API. Clients should inspect remote_get_session_state โ†’ capabilities to determine platform support before invoking platform-dependent commands.
  • Field naming: Command names use snake_case (audio_get_volumes, signaling_start_call), while command return values and event payloads use camelCase (callId, positionId). This is intentional - the payload schema matches the format used by the default Preact frontend.
  • Static assets: The HTTP server that hosts the WebSocket endpoint also serves the vacs SPA at the root path (/). Unresolved paths fall back to index.html to support client-side routing.

Footnotesโ€‹

  1. Desktop only - these commands are unavailable over the remote API. Unconditionally rejected with a ProblemDetails error response with "type": "urn:vacs:error:remote:desktop-only". โ†ฉ โ†ฉ2 โ†ฉ3 โ†ฉ4 โ†ฉ5 โ†ฉ6 โ†ฉ7 โ†ฉ8 โ†ฉ9 โ†ฉ10