Stream’s global edge network ensures a faster and more reliable experience for your video calls and livestreams. Excellent developer experience and docs enable you to build in-app video calling in days. Scale to millions of users and thousands of call participants.
For the average Stream integration, the development work focuses on code that executes in the client. However, some tasks must be executed from the server for safety (for example, token generation).
Stream provides server-side SDKs to help execute these tasks.
You can reference our development roadmap to know which languages and features are supported.
import io.getstream.services.framework.StreamSDKClient;// default client reads credentials from properties:// "io.getstream.apiKey"// "io.getstream.apiSecret"// "io.getstream.timeout"// or from environment variables:// "STREAM_API_KEY"// "STREAM_API_SECRET"var client = new StreamSDKClient();// or init client with apiKey and secretvar client = new StreamSDKClient("your_api_key", "your_api_secret");
# Create a server tokenHEADER=$(echo -n '{"alg": "HS256", "typ": "JWT"}' | openssl base64 -e -A | tr '+/' '-_' | tr -d '=');PAYLOAD=$(echo -n '{"server": true}' | openssl base64 -e -A | tr '+/' '-_' | tr -d '=');SECRET='<API secret>';SIGNATURE=$(echo -n ${HEADER}.${PAYLOAD} | openssl dgst -sha256 -hmac ${SECRET} -binary | openssl base64 -e -A | tr '+/' '-_' | tr -d '=');TOKEN="${HEADER}.${PAYLOAD}.${SIGNATURE}";API_KEY='<API key>';# Provide API key, token and auth header to all requestscurl -X GET "https://video.stream-io-api.com/api/v2/app?api_key=${API_KEY}" \ -H "Authorization: ${TOKEN}" \ -H "stream-auth-type: jwt"
The Node SDK uses the built-in Fetch API to create HTTP requests. The Fetch API uses a default connection pool that doesn't have a cap on the maximum connections. If you want to limit this, or do any other configuration, you can do it by installing the undici library.
# For Node 20.18.1+npm install undici@7# For Node 18.17+npm install undici@6# For Node 18+npm install undici@5
You can provide your own custom Agent to the StreamClient:
import { Agent } from "undici";const client = new StreamClient(apiKey, appSecret, { agent: new Agent({ connections: 100 }),});
To create a user you need to provide an ID, and their role. Optionally you can also specify their name and an image, these fields are recognized and rendered by the default SDK components. It's also possible to add any other custom data.
Tokens need to be generated server-side. Typically, you integrate this into the part of your codebase where you log in or register users. The tokens provide a way to authenticate a user or give access to a specific set of video/audio calls.
const userId = 'john';const newUser: UserRequest = { id: userId, role: 'user', custom: { color: 'red', }, name: 'John', image: 'link/to/profile/image',};await client.upsertUsers([newUser]);// validity is optional (by default the token is valid for an hour)const vailidity = 60 * 60;client.generateUserToken({ user_id: userId, validity_in_seconds: validity });
from getstream.models import UserRequest# ensure the user existsclient.upsert_users( UserRequest( id="tommaso-id", name="tommaso", role="admin", custom={"country": "NL"} ),)# the token will be valid for 1 hourclient.create_token(user_id="tommaso-id", expiration=3600)
// optional values are passed as pointers, you can use `getstream.PtrTo`// to get pointers from literals of any typeresponse, err := client.UpdateUsers(ctx, &getstream.UpdateUsersRequest{ Users: map[string]getstream.UserRequest{ "user-id": { ID: "user-id", Name: getstream.PtrTo("tommaso"), Custom: map[string]any{"country": "NL"}, }, },})// create a user token valid for 24 hourstoken, err := client.CreateToken("tommaso-id", getstream.WithExpiration(24*time.Hour))
import io.getstream.models.*;import io.getstream.models.framework.User;// upserting a userclient.updateUsers( UpdateUsersRequest.builder() .users( Map.of( "john", UserRequest.builder() .id("john") .name("john") .custom(Map.of("country", "NL")) .build() ) ) .build()).execute();// create a user token valid for 24 hoursclient.tokenBuilder().createToken("john", 24 * 60 * 60);
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 can be used once or multiple times depending on your app. Unless you want to re-use the same call multiple times, the recommended way to pick a call ID is to use a uuid v4 so that each call gets a unique random ID.
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.
import uuidfrom getstream.models.call_request import CallRequestcall = client.video.call("default", uuid.uuid4())call.create( data=CallRequest( created_by_id="sacha", ),)# optionally provide additional datacall.create( data=CallRequest( created_by_id="sacha", # note: you can add users as members to calls to support more complex permissions members=[ MemberRequest(user_id="john", role="admin"), MemberRequest(user_id="jack"), ], custom={"color": "blue"}, ),)
Call types provide sensible default settings for different use-cases. We provide the following types out of the box:
Default (default) for 1:1 or group calls that use both video and audio
Livestreaming (livestream) to build ultra low latency livestreaming for your app on our global edge network. Broadcast from your phone or RTMP and scale to millions of participants.
Audio room (audio_room) to build audio experiences for your app. You can build basic calling or feature rich experience like Twitter spaces. Audio quality, reliability and scalability is far ahead of competing solutions.
Development (development) This call type comes with almost all permission settings disabled so that it is simpler to get your initial implementation up and running. You should only use this call type early-on during development.
You can provide a list of call members, this can be done when you create a call or later on when the call already exists. Please note that call members need to be existing users.
There are two reasons to use call members:
Call membership allows you to have more flexibility when it comes to permissions. The permission system allows you to grant different permissions to users and members, this way one user can be a member on one call or a member on another.
Membership also allows you to grant additional roles to users in a call. It's important to note that this doesn't restrict access, but rather expands capabilities.
You can more information about the roles and permissions here.
// Call members need to be existing userscall.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" }],});
# Call members need to be existing users# You can also update the role of existing memberscall.update_call_members( update_members=[ MemberRequest(user_id="sara"), MemberRequest(user_id="emily", role="admin"), ])
// Call members need to be existing users (use `client.UpdateUsers` for that)// You can also update the role of existing membersresponse, err := call.UpdateCallMembers(ctx, &getstream.UpdateCallMembersRequest{ UpdateMembers: []getstream.MemberRequest{ {UserID: "sara"}, {UserID: "emily", Role: getstream.PtrTo("admin")}, },})
// Call members need to be existing users// You can also update the role of existing membersmembers = new ArrayList<MemberRequest>() { { add(MemberRequest.builder().userID("john").role("admin").build()); add(MemberRequest.builder().userID("jane").build()); add(MemberRequest.builder().userID("tom").build()); }};call.updateCallMembers(UpdateCallMembersRequest.builder().updateMembers(members).build());
# You can only add existing members to a callcurl -X POST "https://video.stream-io-api.com/api/v2/video/call/${CALL_TYPE}/${CALL_ID}/members?api_key=${API_KEY}" \ -H "Authorization: ${TOKEN}" \ -H "Content-Type: application/json" \ -H "stream-auth-type: jwt" \ -d '{ "update_members": [ { "user_id": "sara" }, { "user_id": "john", "role": "admin" } ] }'
from getstream.models import CallSettingsRequest# update some custom data for this callcall.update(custom={'color': 'red'})# update settings for this callcall.update( settings_override=CallSettingsRequest( screensharing=ScreensharingSettingsRequest( enabled=True, access_request_enabled=True ), ),)
// update some custom data for this callresponse, err := call.Update(ctx, &getstream.UpdateCallRequest{ Custom: map[string]any{"color": "red"},})// update settings for this callresponse, err = call.Update(ctx, &getstream.UpdateCallRequest{ SettingsOverride: &getstream.CallSettingsRequest{ Screensharing: &getstream.ScreensharingSettingsRequest{ Enabled: getstream.PtrTo(true), AccessRequestEnabled: getstream.PtrTo(true), }, },})