VR Sync API: Documentation

This page is designed to be a reference for the VR Sync API. All protocol messages and properties are described, enabling you to create your own integrations. If you haven’t already, read our quickstart guides to get an understanding of the basic concepts.


Server to Client message types

Command

This is the main VR Sync command, that contains the current playback state the device should be in. The message gets sent on admin commands, when connecting to the server and when requested using the CommandUpdate message.

{
    "sender": "Server",
    "type": "Command",
    "protocolVersion": 2,
    // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
    "sentUnixTimestampMs": 1721807772071,
    // Whether the items in the playlist should loop
    "loop": false,
    // If true, the timer (currentTime) is not running
    "paused": false,
    // The amount of milliseconds since the playback started
    "currentTime": 0,
    // The Unix UTC timestamp of the sender's clock when it received the command from an Admin client, in milliseconds
    "serverReceived": 1721807772071,
    // The Unix UTC timestamp of the sender's clock when it received the pause command from an Admin client, in milliseconds. This is only included if "paused" is true.
    "pauseTime": 1721807772071,
    // An array containing media items to play in sequence, or empty for a stop command
    "playlist": [
        {
            // LocalVideo, CloudVideo, LocalImage, CloudImage
            "type": "LocalVideo",
            // The ID of the media file
            "identifier": "2",
            // The duration of the media file
            "durationinMs": 240000,
            // The amount of milliseconds to wait before starting playback (to compensate buffering/loading)
            "playDelayMs": 4000
        }
    ]
}


Error

Errors can be sent when the server rejects a message and/or connection.

{
    "sender": "Server",
    "type": "Error",
    "protocolVersion": 2,
    // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
    "sentUnixTimestampMs": 1721807772071,
    // An error type string ENUM
    "errorType": "LIMIT_EXCEEDED",
    // A friendly message to show to the user
    "errorMessage": "The maximum amount of users has been reached"
}


Ping

The ping message will be returned from the server directly after receival from the client. The ID can be used to measure the roundtrip time.

 {
    "sender": "Server",
    "type": "Ping",
    "protocolVersion": 2,
    // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
    "sentUnixTimestampMs": 1721807772071,
    // The maximum amount of devices that can connect to the server
    "userLimit": 12,
    // The ping ID from the client that initiated this ping response from the server
    "id": 23
}


Text

When the admin sends a text command, the client should immediately show this text toast.

 {
    "sender": "Server",
    "type": "Text",
    "protocolVersion": 2,
    // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
    "sentUnixTimestampMs": 1721807772071,
    // The message to show to the user
    "text": "Please take off your headset",
    // The amount of ms to show the message
    "lengthInMs": 10000
}


Name change

When the admin has renamed your device, you will receive this message. Your UID should stay the same. But on further device status updates it is recommended to use this new name.

{
    "sender": "Server",
    "type": "NameChange",
    "protocolVersion": 2,
    // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
    "sentUnixTimestampMs": 1721807772071,
    // The new name for the device
    "name": "New device name"
}


Calibrate

You can use this message to calibrate the viewpoint of all connected devices to their current orientation:

{
    "sender": "Server",
    "type": "Calibrate",
    "protocolVersion": 2,
    // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
    "sentUnixTimestampMs": 1721807772071
}


Client to Server message types


Media update

A client can send its current media at all times to the server. The server will use this to cache this media list for display on the webremote. The device with the latest version of the cloud media timestamp will be the main cloud media. Local media files can be different for each device, but can all be mapped to the same identifier, depending on the use case. Therefore local media offers the most flexibility in custom solutions.

{
    "sender": "Client",
    "type": "MediaUpdate",
    "protocolVersion": 2,
    // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
    "sentUnixTimestampMs": 1721807772071,
    // This value should be a lastupdated value that signifies the last revision date of this media. All devices should have the same date in order to be considered identically preloaded
    "updatedAt": "2024-05-03T16:02:44+02:00",
    "media": [
        {
            // LocalVideo, LocalImage, CloudVideo or CloudImage
            "type": "LocalVideo",
            "durationInMs": 42684,
            "width": 1920,
            "height": 1080,
            "identifier": "1",
            "mediaPath": "C:/VRSYNC/1_RideFilm.mp4",
            "mediaName": "RideFilm.mp4",
            "hasSpatialAudio": false,
            "hasSubtitles": false,
            // 0 = None, 1 = TopBottom, 2 = LeftRight
            "stereoPacking": 0,
            "is2D": false,
            "isEquiAngular": false // Equiangular is not true, so equirectangular is being used
        },
        {
            "type": "CloudVideo",
            "durationInMs": 101248,
            "width": 3840,
            "height": 2160,
            "fileUrl": "https://url.com/ee3b8f212c8d.mp4",
            "identifier": "7145",
            "mediaPath": "C:/VRSYNC/Cloud/87145.mp4",
            "mediaName": "1_presentation_v15_H264_MASTER_360_TB.mp4",
            "hasSpatialAudio": true,
            "spatialAudioPath": "C:/VRSYNC/Cloud/87145.tbe",
            "hasSubtitles": true,
            "subtitlePath": "CC:/VRSYNC/Cloud/87145.srt",
            "stereoPacking": 1,
            "is2D": false,
            "isEquiAngular": false
        },
        {
            "type": "CloudImage",
            "durationInMs": 0,
            "fileUrl": "https://url.com/bef90a478c14.png",
            "identifier": "7336",
            "mediaPath": "C:/VRSYNC/Cloud/87336.png",
            "mediaName": "groen.png",
            "hasSpatialAudio": false,
            "hasSubtitles": false,
            "stereoPacking": 0,
            "is2D": false,
            "isEquiAngular": false
        },
        {
            "type": "CloudVideo",
            "durationInMs": 206571,
            "width": 4096,
            "height": 2048,
            "fileUrl": "https://url.com/c923401c226a.mp4",
            "identifier": "12732",
            "mediaPath": "C:/VRSYNC/Cloud/82732.mp4",
            "mediaName": "14_(left_right_stereo)Titan_LR.mp4",
            "hasSpatialAudio": false,
            "hasSubtitles": false,
            "stereoPacking": 2,
            "is2D": false,
            "isEquiAngular": false
        }
    ]
}


Command update

This command requests the current playback status. After the server receives the CommandUpdate, it will send a new Command message to the client.

{
    "sender": "Client",
    "type": "CommandUpdate",
    "protocolVersion": 2,
    // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
    "sentUnixTimestampMs": 1721807772071
}


Ping

A ping command should be sent about every 4 seconds for several reasons:

  1. To keep the server from disconnecting the client
  2. To send an update of the device status
  3. To measure the latency between client and server
{
    "deviceStatus":
    {
        // The name assigned to this device
        "deviceName": "VR Headset 1",
        // A platform string determining the app version/os
        "clientPlatform": "MetaQuest3",
        // Charging or Discharging
        "batteryStatus": "Charging",
        "batteryPercentage": 99,
        // A timestamp for when this content has last changed. If this device does not match the highest timestamp of connected devices, it's considered out of date
        "preloaded": "2024-05-03T16:02:44+02:00",
        "playbackStatus":
        {
            // The duration of the media as perceived by the client
            "mediaLengthMs": 101248,
            // The name of the currently playing media file
            "mediaName": "1_videofile.mp4",
            // The playback progress of the media
            "mediaProgressMs": 57276,
            "currentMedia":
            {
                // The type of the currently playing media
                "type": "CloudVideo",
                // The ID or command number of the media file
                "identifier": "7145",
                // The duration as indicated by the device
                "durationInMs": 101248,
                // The buffering delay before playing this media file
                "playDelayMs": 5000
            },
            // The index of this media file in the playlist
            "playlistPosition": 0,
            "isPaused": false,
            // Indicating if the playlist is looping
            "looping": false
        },
        // The device id should be a hardware determined constant value. Should not contain spaces!
        "deviceUID": "8eab46180a18e60cba6fdd0b5cd4653e56c58cec",
        // The version of the current client app
        "appVersion": "3.5.0",
        // The latency as measured by the client (by utilizing ping commands)
        "clientPerceivedLatencyMs": 13
    },
    "clientVersion": "3.5.0",
    // Give each ping message a unique id to measure latency of roundtrips
    "id": 25,
    "type": "Ping",
    "sender": "Client",
    "protocolVersion": 2,
    // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
    "sentUnixTimestampMs": 1721807772071
}


Server to Admin message types


Media update

This message gets sent to the admin when the latest set of media has changed. This is used in the webremote to show the media command buttons.

{
    "type": "MediaUpdate",
    "sender": "Server",
    "protocolVersion": 2,
    // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
    "sentUnixTimestampMs": 1721807772071,
    // This timestamp signifies the latest content timestamp from the most up-to-date device(s)
    "updatedAt": "2024-04-05T16:47:35+02:00",
    "media": [
      {
        "type": "CloudVideo",
        "durationInMs": 41822,
        "width": 3840,
        "height": 1920,
        "fileUrl": "https://vrsynccontent.azureedge.net/testing-content/67514/1248b13a-615f-4300-b8d9-c994682932d9.mp4",
        "identifier": "58",
        "mediaPath": "c:/VRSync\\Cloud\\58.mp4",
        "mediaName": "gear intro en.mp4",
        "hasSpatialAudio": false,
        "hasSubtitles": false,
        "stereoPacking": 0,
        "is2D": false,
        "isEquiAngular": false
      },
      {
        "type": "CloudImage",
        "durationInMs": 0,
        "width": 0,
        "height": 0,
        "fileUrl": "https://vrsynccontent.azureedge.net/testing-content/67514/e9209587-be49-4a3e-838b-fe05d3575cf8.jpg",
        "identifier": "61",
        "mediaPath": "c:/VRSync\\Cloud\\61.jpg",
        "mediaName": "cave_wall_4k.jp",
        "hasSpatialAudio": false,
        "hasSubtitles": false,
        "stereoPacking": 2,
        "is2D": false,
        "isEquiAngular": true
      },
      {
        "type": "CloudImage",
        "durationInMs": 0,
        "width": 0,
        "height": 0,
        "fileUrl": "https://vrsynccontent.azureedge.net/testing-content/67514/d0189f2b-e191-4160-8ac8-b90dbf0537bd.png",
        "identifier": "62",
        "mediaPath": "c:/VRSync\\Cloud\\62.png",
        "mediaName": "wallpaper.png",
        "hasSpatialAudio": false,
        "hasSubtitles": false,
        "stereoPacking": 0,
        "is2D": false,
        "isEquiAngular": false
      },
      {
        "type": "CloudImage",
        "durationInMs": 0,
        "width": 0,
        "height": 0,
        "fileUrl": "https://vrsynccontent.azureedge.net/testing-content/67514/0da6419e-39e9-4110-a6b8-836267f1f1aa.jpg",
        "identifier": "63",
        "mediaPath": "c:/VRSync\\Cloud\\63.jpg",
        "mediaName": "widescreen.jpg",
        "hasSpatialAudio": false,
        "hasSubtitles": false,
        "stereoPacking": 0,
        "is2D": false,
        "isEquiAngular": false
      },
      {
        "type": "CloudVideo",
        "durationInMs": 4936,
        "width": 3840,
        "height": 2160,
        "fileUrl": "https://vrsynccontent.blob.core.windows.net/testing-content/67514/de15d542-5eec-4271-93ac-573b4da84184.mp4",
        "identifier": "147",
        "mediaPath": "c:/VRSync\\Cloud\\147.mp4",
        "mediaName": "1_Short Jan (tb)_TB.mp4",
        "hasSpatialAudio": false,
        "hasSubtitles": false,
        "stereoPacking": 1,
        "is2D": false,
        "isEquiAngular": false
      },
      {
        "type": "CloudVideo",
        "durationInMs": 14268,
        "width": 3840,
        "height": 2160,
        "fileUrl": "https://vrsynccontent.blob.core.windows.net/testing-content/67514/69c19bfb-9377-48fa-a1ef-53d30317361e.mp4",
        "identifier": "148",
        "mediaPath": "c:/VRSync\\Cloud\\148.mp4",
        "mediaName": "2_Medium Jan (tb)_TB.mp4",
        "hasSpatialAudio": false,
        "hasSubtitles": false,
        "stereoPacking": 1,
        "is2D": false,
        "isEquiAngular": false
      },
      {
        "type": "CloudVideo",
        "durationInMs": 101248,
        "width": 3840,
        "height": 2160,
        "fileUrl": "https://vrsynccontent.blob.core.windows.net/testing-content/67514/ad1e8c25-c7fd-4319-8531-0805192c6d9d.mp4",
        "identifier": "149",
        "mediaPath": "c:/VRSync\\Cloud\\149.mp4",
        "mediaName": "3_Full Jan (tb + srt)_TB.mp4",
        "hasSpatialAudio": false,
        "hasSubtitles": false,
        "stereoPacking": 1,
        "is2D": false,
        "isEquiAngular": false
      },
      {
        "type": "CloudVideo",
        "durationInMs": 206571,
        "width": 4096,
        "height": 2048,
        "fileUrl": "https://vrsynccontent.blob.core.windows.net/testing-content/67514/262645da-de5e-41b6-a3d9-768b8183e750.mp4",
        "identifier": "150",
        "mediaPath": "c:/VRSync\\Cloud\\150.mp4",
        "mediaName": "14_(left_right_stereo)Titan_LR.mp4",
        "hasSpatialAudio": false,
        "hasSubtitles": false,
        "stereoPacking": 2,
        "is2D": false,
        "isEquiAngular": false
      },
      {
        "type": "CloudVideo",
        "durationInMs": 20010,
        "width": 3840,
        "height": 2160,
        "fileUrl": "https://cdn.com/9c22be04-9d68-48c8-acab-1473d5d26559.mp4",
        "identifier": "59",
        "mediaPath": "c:/VRSync\\Cloud\\59.mp4",
        "mediaName": "6_Pearle VR_TB.mp4",
        "hasSpatialAudio": false,
        "hasSubtitles": false,
        "stereoPacking": 1,
        "is2D": false,
        "isEquiAngular": false
      },
      {
        "type": "LocalVideo",
        "identifier": "1",
        "mediaName": "RideFilm.mp4",
        "duplicateAcrossDevices": false
      }
    ]
  }


Status update

A message that contains all new status details from the devices.

{
    "type": "StatusUpdate",
    "sender": "Server",
    "protocolVersion": 2,
    // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
    "sentUnixTimestampMs": 1721807772071,
    "devices": [
      {
        // The name given to the device
        "deviceName": "Device 1",
        // The platform reported by the device
        "clientPlatform": "Meta",
        // Charging or Discharging
        "batteryStatus": "Discharging",
        "batteryPercentage": 100,
        // A preload timestamp. Note this can be in different time offsets
        "preloaded": "2024-04-05T16:47:35+02:00",
        // Device is not playing currently
        "playbackStatus": {
          "mediaLengthMs": 0,
          "mediaProgressMs": 0,
          "playlistPosition": 0,
          "isPaused": false,
          "looping": false
        },
        // A hardware specific constant id. Should not contain spaces!
        "deviceUID": "8eab46180a18e60cba6fdd0b5cd4653e56c58cec",
        "appVersion": "3.5.0",
        "clientPerceivedLatencyMs": 4,
        // A group ID. See the groups in the ping message. -1 means unassigned.
        "clientGroup": -1,
        "connected": false
      },
      {
        "deviceName": "Device 2",
        "clientPlatform": "Pico",
        "batteryStatus": "Discharging",
        "batteryPercentage": 81,
        "preloaded": "2024-04-05T15:47:35+01:00",
        "playbackStatus": {
          "mediaLengthMs": 0,
          "mediaProgressMs": 0,
          "playlistPosition": 0,
          "isPaused": false,
          "looping": false
        },
        // A hardware specific constant id. Should not contain spaces!
        "deviceUID": "e39a6dcf3d4b411adc49a80128fb6081",
        "appVersion": "3.5.0.simtoolsbeta",
        "clientPerceivedLatencyMs": 20,
        "statusAge": 5166,
        // This belongs to the group with ID 0
        "clientGroup": 0,
        "connected": false
      }
    ]
  }


Command history

The command history message contains all the most recent (active) commands. The sentCommands array can contain multiple items if different groups/devices have different commands. You can cross-reference the commandHistory and sentCommands arrays to determine which devices are using which command.

{
  type: 'CommandHistory',
  sender: 'Server',
  protocolVersion: 2,
  // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
  sentUnixTimestampMs: 1721807772071,
  commandHistory: [
	{
  	 uid: 'all',
  	 commandId: 'xKEMEQN77r'
	},
	{
  	 uid: 'd58c75a35eb45b7dd979d0bdb811e6c961405106',
  	 commandId: 'QxqXqA7d3d'
	},
	{
  	 uid: 'as78dy239fys8g67gaew8fg67eiwgfwi73gr32i7',
  	 commandId: '4DEpEnNKnr'
	}
    ],
  sentCommands: [
    {
  	type: 'Command',
  	sender: 'Server',
  	protocolVersion: 2,
  	playlist: [],
  	identifier: 'xKEMEQN77r',
  	loop: false,
  	paused: false,
  	currentTime: 13526,
  	serverReceived: 1721817317920
     },
     {
  	 type: 'Command',
  	 sender: 'Server',
  	 protocolVersion: 2,
  	 playlist: [
      	 {
        	type: 'CloudVideo',
        	identifier: '12993',
        	durationInMs: 42684,
        	disableExactSync: false,
        	playDelayMs: 5000
      	 }],
  	 identifier: 'QxqXqA7d3d',
  	 loop: false,
  	 paused: false,
  	 currentTime: 157844,
  	 serverReceived: 1721817580080
     },
     {
  	 type: 'Command',
  	 sender: 'Server',
  	 protocolVersion: 2,
  	 playlist: [
      	 {
        	type: 'CloudVideo',
        	identifier: '13182',
        	durationInMs: 20010,
        	disableExactSync: false,
        	playDelayMs: 5000
      	 }],
  	 identifier: '4DEpEnNKnr',
  	 loop: false,
  	 paused: false,
  	 currentTime: 152666,
  	 serverReceived: 1721817587901
     }
  ]
}


Ping

The ping message gets sent back to the admin application after receiving a ping message.

{
    "type": "Ping",
    "sender": "Server",
    "protocolVersion": 2,
    // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
    "sentUnixTimestampMs": 1721807772071,
    // The current license limit
    "userLimit": 3,
    // The server software version
    "serverVersion": "3.5.0",
    // The VR Sync version required for clients to work correctly
    "minimumVersion": "3.2.0",
    // The latest VR Sync version
    "preferredVersion": "3.5.0",
    // The different device groups where the key is the Id, and the value is the name.
    "groups": {
      "0": "My Group"
    },
    // For VR Sync boxes, the license can be changed using the licensecode in messages
    "canChangeLicense": false
  }


Admin to Server message types


Command

This command allows you to send a new play/stop command. You can specify a list of devices, or ‘all’ to target all connected devices.

{
    "type": "Command",
    "sender": "Admin",
    // All admin messages should contain a license code
    "licenseCode": "abcdefghijklmnopqrstuvwxyz",
    "protocolVersion": 2,
    // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
    "sentUnixTimestampMs": 1721807772071,
    "currentTime": 0,
    "playlist": [{
        // "LocalVideo", "CloudVideo", "LocalImage", "CloudImage",
        "type": "localvideo",
        // The cloud content id, or local preloaded numbered prefix
        "identifier": "1",
        // The duration of the media file
        "durationInMs": 45000,
        // How much time the device(s) get to buffer the video. A higher number means more accuracy at the cost of waiting time. The server defaults to 5 seconds if this value is omitted.
        "playDelayMs": 5000
    }],
    "loop": false,
    // Can be ["all"] or a list of device IDs
    "deviceUIDs": ["all"]
}

Pause/Resume

If there’s a command currently active, it can be paused/resumed using these messages.

{
    "type": "PauseResume",
    "sender": "Admin",
    // All admin messages should contain a license code
    "licenseCode": "abcdefghijklmnopqrstuvwxyz",
    "protocolVersion": 2,
    // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
    "sentUnixTimestampMs": 1721807772071,
    // Set this to true to pause, use false te resume
    "paused": false,
    // Can be ["all"] or a list of device IDs
    "deviceUIDs": ["all"]
}

Text

To show a text message to the VR devices, send this command.

{
    "type": "Text",
    "sender": "Admin",
    // All admin messages should contain a license code
    "licenseCode": "abcdefghijklmnopqrstuvwxyz",
    "protocolVersion": 2,
    // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
    "sentUnixTimestampMs": 1721807772071,
    // Provide a text message to show to the user
    "text": "Please take off your headset",
    // Can be ["all"] or a list of device IDs
    "deviceUIDs": ["all"]
}


Ping

A ping message should be sent to the server in about a 4 second interval to prevent disconnects.

{
    "sender": "Admin",
    // All admin messages should contain a license code
    "licenseCode": "abcdefghijklmnopqrstuvwxyz",
    "type": "Ping",
    "protocolVersion": 2,
    // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
    "sentUnixTimestampMs": 1721807772071
}


Clear database

To clear the state on the server, and thus remove all devices, commands and media, a clear database message can be sent. This will restart the server.

{
    "sender": "Admin",
    // All admin messages should contain a license code
    "licenseCode": "abcdefghijklmnopqrstuvwxyz",
    "type": "CleaDB",
    "protocolVersion": 2,
    // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
    "sentUnixTimestampMs": 1721807772071
}

Name change

To change the display name of a device, send a name change command using the device’s unique id.


{
    "type": "NameChange",
    "sender": "Admin",
    "licenseCode": "abc",
    "protocolVersion": 2,
    // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
    "sentUnixTimestampMs": 1721807772071,
    "uid": "e39a6dcf3d4b411adc49a80128fb6081",
    "name": "My Device 2"
  }

Make group

Devices can belong to a group. These groups can be used to quickly multiselect devices in the webremote API. To create a new group, send a make group command. the new group will show up in the next ping message from the server.

{
  "type": "MakeGroup",
  "sender": "Admin",
  "licenseCode": "abc",
  "protocolVersion": 2,
  // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
  "sentUnixTimestampMs": 1721807772071,
  "name": "My Group 2"
}


Group change

To rename a group, you can send a group change command.

{
  "type": "GroupChange",
  "sender": "Admin",
  "licenseCode": "abc",
  "protocolVersion": 2,
  // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
  "sentUnixTimestampMs": 1721807772071,
  "name": "MyGroup2",
  "group": 1
}


Remove group

To delete a group, and unassign the devices in that group, send a remove group command.

{
    "type": "RemoveGroup",
    "sender": "Admin",
    "licenseCode": "abc",
    "protocolVersion": 2,
    // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
    "sentUnixTimestampMs": 1721807772071,
    "group": 1
  }


Add to group

Devices can belong to a group. These groups can be used to quickly multiselect devices in the webremote API. To assign a device to a group, specify an id. To unassign a device, specify group id -1.

{
    "type": "AddToGroup",
    "sender": "Admin",
    "licenseCode": "abc",
    "protocolVersion": 2,
    // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
    "sentUnixTimestampMs": 1721807772071,
    // The group id to move the device to
    "group": 0,
    // The device Id
    "uid": "e39a6dcf3d4b411adc49a80128fb6081"
}

Remove client

Removes the device from the devices list. Use this if the device is no longer in use. Note that the list of devices does not affect your license. Licenses are tied to simultaneous connections.

{
  "type": "RemoveClient",
  "sender": "Admin",
  "licenseCode": "abc",
  "protocolVersion": 2,
  // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
  "sentUnixTimestampMs": 1721807772071,
  "uid": "8eab46180a18e60cba6fdd0b5cd4653e56c58cec"
}


Calibrate

A calibration message is being sent to all devices, causing them to reset their orientation to the current position.

{
  "type": "Calibrate",
  "sender": "Admin",
  "licenseCode": "abc",
  "protocolVersion": 2,
  // The Unix UTC timestamp of the sender's clock when the message was sent, in milliseconds
  "sentUnixTimestampMs": 1721807772071
}

Related

VR Sync API: Introduction Read

VR Sync API: Creating a VR Sync Client Read

VR Sync API: Creating a VR Sync Remote Read