# Source: https://docs.livekit.io/reference/internals/client-protocol.md

LiveKit docs › Internals › Signaling Protocol

---

# Client Protocol

> This is an overview of the core protocol LiveKit uses to communicate with clients. It's primarily oriented towards those building new SDKs or developers interested in contributing to LiveKit.

> ℹ️ **Note**
> 
> Using LiveKit in your app does not require you to understand the underlying protocol. This is one of our design goals.

## Basics

LiveKit clients use a WebSocket to communicate with the server over Protocol Buffers. Client could establish up to two WebRTC PeerConnections with the SFUs, used for publishing and receiving streams, respectively.

By default, the subscriber PeerConnection will always be open upon connection. The publisher PeerConnection will be established only when the client is ready to publish.

![Client-Server Connection](/images/diagrams/client-server-connection.svg)

### Protobufs

LiveKit uses Protocol Buffers for all of its communications. Communication happens asynchronously: one side may send a message to the other at any time, without the expectation of an immediate response. LiveKit protobufs reside in the [livekit/protocol repo](https://github.com/livekit/protocol).

As a convention, a client always sends a `SignalRequest` and the server replies with a `SignalResponse`.

### Dedicated PeerConnections

For each client connected to the server, we use up to two separate `PeerConnection` objects. One for publishing tracks to the server, and the other for receiving subscribed tracks.

Using separate peer connections simplifies the negotiation process and eliminates negotiation [Glares](https://www.ietf.org/proceedings/82/slides/rtcweb-10.pdf). The side sending tracks to the other will be the one that initiates the offer.

## Joining a room

1. client initiates WebSocket connection to `/rtc`
2. server sends a `JoinResponse`, which includes room information, the current participant's data, and information about other participants in the room
3. server initiates the subscriber `PeerConnection`, sends `offer` to client- if `AutoSubscribe` is enabled, this offer will contain existing tracks in the room.
- the offer will include two data channels as part of the connection
4. client and server will exchange ICE candidates via `trickle`
5. client accepts the subscriber connection, sends an `answer`
6. ICE connectivity is established
7. server notifies other participants of the new participant

### WebSocket Parameters

Websocket endpoint `/rtc` is the initial step that the client connects to. It takes in several parameters to give the server information about the client and its capabilities:

- access_token: an encoded JWT access token
- reconnect: true if client is trying to resume to an existing connection. when this is set, server will attempt to perform a ICE restart after connection is established.
- auto_subscribe: true by default. If true, server will automatically subscribe client to all tracks in the room
- sdk: indicates the SDK it's using. (js, ios, android, etc)
- protocol: indicates the protocol version. this document descriibes the latest protocol version: 9
- version: version of the client SDK

## Publishing

To publish a track, a client must first notify the server of its intent and send up any client-defined metadata about the track.

1. client sends a `AddTrackRequest` with track metadata
2. server sends back a `TrackPublishedResponse`
3. client adds a transceiver to the `PeerConnection`, along with the media track
4. client initiates `offer`, sends to server
5. server answers the offer and starts receiving the track
6. if server subscribes other participants to the track

## Receiving tracks

LiveKit server sends down track metadata to all participants in a room as soon as it's published, then it adds the track to each client's subscriber `PeerConnection`.

## Server events

The client must also be ready to act upon other changes in the room. The server will notify clients of:

- `ParticipantUpdate`: when other participants join or leave, or if there are changes to their tracks
- `LeaveRequest`: when the participant should immediately disconnect
- `SpeakersChanged`: when the active speakers in the room changes

For all server events, clients should handle them in an idempotent way. For example, it's possible to receive multiple ParticipantUpdates with identical metadata.

### SpeakersChanged

Server will send down a list of `SpeakerInfo` that has changed from the last update. Clients are responsible for applying the deltas and firing the appropriate events.

## Client-initiated control

### Mute/unmute local tracks

WebRTC doesn't natively support muting tracks. When a track is disabled, it will continue to periodically send "empty" packets. With LiveKit (and SFUs, in general), we want a discrete mute event in order to notify other participants of the change and to optimize network consumption by suppressing empty packets.

To mute a track, set `MediaStreamTrack.enabled` to false, and subsequently send a `MuteTrackRequest` to the server with that track's `sid`.

### Changing quality of streams

For a particular client, `UpdateTrackSettings` informs the server whether a subscribed track should be temporarily paused, or if the server should send down a stream of differing quality. This is especially useful for larger rooms, when the client wants to optimize how much data it's receiving at once. For example, offscreen clients could have their streams temporarily paused.

### Subscription control

Clients also have the ability to control which tracks they're subscribed to. An `UpdateSubscription` message allows the client to subscribe or unsubscribe to published tracks.

---

This document was rendered at 2026-02-03T03:25:26.543Z.
For the latest version of this document, see [https://docs.livekit.io/reference/internals/client-protocol.md](https://docs.livekit.io/reference/internals/client-protocol.md).

To explore all LiveKit documentation, see [llms.txt](https://docs.livekit.io/llms.txt).