Skip to main content
This page documents all WebSocket events and room requests used in OpenTogetherTube.

Server Messages

Messages sent from the server to connected clients.

Sync

Synchronize room state with clients. Sent when room properties change. Message Format
{
  "action": "sync",
  "name": "room-name",
  "title": "Room Title",
  "description": "Room description",
  "isTemporary": false,
  "visibility": "public",
  "queueMode": "manual",
  "isPlaying": true,
  "playbackPosition": 42.5,
  "playbackSpeed": 1.0,
  "currentSource": {
    "service": "youtube",
    "id": "dQw4w9WgXcQ",
    "title": "Video Title",
    "length": 213
  },
  "queue": [
    {
      "service": "youtube",
      "id": "next-video",
      "title": "Next Video",
      "length": 180
    }
  ],
  "grants": [[0, 255], [1, 511]],
  "hasOwner": true,
  "voteCounts": [["youtube:dQw4w9WgXcQ", 5]],
  "enableVoteSkip": false,
  "votesToSkip": [],
  "videoSegments": [],
  "autoSkipSegmentCategories": ["sponsor", "intro"]
}
Fields
action
string
Always "sync"
name
string
Room name/identifier
isPlaying
boolean
Whether video is currently playing
playbackPosition
number
Current playback position in seconds
playbackSpeed
number
Playback speed multiplier (default: 1.0)
currentSource
object|null
Currently playing video
queue
array
Array of queued videos
grants
array
Permission grants as [role, mask] pairs
voteCounts
array
Vote counts as ["service:id", count] pairs
videoSegments
array
SponsorBlock segments for current video

Event

Notify clients of user actions (e.g., video added, playback changed). Message Format
{
  "action": "event",
  "request": {
    "type": 5,
    "video": {
      "service": "youtube",
      "id": "dQw4w9WgXcQ"
    }
  },
  "user": {
    "name": "Username",
    "isLoggedIn": true
  },
  "additional": {
    "video": {
      "service": "youtube",
      "id": "dQw4w9WgXcQ",
      "title": "Video Title",
      "length": 213
    }
  }
}
Fields
action
string
Always "event"
request
object
The original room request that triggered this event
user
object
User who performed the action
additional
object
Extra context about the event (varies by request type)

Event Custom

System-generated events with custom messages.
{
  "action": "eventcustom",
  "text": "Skipped sponsor",
  "duration": 3000
}
text
string
Event message
duration
number
Display duration in milliseconds (optional)

Chat

Chat message from a user.
{
  "action": "chat",
  "from": {
    "id": "client-123",
    "name": "Username",
    "isLoggedIn": true,
    "status": "ready",
    "role": 2
  },
  "text": "Hello everyone!"
}
from
object
User who sent the message
text
string
Chat message content

User

User join, leave, or update notification. User Joined/Updated
{
  "action": "user",
  "update": {
    "kind": "update",
    "value": {
      "id": "client-123",
      "name": "Username",
      "isLoggedIn": true,
      "status": "ready",
      "role": 2
    }
  }
}
User Left
{
  "action": "user",
  "update": {
    "kind": "remove",
    "value": "client-123"
  }
}
Initial User List
{
  "action": "user",
  "update": {
    "kind": "init",
    "value": [
      {
        "id": "client-123",
        "name": "User1",
        "isLoggedIn": true,
        "status": "ready",
        "role": 2
      },
      {
        "id": "client-456",
        "name": "User2",
        "isLoggedIn": false,
        "status": "ready",
        "role": 0
      }
    ]
  }
}

You

Server sends your client ID after authentication.
{
  "action": "you",
  "info": {
    "id": "client-123-abc"
  }
}
info.id
string
Your unique client identifier

Announcement

Server-wide announcement message.
{
  "action": "announcement",
  "text": "Server maintenance in 30 minutes"
}

Unload

Room is being unloaded. Clients should disconnect.
{
  "action": "unload"
}

Client Messages

Messages sent from clients to the server.

Authenticate

{
  "action": "auth",
  "token": "YOUR_AUTH_TOKEN"
}

Player Status

{
  "action": "status",
  "status": 2
}
Status Values
  • 0 - none (no player loaded)
  • 1 - buffering
  • 2 - ready
  • 3 - error

Kick Me

Request to be kicked from the room (leave).
{
  "action": "kickme",
  "reason": 0
}

Notify

Notify server of client-side changes.
{
  "action": "notify",
  "message": "usernameChanged"
}

Room Request

Perform an action in the room.
{
  "action": "req",
  "request": {
    "type": 5,
    "video": {
      "service": "youtube",
      "id": "dQw4w9WgXcQ"
    }
  }
}

Room Requests

Room requests are actions performed within a room, sent via the req client message.

Join Request

Join a room.
{
  "type": 0,
  "info": {
    "id": "client-123",
    "username": "MyUsername",
    "status": 2
  }
}
type
number
0 (RoomRequestType.JoinRequest)
info.id
string
Your client ID (from you message)
info.username
string
Your display name
info.status
number
Player status

Leave Request

Leave the room.
{
  "type": 1
}

Playback Request

Play or pause the video.
{
  "type": 2,
  "state": true
}
state
boolean
true to play, false to pause

Skip Request

Skip to the next video (or vote to skip if vote-skip is enabled).
{
  "type": 3
}

Seek Request

Seek to a specific position.
{
  "type": 4,
  "value": 42.5
}
value
number
Position in seconds

Add Request

Add video(s) to the queue. Single Video
{
  "type": 5,
  "video": {
    "service": "youtube",
    "id": "dQw4w9WgXcQ"
  }
}
Multiple Videos
{
  "type": 5,
  "videos": [
    {"service": "youtube", "id": "dQw4w9WgXcQ"},
    {"service": "youtube", "id": "jNQXAC9IVRw"}
  ]
}
By URL
{
  "type": 5,
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
}

Remove Request

Remove a video from the queue.
{
  "type": 6,
  "video": {
    "service": "youtube",
    "id": "dQw4w9WgXcQ"
  }
}

Order Request

Reorder the queue.
{
  "type": 7,
  "fromIdx": 0,
  "toIdx": 3
}
fromIdx
number
Source index
toIdx
number
Destination index

Vote Request

Vote for a video in the queue.
{
  "type": 8,
  "video": {
    "service": "youtube",
    "id": "dQw4w9WgXcQ"
  },
  "add": true
}
add
boolean
true to add vote, false to remove vote

Promote Request

Promote/demote a user’s role.
{
  "type": 9,
  "targetClientId": "client-456",
  "role": 2
}
targetClientId
string
Client ID of user to promote
role
number
New role ID (0-5)

Update User

Update user information.
{
  "type": 10,
  "info": {
    "id": "client-123",
    "username": "NewUsername",
    "status": 2
  }
}

Chat Request

Send a chat message.
{
  "type": 11,
  "text": "Hello everyone!"
}

Undo Request

Undo a previous action.
{
  "type": 12,
  "event": {
    "action": "event",
    "request": { /* original request */ },
    "user": { /* user info */ },
    "additional": { /* event context */ }
  }
}
event
object
The event message to undo

Apply Settings Request

Change room settings.
{
  "type": 13,
  "settings": {
    "title": "New Title",
    "queueMode": "vote",
    "autoSkipSegmentCategories": ["sponsor", "intro"],
    "enableVoteSkip": true
  }
}

Play Now Request

Play a video immediately, pushing current video to queue.
{
  "type": 14,
  "video": {
    "service": "youtube",
    "id": "dQw4w9WgXcQ"
  }
}

Shuffle Request

Shuffle the queue.
{
  "type": 15
}

Playback Speed Request

Change playback speed.
{
  "type": 16,
  "speed": 1.5
}
speed
number
Playback speed (0.25 - 2.0)

Restore Queue Request

Restore or discard the previous queue.
{
  "type": 17,
  "discard": false
}
discard
boolean
true to discard, false to restore

Kick Request

Kick a user from the room.
{
  "type": 18,
  "clientId": "client-456"
}
clientId
string
Client ID of user to kick

RoomRequestType Enum

enum RoomRequestType {
  JoinRequest = 0,
  LeaveRequest = 1,
  PlaybackRequest = 2,
  SkipRequest = 3,
  SeekRequest = 4,
  AddRequest = 5,
  RemoveRequest = 6,
  OrderRequest = 7,
  VoteRequest = 8,
  PromoteRequest = 9,
  UpdateUser = 10,
  ChatRequest = 11,
  UndoRequest = 12,
  ApplySettingsRequest = 13,
  PlayNowRequest = 14,
  ShuffleRequest = 15,
  PlaybackSpeedRequest = 16,
  RestoreQueueRequest = 17,
  KickRequest = 18
}

Example: Complete Client Implementation

class OTTClient {
  constructor(serverUrl, token) {
    this.serverUrl = serverUrl;
    this.token = token;
    this.ws = null;
    this.clientId = null;
  }
  
  connect() {
    this.ws = new WebSocket(this.serverUrl);
    
    this.ws.onopen = () => {
      this.authenticate();
    };
    
    this.ws.onmessage = (event) => {
      const msg = JSON.parse(event.data);
      this.handleMessage(msg);
    };
  }
  
  authenticate() {
    this.send({ action: 'auth', token: this.token });
  }
  
  handleMessage(msg) {
    switch (msg.action) {
      case 'you':
        this.clientId = msg.info.id;
        console.log('Got client ID:', this.clientId);
        break;
      
      case 'sync':
        console.log('Room state:', msg);
        break;
      
      case 'event':
        console.log('User action:', msg);
        break;
      
      case 'chat':
        console.log(`${msg.from.name}: ${msg.text}`);
        break;
    }
  }
  
  joinRoom(roomName) {
    this.send({
      action: 'req',
      request: {
        type: 0, // JoinRequest
        info: {
          id: this.clientId,
          username: 'Guest',
          status: 2 // ready
        }
      }
    });
  }
  
  addVideo(service, id) {
    this.send({
      action: 'req',
      request: {
        type: 5, // AddRequest
        video: { service, id }
      }
    });
  }
  
  sendChat(text) {
    this.send({
      action: 'req',
      request: {
        type: 11, // ChatRequest
        text
      }
    });
  }
  
  send(data) {
    this.ws.send(JSON.stringify(data));
  }
}

// Usage
const client = new OTTClient('wss://opentogethertube.com/', 'YOUR_TOKEN');
client.connect();

Build docs developers (and LLMs) love