Skip to main content

Calls

Creating calls

You can create a call by providing the call type and an ID:

  • The call type controls which features are enabled and sets up permissions. Call type settings and permissions can be set from API, or using the Stream Dashboard.
  • Calls IDs can be reused, which means they can be joined multiple times, so it's possible to set up recurring calls.

You can specify call members who can receive push notification about the call.

It's also possible to store any custom data with the call object.

const callType = 'default';
const callId = 'my-first-call';
const call = client.video.call(callType, callId);

call.create({ data: { created_by_id: 'john' } });

// optionally provide additional data
call.create({
data: {
created_by_id: 'john',
// Call members need to be existing users
members: [{ user_id: 'john', role: 'admin' }, { user_id: 'jack' }],
custom: {
color: 'blue',
},
},
});

// Upsert behavior
call.getOrCreate({data: /* */});

ring flag

To start the ringing flow, we need to set the ring flag to true and provide the list of members we want to call. It is important to note that the caller should also be included in the list of members.

// or call.create
// or call.get
await client.call('default', 'test-outgoing-call').getOrCreate({
ring: true,
data: {
created_by_id: 'myself',
members: [{ user_id: 'myself' }, { user_id: 'my friend' }],
},
});

This step will start the signaling flow and send the call.ring WebSocket event. It's also possible to send push notifications to call members on this event, for more information see the Call Types page.

Call members can decide to accept or reject the incoming call. The callee can decide to cancel the outgoing call (for more information see the SDK documentations).

notify flag

Setting the notify flag to true will send the call.notification WebSocket event. It's also possible to send push notifications to call members on this event, for more information see the Call Types page.

// or call.create
// or call.get
await client.call('default', 'test-outgoing-call').getOrCreate({
notify: true,
data: {
created_by_id: 'myself',
members: [{ user_id: 'myself' }, { user_id: 'my friend' }],
},
});

Updating calls

Default call settings are inherited from the call type. These settings can be overridden if necessary.

call.update({
settings_override: {
audio: { mic_default_on: true, default_device: 'speaker' },
},
});

// or to update custom data
call.update({ custom: { color: 'red' } });

Manage call members

You can provide a list of call members. Call members need to be existing users. Every call member has a call-level role, you can configure roles on the call type.

Call members can receive push notifications.

// Call members need to be existing users
call.updateCallMembers({
// You can add new members
// You can also update the role of existing members
update_members: [{ user_id: 'sara' }, { user_id: 'emily', role: 'admin' }],
});

You can also remove call members:

call.updateCallMembers({
remove_members: ['sara'],
});

Restrict call access

You can restrict access to a call by updating the Call Type permissions and roles. A typical use case is to restrict access to a call to a specific set of users -> call members.

By default, all users unless specified otherwise, have the user role.

You can remove the join-call permission from the user role for the given call type scope. This will prevent regular users from joining a call of the given type.

Next, let's ensure that the call_member role has the join-call permission for the given call type scope. This will allow users with the call_member role to join a call of this type.

Once this is set, we can proceed with setting up a call instance and providing the members.

const callType = (await client.video.listCallTypes()).call_types[callTypeName];
// Remove JOIN_CALL permission from user role
const userGrants = callType.grants['user'].filter(
(c) => c !== VideoOwnCapability.JOIN_CALL,
);
// Make sure JOIN_CALL permission is set for call_member role
const callMemberGrants = callType.grants['call_member'];
if (!callMemberGrants.includes(VideoOwnCapability.JOIN_CALL)) {
callMemberGrants.push(VideoOwnCapability.JOIN_CALL);
}

// Update the call type with the changes
await client.video.updateCallType(callTypeName, {
grants: { user: userGrants, call_member: callMemberGrants },
});

Sessions

Call IDs can be reused, which means a call can be joined and left by participants multiple times. Every time the first participant joins the call, a new session is started. Every time the last participant left the call, the session is ended.

  • If a call session is started, the call.session_started WebSocket event will be dispatched. It's also possible to send push notifications to call members on this event, for more information see the Call Types page.

  • If a call session is ended, the call.session_ended WebSocket event will be dispatched.

Ending calls

This action terminates the call for everyone. Ending a call requires the end-call permission.

await call.endCall();

Only users with the join-ended-call permission can join an ended call.

Query calls

For many video calling, live stream, or audio rooms apps, you'll want to show:

  • Upcoming calls
  • Calls that are currently live
  • Popular live streams / audio rooms with a link to the recording

Examples

Below you can find a few examples of different queries:

Sorting and pagination

// default sorting
client.video.queryCalls();

// sorting and pagination
const queryCallsReq = {
sort: [{ field: 'starts_at', direction: -1 }],
limit: 2,
};
response = await client.video.queryCalls(queryCallsReq);

// loading next page
client.video.queryCalls({
...queryCallsReq,
next: response.next,
});

Query live calls

client.video.queryCalls({
filter_conditions: { backstage: { $eq: false } },
});

Query upcoming calls

const mins30 = 1000 * 60 * 60 * 30;
const inNext30mins = new Date(Date.now() + mins30);
client.video.queryCalls({
filter_conditions: {
starts_at: { $gt: inNext30mins.toISOString() },
},
});

Query ongoing calls

client.video.queryCalls({
filter_conditions: { ongoing: { $eq: true } },
});

Sort options

Sorting is supported on these fields:

  • starts_at
  • created_at
  • updated_at
  • ended_at
  • type
  • id
  • cid

Filter options

NameTypeDescriptionSupported operators
idstringCall ID$in, $eq, $gt, $gte, $lt, $lte, $exists
cidstringCall CID (format: type:id)$in, $eq, $gt, $gte, $lt, $lte, $exists
teamstringThe team associated with the channel$in, $eq, $gt, $gte, $lt, $lte, $exists
typestringCall type$in, $eq, $gt, $gte, $lt, $lte, $exists
created_by_user_idstringUser ID of the call's creator$in, $eq, $gt, $gte, $lt, $lte, $exists
created_atstring, must be formatted as an RFC3339 timestamp (for example 2021-01-15T09:30:20.45Z)Creation time of the call$in, $eq, $gt, $gte, $lt, $lte, $exists
updated_atstring, must be formatted as an RFC3339 timestamp (for example 2021-01-15T09:30:20.45Z)The time of the last update of the call$in, $eq, $gt, $gte, $lt, $lte, $exists
ended_atstring, must be formatted as an RFC3339 timestamp (for example 2021-01-15T09:30:20.45Z)The time the call was ended$in, $eq, $gt, $gte, $lt, $lte, $exists
starts_atstring, must be formatted as an RFC3339 timestamp (for example 2021-01-15T09:30:20.45Z)The scheduled start time of the call$in, $eq, $gt, $gte, $lt, $lte, $exists
backstagebooleantrue if the call is in backstage mode$eq
membersstring arrayMember user ids$in
ongoingbooleantrue if there is an ongoing session for the call$eq

The Stream API allows you to specify filters and ordering for several endpoints. The query syntax is similar to that of Mongoose, however we do not run MongoDB on the backend. Only a subset of the MongoDB operations are supported.

NameDescriptionExample
$eqMatches values that are equal to a specified value.{ "key": { "$eq": "value" } } or the simplest form { "key": "value" }
$qFull text search (matches values where the whole text value matches the specified value){ "key": { "$q": "value } }
$gtMatches values that are greater than a specified value.{ "key": { "$gt": 4 } }
$gteMatches values that are greater than or equal to a specified value.{ "key": { "$gte": 4 } }
$ltMatches values that are less than a specified value.{ "key": { "$lt": 4 } }
$lteMatches values that are less than or equal to a specified value.{ "key": { "$lte": 4 } }
$inMatches any of the values specified in an array.{ "key": { "$in": [ 1, 2, 4 ] } }
$existsMathces values that either have (when set to true) or not have (when set to false) certain attributes{ "key": { "$exists": true } }
$autocompleteMathces values that start with the specified string value{ "key": { "$autocomplete": "value" } }

It's also possible to combine filter expressions with the following operators:

NameDescriptionExample
$andMatches all the values specified in an array.{ "$and": [ { "key": { "$in": [ 1, 2, 4 ] } }, { "some_other_key": 10 } ] }
$orMatches at least one of the values specified in an array.{ "$or": [ { "key": { "$in": [ 1, 2, 4 ] } }, { "key2": 10 } ] }

Query call members

// default sorting
call.queryMembers();

// sorting and pagination
const queryMembersReq = {
sort: [{ field: 'user_id', direction: 1 }],
limit: 2,
};
const response = await call.queryMembers(queryMembersReq);

// loading next page
call.queryMembers({
...queryMembersReq,
next: response.next,
});

// filtering
call.queryMembers({
filter_conditions: { role: { $eq: 'admin' } },
});

Sort options

Sorting is supported on these fields:

  • user_id
  • created_at

Filter options

NameTypeDescriptionSupported operators
user_idstringUser ID$in, $eq, $gt, $gte, $lt, $lte, $exists
rolestringThe role of the user$in, $eq, $gt, $gte, $lt, $lte, $exists
customObjectSearch in custom user data$in, $eq, $gt, $gte, $lt, $lte, $exists
created_atstring, must be formatted as an RFC3339 timestamp (for example 2021-01-15T09:30:20.45Z)Creation time of the user$in, $eq, $gt, $gte, $lt, $lte, $exists
updated_atstring, must be formatted as an RFC3339 timestamp (for example 2021-01-15T09:30:20.45Z)The time of the last update of the user$in, $eq, $gt, $gte, $lt, $lte, $exists

The Stream API allows you to specify filters and ordering for several endpoints. The query syntax is similar to that of Mongoose, however we do not run MongoDB on the backend. Only a subset of the MongoDB operations are supported.

NameDescriptionExample
$eqMatches values that are equal to a specified value.{ "key": { "$eq": "value" } } or the simplest form { "key": "value" }
$qFull text search (matches values where the whole text value matches the specified value){ "key": { "$q": "value } }
$gtMatches values that are greater than a specified value.{ "key": { "$gt": 4 } }
$gteMatches values that are greater than or equal to a specified value.{ "key": { "$gte": 4 } }
$ltMatches values that are less than a specified value.{ "key": { "$lt": 4 } }
$lteMatches values that are less than or equal to a specified value.{ "key": { "$lte": 4 } }
$inMatches any of the values specified in an array.{ "key": { "$in": [ 1, 2, 4 ] } }
$existsMathces values that either have (when set to true) or not have (when set to false) certain attributes{ "key": { "$exists": true } }
$autocompleteMathces values that start with the specified string value{ "key": { "$autocomplete": "value" } }

It's also possible to combine filter expressions with the following operators:

NameDescriptionExample
$andMatches all the values specified in an array.{ "$and": [ { "key": { "$in": [ 1, 2, 4 ] } }, { "some_other_key": 10 } ] }
$orMatches at least one of the values specified in an array.{ "$or": [ { "key": { "$in": [ 1, 2, 4 ] } }, { "key2": 10 } ] }

Send custom event

It's possible to send any custom event for a call:

// the custom event can be any kind of data
await call.sendCustomEvent({
type: 'draw',
x: 10,
y: 30,
});

Sending a custom event will dispatch the custom WebSocket event.

Pin and unpin video

You can pin the video of a participant in a call session. The SDKs will make sure that the pinned participant is displayed in a prominent location in the call layout for all participants. You can also unpin a pinned participant if you no longer want to highlight them.

await call.pinVideo({
session_id: 'session-id',
user_id: 'user-id-to-unpin',
});

await call.unpinVideo({
session_id: 'session-id',
user_id: 'user-id-to-pin',
});

Did you find this page helpful?