Integration documentation and an independent browser test panel for direct
one-to-one audio/video calling. Meetings, group calls, and livestreaming are
intentionally not included.
1. Requirements
A modern browser with WebRTC support.
Microphone permission for audio calls.
Camera and microphone permission for video calls.
Run through HTTPS or localhost so browser media permissions work correctly.
Each connected user must have a unique userId.
The SDK creates the signaling callId. The application user does not enter it.
The caller reads the generated callId from sdk.controller.currentCallId immediately after sdk.startCall(...).
If onCallRinging provides a callId, the same value is stored again there.
The receiver receives the same callId in onIncomingCall.
Use that callId as the LiveKit roomId for both users.
For a video call, pass isVideo: true. For an audio-only call, pass isVideo: false.
The receiver must use the same isVideo value received in onIncomingCall.
No separate room ID is required: do not ask the user for a room ID and do not add
a new room ID field to WebSocket signaling. Pass the received callId as the
roomId value when calling startMedia or acceptCall.
One-to-one media controls: use muteCallAudio,
muteCallVideo, and switchCallCamera. The shorter
muteAudio, muteVideo, and switchCamera names are
assigned to meeting controls in the combined SDK.
2. Import and initialize the SDK
import { CallSDK } from "https://rtc.myhubs.in/myhub-livekit-sdk/myhub-livekit-sdk.js";
const sdk = new CallSDK();
3. One-to-one call flow
Create the SDK and register the listener.
Connect the current user using sdk.connect({ userId }).
The caller selects the call type: isVideo: true for video or isVideo: false for audio.
The caller starts a call using sdk.startCall(...) with that isVideo value.
Immediately after sdk.startCall(...), the caller stores sdk.controller.currentCallId.
If onCallRinging(callId) supplies an ID, store that same value there too.
The receiver stores the same callId received in onIncomingCall(callId, fromUserId, isVideo).
The receiver accepts using roomId: currentCallId.
The caller starts media after onCallAccepted using roomId: currentCallId.
Attach localStream and remoteStream to the video elements.
Either user can mute audio/video, switch the camera, or end the call.
4. SDK methods
Method
Parameters
Purpose
connect
{ userId }
Connects and registers the current user for signaling.
startCall
{ toUserId, isVideo }
Starts an outgoing one-to-one call. Use true for video or false for audio.
Accepts an incoming call using the received callId as the media room and the received isVideo call type.
rejectCall
None
Rejects the current incoming call.
startMedia
{ roomId: currentCallId, userId, isVideo }
Starts caller-side media using the ringing call's callId as the media room.
endCall
None
Ends the current call.
muteCallAudio
boolean
Mutes or unmutes the current user's microphone.
muteCallVideo
boolean
Disables or enables the current user's camera track.
switchCallCamera
None
Switches between available front and rear cameras.
5. Listener callbacks
Callback
Arguments
When it is called
onIncomingCall
callId, fromUserId, isVideo
A direct call is received.
onCallRinging
callId when supplied
The outgoing call is ringing. Use the callback value when present; otherwise use sdk.controller.currentCallId, which was generated by startCall.
onCallAccepted
None
The receiver accepted the call.
onCallRejected
None
The receiver rejected the call.
onCallEnded
None
The active call ended.
onUserOffline
None
The target user is not connected.
onRemoteAudioMuted
muted
The remote user mutes or unmutes audio.
onRemoteVideoMuted
muted
The remote user disables or enables video.
onLocalAudioMuted
muted
The local microphone state changes.
onLocalVideoMuted
muted
The local camera state changes.
6. Complete one-to-one integration example
This is the same call flow used by the independent tester below.
import { CallSDK } from "https://rtc.myhubs.in/myhub-livekit-sdk/myhub-livekit-sdk.js";
const sdk = new CallSDK();
let streams = null;
let currentCallId = null;
let incomingCallerId = null;
let currentCallIsVideo = true;
let audioMuted = false;
let videoMuted = false;
sdk.setListener({
onIncomingCall: (callId, fromUserId, isVideo) => {
currentCallId = callId;
incomingCallerId = fromUserId;
currentCallIsVideo = isVideo;
console.log("Incoming call", { callId, fromUserId, isVideo });
},
onCallerBusy: (callId) => {
alert("You cannot start a new call because you are already in an active call on another device.");
},
onCalleeBusy: (callId) => {
alert("The person you are trying to call is currently on another call.");
},
onCallAnsweredElsewhere: (callId) => {
// Hide the ringing screen because you just answered it on your phone/other tab!
console.log(`Call ${callId} was answered on another device.`);
},
onError: (code, message) => {
console.error(`Received error ${code}: ${message}`);
},
onCallRinging: (callId) => {
currentCallId = callId || sdk.controller?.currentCallId || currentCallId;
console.log("Ringing", { callId: currentCallId });
},
onCallAccepted: async () => {
streams = await sdk.startMedia({
roomId: currentCallId,
userId: "user_123",
isVideo: currentCallIsVideo
});
localVideo.srcObject = streams.localStream;
remoteVideo.srcObject = streams.remoteStream;
},
onCallRejected: () => console.log("Call rejected"),
onCallEnded: () => console.log("Call ended"),
onUserOffline: () => console.log("User offline"),
onRemoteAudioMuted: (muted) => console.log("Remote audio muted:", muted),
onRemoteVideoMuted: (muted) => console.log("Remote video muted:", muted),
onLocalAudioMuted: (muted) => console.log("Local audio muted:", muted),
onLocalVideoMuted: (muted) => console.log("Local video muted:", muted)
});
// Connect current user
sdk.connect({ userId: "user_123" });
// Caller: true for video, false for audio-only
currentCallIsVideo = true;
sdk.startCall({
toUserId: "user_439",
isVideo: currentCallIsVideo
});
// The SDK generates the signaling callId synchronously.
// Use exactly that value as the LiveKit roomId.
currentCallId = sdk.controller?.currentCallId;
// Receiver
streams = await sdk.acceptCall({
roomId: currentCallId,
userId: "user_439",
fromUserId: incomingCallerId,
isVideo: currentCallIsVideo
});
localVideo.srcObject = streams.localStream;
remoteVideo.srcObject = streams.remoteStream;
// Other one-to-one call actions
sdk.rejectCall();
sdk.muteCallAudio(true);
sdk.muteCallVideo(true);
sdk.switchCallCamera();
sdk.endCall();
7. Independent browser test
Open this page for both users and use different user IDs. Connect both users before
starting the call. The caller reads the generated callId from the existing SDK controller,
while the receiver gets the same value from onIncomingCall. Both use it automatically as the LiveKit room ID.