The Stream user object is central to every product and appears in many API responses, effectively following the user throughout the platform. Only an id is required to create a user but you can store additional custom data. We recommend only storing what is necessary, such as a username and image URL.
The operations performed on users, such as updating and deleting, have an effect on all products (chat, video and feeds).
When creating users, there are a few important things to keep in mind:
The id field is mandatory, in most cases you want this to be the same ID you use on your database.
The role field is optional, by default it is set to user but you can specify any existing role.
Custom data can be added to users in the custom field.
name and image are optional and handled by all SDKs automatically to render users.
Users are also created on the fly when they connect from a client-side SDK, with the user role. However, it is also common to add your users to Stream before going live and keep properties of your user base in sync. For this you'll want to use the upsertUsers function server-side and send users in bulk.
The upsertUser method creates or updates a user, replacing its existing data with the new payload (see below for partial updates). To create or update users in batches of up to 100, use the upsertUsers or partialUpdateUsers APIs, which accept an array of user objects.
If you need to update a subset of properties for a user(s), you can use a partial update method. Both set and unset parameters can be provided to add, modify, or remove attributes to or from the target user(s). The set and unset parameters can be used separately or combined.
Clients can set a username, by setting the name custom field. The field is optional and by default has no uniqueness constraints applied to it, however this is configurable by setting the enforce_unique_username to either app or team.
When checking for uniqueness, the name is normalized, by removing any white-space or other special characters, and finally transforming it to lowercase. So "John Doe" is considered a duplicate of "john doe", "john.doe", etc.
With the setting at app, creating or updating a user fails if the username already exists anywhere in the app. With team, it only fails if the username exists within the same team.
// Enable uniqueness constraints on App levelawait client.updateAppSettings({ enforce_unique_usernames: "app",});// Enable uniqueness constraints on Team levelawait client.updateAppSettings({ enforce_unique_usernames: "team",});
# Enable uniqueness constraints on App levelclient.update_app(enforce_unique_usernames="app")# Enable uniqueness constraints on Team levelclient.update_app(enforce_unique_usernames="team")
Models = GetStream::Generated::Models# Enable uniqueness constraints on App levelclient.common.update_app( Models::UpdateAppRequest.new(enforce_unique_usernames: 'app'))# Enable uniqueness constraints on Team levelclient.common.update_app( Models::UpdateAppRequest.new(enforce_unique_usernames: 'team'))
use GetStream\GeneratedModels as Models;// Enable uniqueness constraints on App level$client->updateApp(new Models\UpdateAppRequest( enforceUniqueUsernames: "app",));// Enable uniqueness constraints on Team level$client->updateApp(new Models\UpdateAppRequest( enforceUniqueUsernames: "team",));
// Enable uniqueness constraints on App levelresp, err := client.UpdateApp(ctx, &getstream.UpdateAppRequest{ EnforceUniqueUsernames: getstream.PtrTo("app"),})// Enable uniqueness constraints on Team levelresp, err = client.UpdateApp(ctx, &getstream.UpdateAppRequest{ EnforceUniqueUsernames: getstream.PtrTo("team"),})
// dotnet add package getstream-netusing GetStream;using GetStream.Models;var client = new StreamClient("{{ api_key }}", "{{ api_secret }}");// Enable uniqueness constraints on App levelawait client.UpdateAppAsync(new UpdateAppRequest{ EnforceUniqueUsernames = "app"});// Enable uniqueness constraints on Team levelawait client.UpdateAppAsync(new UpdateAppRequest{ EnforceUniqueUsernames = "team"});
// Enable uniqueness constraints on App levelclient.updateApp(UpdateAppRequest.builder() .enforceUniqueUsernames("app") .build()).execute();// Enable uniqueness constraints on Team levelclient.updateApp(UpdateAppRequest.builder() .enforceUniqueUsernames("team") .build()).execute();
Enabling this setting will only enforce the constraint going forward and will not try to validate existing usernames.
You can query and filter users using the queryUsers endpoint. This allows you to search for users based on various criteria such as custom fields, roles, and other user properties.
All filters use a Mongoose-style syntax; however, we do not run MongoDB on the backend, so only a subset of Mongoose queries are supported. The supported filters are described below.
Query Users runs on an indexed user database. To keep response times predictable for apps of every size, filters that cannot use an index are rejected with a 400 error.
Custom fields only support exact matches. Custom properties are stored in a JSON column, and the index on that column only supports exact-match lookups. Range operators such as $gt, $gte, $lt, and $lte (as well as $in, $exists, and $contains) on a custom field cannot use the index and would require scanning every user in your app, so they are not allowed. If you need range queries on a field, consider using one of the built-in indexed fields (such as created_at, updated_at, or last_active), or query your own database instead.
Not-equal operators are only allowed on id and boolean fields. The $ne, $nin, and $nor operators are only supported on the id field (and $ne on boolean fields such as banned). On other fields these negative filters prevent the database query planner from using an index: instead of seeking directly to matching rows, it has to walk through everything that does not match, which can result in slow queries and timeouts.
You can usually rewrite a negative filter as a positive one. For example, instead of role: { $ne: "admin" }, query for the roles you do want: role: { $eq: "user" } or role: { $in: ["user", "moderator"] }. Positive filters are index-backed and fast.
You can use the last_active field with the $exists operator to find users who have never connected. Use $exists: false for users who have never been active, or $exists: true for users who have connected at least once.
Depending on your use case, you can choose to delete users or deactivate them. There are some differences between these two approaches.
Deactivating users:
the user will not be allowed to perform API requests / connect
user data is retained on Stream's side and returned from API
deactivated users can be re-activated
Deleting users:
the user will no longer be able to perform API requests / connect
the user is deleted and, by default, not returned from API
all data from the user is marked as deleted
by default, the data is retained and "soft" deleted; you can optionally request hard deletion
deletion is not reversible
Both bulk deletion and bulk deactivation are performed asynchronously by the Stream API. A task ID is returned and you can use that to check the status of its processing.
client.deactivateUser({ user_id: '<id>',});// reactivateclient.reactivateUsers({ user_ids: ['<id>'],});// deactivating users in bulk is performed asynchronouslyconst deactivateResponse = client.deactivateUsers({ user_ids: ['<id1>', '<id2>'...],});
# Deactivating a single userclient.deactivate_user(user_id=user_id)# Deactivating users in bulk is performed asynchronouslyresponse = client.deactivate_users( user_ids=[user_id, user_id_2])# Reactivate usersclient.reactivate_users( user_ids=[user_id, user_id_2])
// Deactivating a single user$client->deactivateUser( 'user1', new GeneratedModels\DeactivateUserRequest());// Deactivating users in bulk is performed asynchronously$response = $client->deactivateUsers( new GeneratedModels\DeactivateUsersRequest( userIds: ['user1', 'user2'] ));// Reactivate users$client->reactivateUsers( new GeneratedModels\ReactivateUsersRequest( userIds: ['user1', 'user2'] ));
// Deactivating a single user_, err = client.DeactivateUser(context.Background(), "user1", &getstream.DeactivateUserRequest{})// Deactivating users in bulk is performed asynchronously_, err = client.DeactivateUsers(context.Background(), &getstream.DeactivateUsersRequest{ UserIds: []string{"user2"},})// Reactivate users_, err = client.ReactivateUsers(context.Background(), &getstream.ReactivateUsersRequest{ UserIds: []string{"user1", "user2"},})
// Deactivating a single userawait client.DeactivateUserAsync(userId, new DeactivateUserRequest());// Deactivating users in bulk is performed asynchronouslyvar deactivateRequest = new DeactivateUsersRequest{ UserIds = new[] { userId, userId2 }};await client.DeactivateUsersAsync(deactivateRequest);// Reactivate usersvar reactivateRequest = new ReactivateUsersRequest{ UserIds = new[] { userId, userId2 }};await client.ReactivateUsersAsync(reactivateRequest);
// Deactivating a single userclient.deactivateUser(userId, DeactivateUserRequest.builder().build()).execute();// Deactivating users in bulk is performed asynchronouslyDeactivateUsersRequest deactivateRequest = DeactivateUsersRequest.builder() .userIds(List.of(userId, userId2)) .build();client.deactivateUsers(deactivateRequest).execute();// Reactivate usersReactivateUsersRequest reactivateRequest = ReactivateUsersRequest.builder() .userIds(List.of(userId, userId2)) .build();client.reactivateUsers(reactivateRequest).execute();
Deactivating users in bulk can take some time. Monitor the returned task ID as described in Async operations.
You can delete up to 100 users per call. The user parameter controls what happens to the user object itself:
Name
Type
Description
Default
Optional
user
enum (soft, pruning, hard)
Soft: marks user as deleted and retains all user data. Pruning: marks user as deleted and nullifies user information. Hard: deletes user completely.
soft
✓
Each product adds parameters that control what happens to the user's product data, such as their messages, channels and calls. These are documented with each product: see deleting many users in chat and deleting users in video.
Exporting or deleting user data to meet compliance requests is covered on GDPR and privacy.
If users are soft deleted, they can be restored using the server-side client. However, only the user's metadata is restored; product data such as memberships, messages and reactions is not restored.