val user = User(userId)
chatClient.updateUser(user).enqueue {
val user = it.data()
}
Managing Users
Stream Users require only an id
to be created. Users can be created with role of user
or admin
. The role
will be set to user
if a value is not provided in the request. There are additional properties you can provide to further describe your users.
If you wish to have users join a chat before they have created an account, check out our Anonymous User support
Client-side User Creation
Stream Chat exposes a connectUser
method that automatically creates and updates the user. Please note that the connectUser
call has some limitations:
For example, if you’re looking to sync your user base, you’ll want to use the upsertUsers
function server-side and send users in bulk.
Server-side User Updates (Batch)
The upsertUsers
method creates or updates users, and accepts batch of users in the form of an array of user objects. Any user present in the payload will have its data replaced with the new version. (see below for partial updates) You can send up to 100 users per API request in both upsertUsers
and partialUpdateUser
.
For example, a client authenticated as a user with permission to create new users or using serverside authentication can create or update a new user:
const updateResponse = await chatClient.upsertUser({
id: userID,
role: 'admin',
book: 'dune'
});
// user object is now {id: userID, role: 'admin', book: 'dune'}
# user object is now {id: userID, role: 'admin', book: 'dune'}
client.upsert_users([{"id": user_id, "role": "admin", "book": "dune"}])
// user object is now {id: userID, role: 'admin', book: 'dune'}
client.UpsertUsers(ctx, &User{
ID: userID, Role: "admin", ExtraData: map[string]interface{}{"book": "dune"}})
var user = new UserRequest
{
Id = "bob-1",
Role = Role.Admin,
};
user.SetData("book", "dune");
// user object is now {id: userID, role: 'admin', book: 'dune'}
await userClient.UpsertManyAsync(new[] { user });
await chatClient.upsertUser(user);
const updateResponse = await serverClient.upsertUser({
id: userID,
role: 'admin',
book: 'dune'
});
# user object is now {id: userID, role: 'admin', book: 'dune'}
serverClient.upsert_users([{:id => user_id, :role => "admin", :book => "dune"}])
var user = UserRequestObject.builder().id(userId1).role("admin").build();
User.upsert().user(user).request();
Client->UpsertUsers({User});
// Only Id field is required, the rest is optional
var createOrUpdateUser = new StreamUserUpsertRequest
{
Id = "my-user-id",
// BanExpires = DateTimeOffset.Now.AddDays(7),
// Banned = true,
// Invisible = true,
// Role = "user",
// Name = "David",
// Image = "image-url", // You can upload image to Stream CDN or your own
// CustomData = new StreamCustomDataRequest
//{
// { "Age", 24 },
// { "Passions", new string[] { "Tennis", "Football", "Basketball" } }
//}
};
// Upsert means: update user with a given ID or create a new one if it doesn't exist
var users = await Client.UpsertUsers(new[] { createOrUpdateUser });
The upsertUser
(server-side) method has permission to make a user an admin; however, the connectUser
(client-side) method does not have permission. This is because the connectUser
method is called client-side and it is unsecure to edit user permissions in the front-end.
And for a batch of users, simply add additional entries (up to 100) into the array you pass to upsertUsers
:
const updateResponse = await serverClient.upsertUsers([
{ id: userID1, role: 'admin', book: 'dune'},
{ id: userID2, role: 'user', book: '1984'},
{ id: userID3, role: 'admin', book: 'Fahrenheit 451'}
]);
// each user object is updated accordingly
client.upsert_users([
{"id": user_id1, "role": "admin", "book": "dune"},
{"id": user_id2, "role": "user", "book": "1984"},
{"id": user_id3, "role": "admin", "book": "Fahrenheit 451"},
])
client.UpsertUsers(ctx,
&User{ID: userID1, Role: "admin", ExtraData: map[string]interface{}{"book": "dune"}},
&User{ID: userID2, Role: "user", ExtraData: map[string]interface{}{"book": "1984"}},
&User{ID: userID3, Role: "admin", ExtraData: map[string]interface{}{"book": "Fahrenheit 451"}},
)
await userClient.UpsertManyAsync(new[] {
new UserRequest
{
Id = userId1,
Role = Role.Admin,
},
new UserRequest
{
Id = userId2,
Role = Role.User,
},
new UserRequest
{
Id = userId3,
Role = Role.Admin,
}
});
client.upsert_users([
{:id => user_id1, :role => "admin", :book => "dune"},
{:id => user_id2, :role => "user", :book => "1984"},
{:id => user_id3, :role => "admin", :book => "Fahrenheit 451"},
])
var user1 = UserRequestObject.builder().id(userId1).role("admin").build();
var user2 = UserRequestObject.builder().id(userId1).role("admin").build();
var user3 = UserRequestObject.builder().id(userId1).role("admin").build();
User.upsert().user(user1).user(user2).user(user3).request();
var usersToCreateOrUpdate = new[]
{
new StreamUserUpsertRequest
{
Id = "my-user-id",
Role = "user",
},
new StreamUserUpsertRequest
{
Id = "my-user-id-2",
// BanExpires = DateTimeOffset.Now.AddDays(7),
// Banned = true,
// Invisible = true,
// Role = "user",
// Name = "David",
// Image = "image-url", // You can upload image to Stream CDN or your own
// CustomData = new StreamCustomDataRequest
//{
// { "Age", 24 },
// { "Passions", new string[] { "Tennis", "Football", "Basketball" } }
//}
},
};
// Upsert means: update user with a given ID or create a new one if it doesn't exist
var users = await Client.UpsertUsers(usersToCreateOrUpdate);
If any user in a batch of users contains an error, the entire batch will fail, and the first error encountered will be returned.
Server-side Partial Update (Batch)
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.
Please see below for an example:
// make partial update call for userID
// it set's user.role to "admin", sets user.field = {'text': 'value'}
// and user.field2.subfield = 'test'.
//
// NOTE:
// changing role is available only for server-side auth.
// field name should not contain dots or spaces, as dot is used as path separator.
const update = {
id: "userID",
set: {
role: "admin",
field: {
text: 'value'
},
'field2.subfield': 'test',
},
unset: ['field.unset'],
};
// response will contain user object with updated users info
const response = await client.partialUpdateUser(update);
// partial update for multiple users
const updates = [{
id: "userID",
set: {
field: "value"
}
}, {
id: "userID2",
unset: ["field.value"],
}];
// response will contain object {userID => user} with updated users info
const response = await client.partialUpdateUsers(updates);
# make partial update call for userID
# it set's user.role to "admin", sets user.field = {'text': 'value'}
# and user.field2.subfield = 'test'.
# NOTE:
# changing role is available only for server-side auth.
# field name should not contain dots or spaces, as dot is used as path separator.
update = {
"id": "userID",
"set": {
"role": "admin",
"field": {
"text": 'value'
},
'field2.subfield': 'test',
},
"unset": ['field.unset'],
};
# response will contain user object with updated users info
client.update_user_partial(update);
# partial update for multiple users
updates = [
{
"id": "userID",
"set": {"field": "value"}
},
{
"id": "userID2",
"unset": ["field.value"]
}
]
# response will contain object {userID => user} with updated users info
client.update_users_partial(updates)
# make partial update call for userID
# it set's user.role to "admin", sets user.field = {'text': 'value'}
# and user.field2.subfield = 'test'.
# NOTE:
# changing role is available only for server-side auth.
# field name should not contain dots or spaces, as dot is used as path separator.
update = {
"id" => "userID",
"set"=> {
"role"=> "admin",
"field"=> {
"text"=> 'value'
},
'field2.subfield'=> 'test',
},
"unset"=> ['field.unset'],
};
# response will contain user object with updated users info
client.update_user_partial(update);
# partial update for multiple users
updates = [
{
"id"=> "userID",
"set"=> {"field"=> "value"}
},
{
"id"=> "userID2",
"unset"=> ["field.value"]
}
]
# response will contain object {userID => user} with updated users info
client.update_users_partial(updates)
$client->partialUpdateUser([
"id" => $user["id"],
"set" => ["role" => "admin", "field2.subfield" => "test"]
]);
var updateObj = UserPartialUpdateRequestObject.builder()
.id(user.getId())
.setValue("role", "admin")
.setValue("field2.subfield", "test")
.build();
User.partialUpdate().users(Arrays.asList(updateObj)).request();
update := PartialUserUpdate{
ID: user.ID,
Set: map[string]interface{}{
"role": "admin",
"field": map[string]interface{}{
"text": "value",
},
},
}
resp, err := client.PartialUpdateUsers(ctx, []PartialUserUpdate{update})
// Partial update is not supported in Swift SDK
var updates = new Dictionary<string, object>
{
{ "role", "admin" },
{ "field2.subfield", "test" }
};
var resp = await userClient.UpdatePartialAsync(new UserPartialRequest
{
Id = user.Id,
Set = updates,
});
Partial updates support batch requests, similar to the upsertUser endpoint.
Unique Usernames
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, team or no.
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.
// Enable uniqueness constraints on App level
await client.updateAppSettings({
enforce_unique_usernames: 'app',
});
# Enable uniqueness constraints on App level
client.update_app_settings(enforce_unique_usernames: 'app')
# Enable uniqueness constraints on App level
client.update_app_settings(enforce_unique_usernames="app")
// Enable uniqueness constraints on App level
App.update().enforceUniqueUsernames("app").request();
// Enable uniqueness constraints on App level
await _appClient.UpdateAppSettingsAsync(new AppSettingsRequest
{
EnforceUniqueUsernames = UniqueUsernameEnforcementPolicy.App,
});
// Enable uniqueness constraints on App level
settings := &AppSettings{EnforceUniqueUsernames: "app"}
resp, err = client.UpdateAppSettings(ctx, settings)
// Enable uniqueness constraints on App level
$client->updateAppSettings(["enforce_unique_usernames" => "app"]);
// This can be set in https://dashboard.getstream.io/ -> Open your application -> Overview -> Authentication
Enabling this setting will only enforce the constraint going forward and will not try to validate existing usernames.
When this setting is set to app, attempts to create or update a user with an existing name in this app, will fail with duplicate username error.
// Enable uniqueness constraints on Team level
await client.updateAppSettings({
enforce_unique_usernames: 'team',
});
# Enable uniqueness constraints on Team level
client.update_app_settings(enforce_unique_usernames: 'team')
# Enable uniqueness constraints on Team level
client.update_app_settings(enforce_unique_usernames="team")
// Enable uniqueness constraints on Team level
App.update().enforceUniqueUsernames("team").request();
// Enable uniqueness constraints on Team level
await _appClient.UpdateAppSettingsAsync(new AppSettingsRequest
{
EnforceUniqueUsernames = UniqueUsernameEnforcementPolicy.Team,
});
// Enable uniqueness constraints on Team level
settings := &AppSettings{EnforceUniqueUsernames: "team"}
resp, err = client.UpdateAppSettings(ctx, settings)
// Enable uniqueness constraints on Team level
$client->updateAppSettings(["enforce_unique_usernames" => "team"]);
// This can be set in https://dashboard.getstream.io/ -> Open your application -> Overview -> Authentication
When set to team, attempts to create or update a user with an existing name will fail only if the name already exists within a common team.
Deactivate a User
To deactivate a user, Stream Chat exposes a deactivateUser
method. This method can *only * be called server-side due to security concerns, so please keep this in mind when attempting to make the call.
Deactivated users cannot:
Connect to Stream Chat
Send or receive messages
(deactivated users can be reactivated, see below)
Below is an example of how to execute the call to deactivateUser
:
The mark_messages_deleted
parameter is optional. This parameter will delete all messages associated with the user. If you would like to keep message history, ensure that mark_messages_deleted
is set to false
. To remove all messages related to the user, set the value to true
.
const deactivate = await client.deactivateUser(userID);
const deactivate = await client.deactivateUser(userID, {
mark_messages_deleted: true,
created_by_id: "joe",
});
response = client.deactivate_user(user_id)
response = client.deactivate_user(user_id,
mark_messages_deleted=True,
created_by_id="joe")
response = client.deactivate_user(user_id)
response = client.deactivate_user(user_id,
mark_messages_deleted: true,
created_by_id: "joe")
$response = $this->client->deactivateUser($user["id"]);
$response = $this->client->deactivateUser($user["id"], [
"mark_messages_deleted" => true,
"created_by_id" => "joe",
]);
err := client.DeactivateUser(userID, nil)
err := client.DeactivateUser(userID, map[string]interface{}{
"mark_messages_deleted": true,
"created_by_id": "joe",
})
await userClient.DeactivateAsync(userId);
await userClient.DeactivateAsync(userId, true, "joe");
User.deactivate(userId).createdById(userId2).request();
// This is a server-side only feature, choose any of our server-side SDKs to use it
The response will contain an object with the user ID that was deactivated. Further, the user will no longer be able to connect to Stream Chat as an error will be thrown.
To reinstate the user as active, use the reactivateUser
method by passing the users ID as a parameter:
const reactivate = await client.reactivateUser(userID);
const reactivate = await client.reactivateUser(userID, {
restore_messages: true,
name: "I am back",
created_by_id: "joe",
});
response = client.reactivate_user(user_id)
response = client.reactivate_user(user_id,
restore_messages=True,
name="I am back",
created_by_id="joe")
response = client.reactivate_user(user_id)
response = client.reactivate_user(user_id,
restore_messages: true,
name: "I am back",
created_by_id: "joe")
$response = $this->client->reactivateUser($user["id"]);
$response = $this->client->reactivateUser($user["id"], [
"restore_messages" => true,
"name" => "I am back",
"created_by_id" => "joe",
]);
err := client.ReactivateUser(userID, nil)
err := client.ReactivateUser(userID, map[string]interface{}{
"restore_messages": true,
"name": "I am back",
"created_by_id": "joe",
})
await userClient.ReactivateAsync(userId);
await userClient.ReactivateAsync(user2.Id, restoreMessages: true, name: "I am back", createdById: "joe");
User.reactivate(userId).createdById(userId2).request();
// This is a server-side only feature, choose any of our server-side SDKs to use it
Deactivate many Users
Many users (up to 100) can be deactivated and reactivated with only one call. The operation will be ran asynchronously, the response contains a task_id which can be polled using the getTask endpoint to check the status of the operation.
// Deactivate many users in one call
let resp = await serverClient.deactivateUsers([userIDs, ...], {
created_by_id: userID, // optional: define who deactivated the users
mark_messages_deleted: true // optional: the messages will be marked as deleted
})
resp = await serverClient.reactivateUsers([userIDs, ...], {
created_by_id: userID, // optional: define who reactivated the users
restore_messages: true // optional: the messages will be restored if they were deleted
})
// both resp contains a task_id to check the status of the operation
// This is a server-side only feature, choose any of our server-side SDKs to use it
Delete a User
While it is usually safer for data retention to deactivate a user, some use cases require completely deleting a user and their data. Regional or platform regulations such as the GDPR right to deletion or Apple App Store’s data collection and storage guidelines are two examples of regulations that require this.
To delete user data, Stream Chat exposes a deleteUser
method. Once a user has been hard deleted, it cannot be un-deleted and the user_id cannot be used again. This method can *only *be called server-side due to security concerns, so please keep this in mind when attempting to make the call.
Below is an example of how to execute the call to deleteUser
:
const destroy = await client.deleteUser(userID, {
mark_messages_deleted: false,
});
response = client.delete_user(user_id)
response = client.delete_user(user_id)
$response = $this->client->deleteUser($user["id"]);
err := client.DeleteUser(userID, nil)
await userClient.DeleteAsync(userId);
User.delete(userId).request();
// This is a server-side only feature, choose any of our server-side SDKs to use it
The mark_messages_deleted
parameter is optional. This parameter will delete all messages associated with the user. If you would like to keep message history, ensure that mark_messages_deleted
is set to false
. To remove all messages related to the user, set the value to true
.
Hard Delete a User
To perform a “hard delete” on the user, you must set mark_messages_deleted
to true and provide an additional parameter called hard_delete
with the value set to true
. This method will delete all messages, reactions, and any other associated data with the user. Another option is delete_conversation_channels
, if set true
the deleted user is removed from all one-to-one channels.
const destroy = await client.deleteUser('user_id', {
delete_conversation_channels: true,
mark_messages_deleted: true,
hard_delete: true,
});
$response = $client->deleteUser($user["id"], [
"mark_messages_deleted" => true,
"hard_delete" => true,
"delete_conversation_channels" => true,
]);
options := map[string]interface{}{
"mark_messages_deleted": true,
"hard_delete": true,
"delete_conversation_channels": true,
}
err := client.DeleteUser(userID, options)
await userClient.DeleteAsync(userId, markMessagesDeleted: true, hardDelete: true, deleteConversations: true);
client.delete_user(user_id,
mark_messages_deleted=True,
hard_delete=True,
delete_conversation_channels=True)
response = client.delete_user(user_id,
mark_messages_deleted: true,
hard_delete: true,
delete_conversation_channels: true)
User.delete(userId)
.markMessagesDeleted(true)
.hardDelete(true)
.deleteConversationChannels(true)
.request();
// This is a server-side only feature, choose any of our server-side SDKs to use it
deleteUser
with hard_delete: true
does not perform full hard delete of the user, but does “pruning” delete instead. Please use deleteUsers
endpoint to perform true hard delete. See “Deleting Many Users” section for more deletion options
After deleting or hard deleting a user, the user will no longer be able to:
Connect to Stream Chat
Send or receive messages
Be displayed when querying users
Have messages stored in Stream Chat (depending on whether or not
mark_messages_deleted
is set totrue
orfalse
)
These are the request parameters which determine what user data is deleted:
name | type | description | default | optional |
---|---|---|---|---|
user_ids | array | List of users who will be deleted | - | |
mark_messages_deleted | boolean | Delete all of the users messages too | - | ✓ |
delete_conversation_channels | boolean | Delete all 1:1 channels with this user | - | ✓ |
hard_delete | boolean | By default, messages are soft deleted, which means they are removed from client but are still available via server-side export functions. Hard delete will remove data from everywhere on the server | ✓ | |
new_channel_owner_id | string | Channels owned by hard-deleted users will be transferred to this userID. | - | ✓ |
Deleting Many Users
You can delete up to 100 users and optionally all of their channels and messages using this method. First the users are marked deleted synchronously so the user will not be directly visible in the API. Then the process deletes the user and related objects asynchronously by scheduling a task to be handles by the task worker.
This method can only be called server-side due to security concerns. This is supported on the following SDK versions (or higher):
- Javascript 4.3.0, Python 3.14.0, Ruby 2.12.0, PHP 2.6.0, Go 3.13.0, Java 1.4.0 and.NET 0.22.0
let response = await serverClient.deleteUsers(['userID1', 'userID2'], {
user: 'soft',
messages: 'hard',
});
response = await serverClient.getTask(response['task_id']);
if(response['status] === 'completed') {
// success!
}
response = client.delete_users(
['userID1', 'userID2'],
"soft",
messages="hard"
)
response = client.get_task(response["task_id"])
if response['status'] == 'completed':
# success!
pass
taskID, err := client.DeleteUsers([]string{"userID1", "userID2"}, DeleteUserOptions{
User: SoftDelete,
Messages: HardDelete,
})
if err != nil {
return err
}
resp, err := client.GetTask(taskID)
if err != nil {
return err
}
if resp.Status == "completed" {
// success!
}
$response = $client->deleteUsers(
["userID1", "userID2"],
["user" => "soft", "messages" => "hard"]
);
$response = $client->getTask($response["task_id"]);
if ($response["status"] == "completed") {
// success!
};
response = client.delete_users(
['userID1', 'userID2'],
user: StreamChat::SOFT_DELETE
)
response = client.get_task(response["task_id"])
if response['status'] == 'completed'
# success!
var taskId = User.deleteMany(List.of(userID1, userID2))
.deleteUserStrategy(DeleteStrategy.SOFT)
.deleteMessagesStrategy(DeleteStrategy.HARD)
.request().getTaskId();
var taskStatusResponse = TaskStatus.get(taskId).request();
// "completed".equals(taskStatusResponse.status);
var resp = await userClient.DeleteManyAsync(
new DeleteUsersRequest()
.WithUserIds(user1.Id, user2.Id)
.WithUserDeletionStrategy(DeletionStrategy.Hard)
.WithMessagesDeletionStrategy(DeletionStrategy.Hard)
.WithConversationsDeletionStrategy(DeletionStrategy.Hard));
var status = await taskClient.GetTaskStatusAsync(resp.TaskId);
var finished = status.Status == AsyncTaskStatus.Completed;
// This is a server-side only feature, choose any of our server-side SDKs to use it
The deleteUsers
response contain a task_id which can be polled using the getTask endpoint to check the status of the deletions.
These are the request parameters which determine what user data is deleted:
name | type | description | default | optional |
---|---|---|---|---|
user_ids | array | List of users who will be deleted | - | |
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 - this requires hard option for messages and conversation as well. | - | ✓ |
conversations | enum (soft, hard) | Soft: marks all conversation channels as deleted (same effect as Delete Channels with ‘hard’ option disabled). Hard: deletes channel and all its data completely including messages (same effect as Delete Channels with ‘hard’ option enabled). | ✓ | |
messages | enum (soft, pruning, hard) | Soft: marks all user messages as deleted without removing any related message data. Pruning: marks all user messages as deleted, nullifies message information and removes some message data such as reactions and flags. Hard: deletes messages completely with all related information. | - | ✓ |
new_channel_owner_id | string | Channels owned by hard-deleted users will be transferred to this userID. | - | ✓ |
When deleting a user, if you wish to transfer ownership of their channels to another user, provide that user’s ID in the new_channel_owner_id
field. Otherwise, the channel owner will be updated to a system generated ID like delete-user-8219f6578a7395g
Restoring deleted users
If users are soft (only) deleted they can be restored using server side client. However, only user’s metadata is restored, membership, messages etc… can’t be put back.
You can restore up to 100 users per call:
await client.restoreUsers(['userID1', 'userID2'])
client.restore_users(['userID1', 'userID2'])
// This is a server-side only feature, choose any of our server-side SDKs to use it