// shows the current status
const appSettings = await client.getApp();
console.log(appSettings.app.multi_tenant_enabled);
// enables teams
client.updateApp({
multi_tenant_enabled: true,
});
Multi-Tenant & Teams
Stream Video can be configured for teams/multi-tenant operation, allowing users to be organized into distinct, isolated teams. This configuration is essential for applications like Zoom or Disqus, where it’s crucial that users within one team remain completely separated from others to ensure privacy and security.
Teams
Stream Video has the concept of teams for users and calls. The purpose of teams is to provide a simple way to separate different groups of users and calls within a single application.
If a user belongs to a team, the API will ensure that such user will only be able to connect to calls from the same team. Features such as user search are limited so that a user can only search for users from the same team by default.
When enabling multi-tenant mode all user requests will always ensure that the request applies to a team the user belongs to. For instance, if a user from team “blue” tries to delete a message that was created on a channel from team “red” the API will return an error. If user doesn’t have team set, it will only have access to users and channels that don’t have team.
Enable Teams for your application
In order to use Teams, your application must have multi-tenant mode enabled. You can enable multi-tenant from the dashboard (Overview screen) or using the API.
# shows the current status
print(client.get_app().data.app.multi_tenant_enabled)
# enables teams
client.update_app(multi_tenant_enabled=True)
res, _ := client.GetApp(ctx, &getstream.GetAppRequest{})
fmt.Println(res.Data.App.MultiTenantEnabled)
// enables teams
client.UpdateApp(ctx, &getstream.UpdateAppRequest{
MultiTenantEnabled: getstream.PtrTo(true),
})
# Check status
curl -X GET "https://video.stream-io-api.com/api/v2/app?api_key=${API_KEY}" \
-H "Authorization: ${TOKEN}" \
-H "stream-auth-type: jwt"
# Enable multi tenant
curl -X PATCH "https://video.stream-io-api.com/api/v2/app?api_key=${API_KEY}" \
-H "Authorization: ${TOKEN}" \
-H "stream-auth-type: jwt" \
-H "Content-Type: application/json" \
-d '{
multi_tenant_enabled: true,
}'
Please keep in mind that enabling/disabling multi-tenant changes permission checking for your users. Do not change this on a production app without testing that your integration supports it correctly.
User teams
When multi-tenant is enabled, users can only be created from your back-end. This is necessary to ensure that a user cannot pick its own team/tenant.
client.upsertUsers([
{
id: '<user id>',
name: 'Sara',
teams: ['red', 'blue'],
},
]);
client.upsert_users(
UserRequest(id=user_id, teams=["red", "blue"])
)
client.UpdateUsers(ctx, &getstream.UpdateUsersRequest{
Users: map[string]getstream.UserRequest{
"john": {
ID: "john",
Teams: []string{
"red",
"blue",
},
},
},
})
curl -X POST https://video.stream-io-api.com/api/v2/users?api_key=${API_KEY} \
-H "Authorization: ${TOKEN}" \
-H "stream-auth-type: jwt" \
-H "Content-Type: application/json" \
-d '{
"users": {
"john": {
"id": "john",
"teams": ["red", "blue"]
}
}
}'
A user can be a member of up to 250 teams, a team name can be up to 100 characters. There is no limit to how many teams your application can have.
Call team
Calls can be associated with a team. Users can create calls client-side but if their user is part of a team, they will have to specify a team or the request will be rejected with an error.
client.call('default', 'red-team-weekly').create({
data: { team: 'red', created_by_id: '<user_id>' },
});
call = client.video.call("default", call_id)
response = call.create(
data=CallRequest(
created_by_id=user_id,
team="blue",
)
)
call = client.Video().Call("default", "callID")
response, err := call.GetOrCreate(ctx, &getstream.GetOrCreateCallRequest{
Data: &getstream.CallRequest{
Team: getstream.PtrTo("blue"),
CreatedByID: getstream.PtrTo("<user_id>"),
},
})
curl -X POST "https://video.stream-io-api.com/api/v2/video/call/${CALL_TYPE}/${CALL_ID}?api_key=${API_KEY}" \
-H "Authorization: ${TOKEN}" \
-H "stream-auth-type: jwt" \
-H "Content-Type: application/json" \
-d '{
"data": {
"created_by_id": "sacha@getstream.io",
"team": "blue"
}
}'
Call teams allow you to ensure proper permission checking for a multi tenant application. Keep in mind that you will still need to enforce that call IDs are unique. There are two common way to address this: generate random IDs using a random UUID, prefix the team name to the id of the call.
User search
By default client-side user search will only return results from teams that authenticated user is a part of. API injects filter {team: {$in: [<user_teams>]}}
for every request that doesn’t already contain filter for teams field. If you want to query users from all teams, you have to provide empty filter like this: {teams:{}}
.
For server-side requests, this filter does not apply and you can search as usual and also filter by teams.
// search for users by name and team
client.queryUsers({
payload: {
filter_conditions: {
name: 'Nick',
teams: { $in: ['red', 'blue'] },
},
},
});
// search for users that are not part of any team
client.queryUsers({
payload: {
filter_conditions: {
name: 'Tom',
teams: null,
},
},
});
# search for users by name and team
response = client.query_users(
QueryUsersPayload(
filter_conditions={
"name": {"$eq": "Nick"},
"teams": {"$in": ["red", "blue"]},
}
)
)
# search for users that are not part of any team
response = client.query_users(
QueryUsersPayload(
filter_conditions={
"teams": None,
}
)
)
response, err := client.QueryUsers(ctx, &getstream.QueryUsersRequest{
Payload: &getstream.QueryUsersPayload{
FilterConditions: map[string]interface{}{
"name": "Nick",
"teams": map[string]interface{}{"$in": []string{"red", "blue"}},
},
},
})
// search for users that are not part of any team
response, err = client.QueryUsers(ctx, &getstream.QueryUsersRequest{
Payload: &getstream.QueryUsersPayload{
FilterConditions: map[string]interface{}{
"teams": nil,
},
},
})
# Search for users by name and team
PAYLOAD='{"filter_conditions": {"name": "Nick", "teams": { "$in": ["red", "blue"] }}}';
ENCODED_PAYLOAD=$(echo ${PAYLOAD} | perl -MURI::Escape -lne 'print uri_escape($_)')
curl -X GET "https://video.stream-io-api.com/api/v2/users?api_key=${API_KEY}&payload=${ENCODED_PAYLOAD}" \
-H "Authorization: ${TOKEN}" \
-H "stream-auth-type: jwt"
Search for users that are not part of any team
PAYLOAD='{"filter_conditions": {"name": "Tom", "teams": null}}';
ENCODED_PAYLOAD=$(echo ${PAYLOAD} | perl -MURI::Escape -lne 'print uri_escape($_)')
curl -X GET "https://video.stream-io-api.com/api/v2/users?api_key=${API_KEY}&payload=${ENCODED_PAYLOAD}" \
-H "Authorization: ${TOKEN}" \
-H "stream-auth-type: jwt"
Query Calls
When using multi-tenant, the client-side query calls endpoint will only return calls that match the query and are on the same team as the authenticated user. The API injects filter {team: {$in: [<user_teams>]}}
for every request that doesn’t already contain filter for team field. If you want to query channels from all teams, you have to provide empty filter like this: {team:{}}
.
For server-side requests, this filter does not apply and you can search as usual and also filter by teams.
// All calls
client.video.queryCalls();
// Calls without team
client.video.queryCalls({
filter_conditions: {
team: null,
},
});
// Calls with specific team
client.video.queryCalls({
filter_conditions: {
team: 'blue',
},
});
# query calls by team field
response = client.video.query_calls(
filter_conditions={"id": call_id, "team": {"$eq": "blue"}}
)
# retrieve calls without a team
response = client.video.query_calls(
filter_conditions={"id": call_id, "team": {"$eq": "blue"}}
)
response, err := client.Video().QueryCalls(ctx, &getstream.QueryCallsRequest{
FilterConditions: &map[string]interface{}{
"id": call_id,
"team": map[string]interface{}{"$eq": "blue"},
},
})
// retrieve calls without a team
response, err := client.Video().QueryCalls(ctx, &getstream.QueryCallsRequest{
FilterConditions: &map[string]interface{}{
"id": call_id,
"team": &map[string]interface{}{"$eq": nil},
},
})
# All calls
curl -X POST "https://video.stream-io-api.com/api/v2/video/calls?api_key=${API_KEY}" \
-H "Authorization: ${TOKEN}" \
-H "stream-auth-type: jwt"
# Calls without team
curl -X POST "https://video.stream-io-api.com/api/v2/video/calls?api_key=${API_KEY}" \
-H "Authorization: ${TOKEN}" \
-H "stream-auth-type: jwt" \
-H "Content-Type: application/json" \
-d '{"filter_conditions": {"team": null}}'
# Calls with a specific team
curl -X POST "https://video.stream-io-api.com/api/v2/video/calls?api_key=${API_KEY}" \
-H "Authorization: ${TOKEN}" \
-H "stream-auth-type: jwt" \
-H "Content-Type: application/json" \
-d '{"filter_conditions": {"team": "blue"}}'