The SDK adds WebRTC voice and video to any conversation. It handles signaling (over the same WebSocket), peer connections, and fetching ICE servers (STUN/TURN) — you wire up the media streams and buttons. Calls live on the conversation object. Get a conversation, then call its call methods and listen for its call events.
For calls to connect reliably across networks, your app needs a TURN server. The SDK fetches ICE servers (STUN/TURN) from your app’s config — make sure appId and apiUrl are set on the client so it can call GET /apps/:id/ice-servers.

Prerequisites

Start a call

The other participants receive a call_invite event.

Receive & answer a call

Use joinCall(callType) instead of acceptCall() to join a call that’s already in progress (for example, a group call you weren’t the original invitee of).

Render remote media

In-call controls

Peers are notified via peer_mute / peer_unmute / peer_video_off / peer_video_on.

End a call

Call events

Listen on the conversation:

Call quality

After a call ends, the SDK can report quality metrics (packet loss, jitter, RTT) so they show up in your dashboard analytics. You can also read them live:
Metrics are submitted to POST /conversations/:id/call-quality.

Full flow

1

Set up media permissions

The SDK requests camera/mic access when a call starts. Handle the browser permission prompt in your UI.
2

Caller

conversation.startCall('video') → listen for call_accept / call_connected → render streams.
3

Callee

call_invite → conversation.acceptCall() → call_connected → render streams.
4

Hang up

Either side calls conversation.endCall(); both receive call_hangup.

How calls work

WebRTC, ICE/TURN, and the end-to-end call flow.

Call signaling protocol

The raw WebSocket signaling messages, if you’re not using the SDK.