# User management

<admonition type="info">

The operations performed on users, such as updating and deleting, have an effect on all products (chat, feeds and video).

</admonition>

## Creating and connecting users

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.
- The `language` field (ISO 639-1) is optional but recommended when using [activity and comment translation](/activity-feeds/docs/java/translation/). It helps the API determine the source language when translating content authored by that user.

<Tabs>

```js label="Node.js"
const newUser: UserRequest = {
  id: 'user-id',
  role: 'user',
  custom: {
    color: 'red',
  },
  name: 'This is a test user',
  image: 'link/to/profile/image',
};
await client.upsertUsers([newUser]);
```

```python label="Python"
users = {
    self.test_user_id: UserRequest(
        id=self.test_user_id, name="Test User 1", role="user"
    ),
    self.test_user_id_2: UserRequest(
        id=self.test_user_id_2, name="Test User 2", role="user"
    ),
}
response = self.client.update_users(users=users)
```

```ruby label="Ruby"
# Create/update users in batch
update_request = GetStream::Generated::Models::UpdateUsersRequest.new(
  users: {
    'user1' => {
      'id' => 'user1',
      'name' => 'Test User 1',
      'role' => 'user',
    },
    'user2' => {
      'id' => 'user2',
      'name' => 'Test User 2',
      'role' => 'user',
    },
  },
)

response = client.common.update_users(update_request)
```

```php label="PHP"
use GetStream\ClientBuilder;
use GetStream\GeneratedModels;

$client = (new ClientBuilder())
    ->apiKey($apiKey)
    ->apiSecret($apiSecret)
    ->build();

$response = $client->updateUsers(new GeneratedModels\UpdateUsersRequest(
    users: [
        'user-id-1' => [
            'id' => 'user-id-1',
            'name' => 'Test User 1',
            'role' => 'user',
            'custom' => (object)[
                'color' => 'red'
            ]
        ],
        'user-id-2' => [
            'id' => 'user-id-2',
            'name' => 'Test User 2',
            'role' => 'user'
        ]
    ]
));
```

```go label="Go"
users := map[string]getstream.UserRequest{
    "user1": {
        ID:   "user1",
        Name: getstream.PtrTo("Test User 1"),
        Role: getstream.PtrTo("user"),
    },
    "user2": {
        ID:   "user2",
        Name: getstream.PtrTo("Test User 2"),
        Role: getstream.PtrTo("user"),
    },
}
request := &getstream.UpdateUsersRequest{
    Users: users,
}

response, err := client.UpdateUsers(context.Background(), request)
if err != nil {
    log.Fatal("Error updating users:", err)
}
log.Printf("Users updated successfully: %+v\n", response)
```

```csharp label="C#"
var updateUsersRequest = new UpdateUsersRequest
{
    Users = new Dictionary<string, UserRequest>
    {
        [_testUserId] = new UserRequest
        {
            ID = _testUserId,
            Name = "Test User 1",
            Role = "user"
        },
        [_testUserId2] = new UserRequest
        {
            ID = _testUserId2,
            Name = "Test User 2",
            Role = "user"
        },
        [_testUserId3] = new UserRequest
        {
            ID = _testUserId3,
            Name = "Test User 3",
            Role = "user"
        }
    }
};
var userResponse = await _client.UpdateUsersAsync(updateUsersRequest);
```

```java label="Java"
Map<String, UserRequest> usersMap = new HashMap<>();
usersMap.put(
    testUserId,
    UserRequest.builder().id(testUserId).name("Test User 1").role("user").build());
usersMap.put(
    testUserId2,
    UserRequest.builder().id(testUserId2).name("Test User 2").role("user").build());

UpdateUsersRequest updateUsersRequest = UpdateUsersRequest.builder().users(usersMap).build();

client.updateUsers(updateUsersRequest).execute();
```

</Tabs>

Users can be created on the fly when using client-side SDKs. Users created with this method will have the `user` role:

<Tabs>

```js label="React"
import { useCreateFeedsClient } from "@stream-io/feeds-react-sdk";

const client = useCreateFeedsClient({
  apiKey,
  userData: {
    id: "adam",
    // Optional data
    name: "Adam",
    image: "url/to/profile/picture",
  },
  tokenOrProvider: "<string token or token API call>",
});

// When logging out: await client.disconnectUser();
```

```js label="React Native"
import { useCreateFeedsClient } from "@stream-io/feeds-react-native-sdk";

const client = useCreateFeedsClient({
  apiKey,
  userData: {
    id: "adam",
    // Optional data
    name: "Adam",
    image: "url/to/profile/picture",
  },
  tokenOrProvider: "<string token or token API call>",
});

// When logging out: await client.disconnectUser();
```

```js label="JavaScript"
import { FeedsClient } from "@stream-io/feeds-client";

const client = new FeedsClient("<API key>");
await client.connectUser(
  {
    id: "john",
    // Optional data
    name: "John",
    image: "url/to/profile/picture",
  },
  "<user token or provider>",
);

// When logging out: await client.disconnectUser();
```

```kotlin label="Kotlin"
val client = FeedsClient(
    context = context,
    apiKey = StreamApiKey.fromString("<your_api_key>"),
    user = User(
        id = "john",
        name = "John",                       // optional
        imageURL = "url/to/profile/picture"  // optional
    ),
    tokenProvider = object : StreamTokenProvider {
        override suspend fun loadToken(userId: StreamUserId): StreamToken {
            return StreamToken.fromString("<user_token>")
        }
    }
)
val connectResult: Result<StreamConnectedUser> = client.connect()

// When logging out: client.disconnect()
```

```swift label="Swift"
import StreamCore
import StreamFeeds

let client = FeedsClient(
    apiKey: APIKey("<your_api_key>"),
    user: User(
        id: "john",
        name: "John",           // optional
        image: "url/to/profile/picture"  // optional
    ),
    token: UserToken("<user_token>"),
    tokenProvider: nil
)

// When logging out: try await client.disconnect()
```

```dart label="Dart"
import 'package:stream_feeds/stream_feeds.dart';

final client = StreamFeedsClient(
  apiKey: '<your_api_key>',
  user: User(
    id: 'john',
    name: 'John',           // optional
    image: 'url/to/profile/picture',  // optional
  ),
  tokenProvider: TokenProvider.static(UserToken('<user_token>')),
);
await client.connect();

// When logging out: await client.disconnect();
```

</Tabs>

## Built-in fields for users

<open-api-models modelname="UserResponse" recursive="false" headerlevel="3">
</open-api-models>

## Querying users

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.

<Tabs>

```js label="React"
const response = await client.queryUsers({
  payload: {
    filter_conditions: {
      role: "admin",
    },
    sort: [{ field: "created_at", direction: -1 }],
    limit: 10,
    offset: 0,
  },
});

console.log(response.users);
```

```js label="React Native"
const response = await client.queryUsers({
  payload: {
    filter_conditions: {
      role: "admin",
    },
    sort: [{ field: "created_at", direction: -1 }],
    limit: 10,
    offset: 0,
  },
});

console.log(response.users);
```

```js label="JavaScript"
const response = await client.queryUsers({
  payload: {
    filter_conditions: {
      role: "admin",
    },
    sort: [{ field: "created_at", direction: -1 }],
    limit: 10,
    offset: 0,
  },
});

console.log(response.users);
```

```kotlin label="Kotlin"
val query = UsersQuery(
    filter = UsersFilterField.role.equal("admin"),
    sort = listOf(UsersSort(UsersSortField.CreatedAt, SortDirection.REVERSE)),
    limit = 10,
)
val userList = client.userList(query = query)
val users: Result<List<UserData>> = userList.get()
```

```js label="Node.js"
const response = await client.queryUsers({
  payload: {
    filter_conditions: {
      role: "admin",
    },
    sort: [{ field: "created_at", direction: -1 }],
    limit: 10,
    offset: 0,
  },
});

console.log(response.users);
```

```python label="Python"
from getstream.models import QueryUsersPayload, SortParamRequest

response = self.client.query_users(
    payload=QueryUsersPayload(
        filter_conditions={
            "role": "admin",
        },
        sort=[
            SortParamRequest(field="created_at", direction=-1)
        ],
        limit=10,
        offset=0,
    )
)
```

```ruby label="Ruby"
payload = GetStream::Generated::Models::QueryUsersPayload.new(
  filter_conditions: {
    'role' => 'admin',
  },
  sort: [
    {
      'field' => 'created_at',
      'direction' => -1,
    },
  ],
  limit: 10,
  offset: 0,
)

response = client.common.query_users(payload)
```

```php label="PHP"
$response = $client->queryUsers(
    new GeneratedModels\QueryUsersPayload(
        filterConditions: (object)['role' => 'admin'],
        sort: [
            ['field' => 'created_at', 'direction' => -1]
        ],
        limit: 10,
        offset: 0
    )
);
```

```go label="Go"
response, err := client.QueryUsers(context.Background(), &getstream.QueryUsersRequest{
    Payload: &getstream.QueryUsersPayload{
        FilterConditions: map[string]any{
            "role": "admin",
        },
        Sort: []getstream.SortParamRequest{
            {
                Field:     getstream.PtrTo("created_at"),
                Direction: getstream.PtrTo(-1),
            },
        },
        Limit:  getstream.PtrTo(10),
        Offset: getstream.PtrTo(0),
    },
})
if err != nil {
    log.Fatal("Error querying users:", err)
}
log.Printf("Found %d users\n", len(response.Data.Users))
```

```csharp label="C#"
var response = await _client.QueryUsersAsync(
    new QueryUsersRequest
    {
        Payload = new QueryUsersPayload
        {
            FilterConditions = new Dictionary<string, object>
            {
                ["role"] = "admin"
            },
            Sort = new[]
            {
                new SortParamRequest
                {
                    Field = "created_at",
                    Direction = -1
                }
            },
            Limit = 10,
            Offset = 0
        }
    }
);
```

```java label="Java"
Map<String, Object> filterConditions = new HashMap<>();
filterConditions.put("role", "admin");

QueryUsersRequest request = QueryUsersRequest.builder()
    .payload(QueryUsersPayload.builder()
        .filterConditions(filterConditions)
        .sort(List.of(
            SortParamRequest.builder()
                .field("created_at")
                .direction(-1)
                .build()
        ))
        .limit(10)
        .offset(0)
        .build())
    .build();

QueryUsersResponse response = client.queryUsers(request).execute().getData();
```

</Tabs>

<admonition type="warning">

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.

</admonition>

### Supported Filters

| Name              | Type                                              | Description                        | Allowed Operators                                        |
| ----------------- | ------------------------------------------------- | ---------------------------------- | -------------------------------------------------------- |
| id                | string                                            | ID of the user                     | $eq, $ne, $in, $nin, $gt, $gte, $lt, $lte, $autocomplete |
| role              | string                                            | Role of the user                   | $eq, $gt, $gte, $lt, $lte, $in                           |
| banned            | boolean                                           | Whether the user is banned         | $eq, $ne                                                 |
| shadow_banned     | boolean                                           | Whether the user is shadow banned  | $eq, $ne                                                 |
| created_at        | string, must be formatted as an RFC3339 timestamp | Time when the user was created     | $eq, $gt, $gte, $lt, $lte, $in                           |
| updated_at        | string, must be formatted as an RFC3339 timestamp | Time when the user was updated     | $eq, $gt, $gte, $lt, $lte, $in                           |
| last_active       | string, must be formatted as an RFC3339 timestamp | Time when the user was last active | $eq, $gt, $gte, $lt, $lte, $in, $exists                  |
| teams             | string                                            | Teams the user belongs to          | $eq, $contains, $in                                      |
| name              | string                                            | Name of the user                   | $eq, $in, $autocomplete                                  |
| username          | string                                            | Username of the user               | $eq, $autocomplete                                       |
| custom properties | string, boolean, int                              | Custom user properties             | $eq                                                      |

### Query Users Performance

Query Users runs against 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.

<admonition type="info">

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.

</admonition>

### Supported Sort

You can sort results by specifying a field and direction (1 for ascending, -1 for descending).

| Name        | Description                        |
| ----------- | ---------------------------------- |
| id          | User ID                            |
| created_at  | Time when the user was created     |
| updated_at  | Time when the user was updated     |
| last_active | Time when the user was last active |
| role        | Role of the user                   |

### Supported Options

The options for the `queryUsers` method are primarily used for pagination.

| Name                      | Type    | Description                                                                                                                          | Default | Optional |
| ------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------- | -------- |
| limit                     | integer | Number of users to return                                                                                                            | 30      | ✓        |
| offset                    | integer | Offset for pagination                                                                                                                | 0       | ✓        |
| id_gt                     | string  | ID-based pagination. Return IDs greater than this ID. If this is not empty, the default sort order will be `[{id: -1}]`.             | -       | ✓        |
| id_gte                    | string  | ID-based pagination. Return IDs greater than or equal to this ID. If this is not empty, the default sort order will be `[{id: -1}]`. | -       | ✓        |
| id_lt                     | string  | ID-based pagination. Return IDs less than this ID. If this is not empty, the default sort order will be `[{id: -1}]`.                | -       | ✓        |
| id_lte                    | string  | ID-based pagination. Return IDs less than or equal to this ID. If this is not empty, the default sort order will be `[{id: -1}]`.    | -       | ✓        |
| include_deactivated_users | boolean | Include deactivated users in the response                                                                                            | -       | ✓        |

<admonition type="info">

The maximum offset value is 1000.

</admonition>


### Filter examples

You can use various filter operators to query users:

<Tabs>

```js label="React"
// Query users by custom field
const response = await client.queryUsers({
  payload: {
    filter_conditions: {
      "custom.color": "red",
    },
  },
});

// Query users with multiple conditions
const response2 = await client.queryUsers({
  payload: {
    filter_conditions: {
      role: { $in: ["admin", "moderator"] },
      "custom.plan": "premium",
    },
  },
});

// Query users by name (text search)
const response3 = await client.queryUsers({
  payload: {
    filter_conditions: {
      name: { $autocomplete: "john" },
    },
  },
});
```

```js label="React Native"
// Query users by custom field
const response = await client.queryUsers({
  payload: {
    filter_conditions: {
      "custom.color": "red",
    },
  },
});

// Query users with multiple conditions
const response2 = await client.queryUsers({
  payload: {
    filter_conditions: {
      role: { $in: ["admin", "moderator"] },
      "custom.plan": "premium",
    },
  },
});

// Query users by name (text search)
const response3 = await client.queryUsers({
  payload: {
    filter_conditions: {
      name: { $autocomplete: "john" },
    },
  },
});
```

```js label="JavaScript"
// Query users by custom field
const response = await client.queryUsers({
  payload: {
    filter_conditions: {
      "custom.color": "red",
    },
  },
});

// Query users with multiple conditions
const response2 = await client.queryUsers({
  payload: {
    filter_conditions: {
      role: { $in: ["admin", "moderator"] },
      "custom.plan": "premium",
    },
  },
});

// Query users by name (text search)
const response3 = await client.queryUsers({
  payload: {
    filter_conditions: {
      name: { $autocomplete: "john" },
    },
  },
});
```

```kotlin label="Kotlin"
// Query users by role
val query = UsersQuery(
    filter = UsersFilterField.role.`in`(listOf("admin", "moderator")),
)
val userList = client.userList(query = query)
val users: Result<List<UserData>> = userList.get()

// Query users by name (autocomplete)
val query2 = UsersQuery(
    filter = UsersFilterField.name.autocomplete("john"),
)
val userList2 = client.userList(query = query2)
val users2: Result<List<UserData>> = userList2.get()
```

```js label="Node.js"
// Query users by custom field
const response = await client.queryUsers({
  payload: {
    filter_conditions: {
      "custom.color": "red",
    },
  },
});

// Query users with multiple conditions
const response2 = await client.queryUsers({
  payload: {
    filter_conditions: {
      role: { $in: ["admin", "moderator"] },
      "custom.plan": "premium",
    },
  },
});

// Query users by name (text search)
const response3 = await client.queryUsers({
  payload: {
    filter_conditions: {
      name: { $autocomplete: "john" },
    },
  },
});
```

```python label="Python"
from getstream.models import QueryUsersPayload

# Query users by custom field
response = self.client.query_users(
    payload=QueryUsersPayload(
        filter_conditions={
            "custom.color": "red",
        }
    )
)

# Query users with multiple conditions
response2 = self.client.query_users(
    payload=QueryUsersPayload(
        filter_conditions={
            "role": {"$in": ["admin", "moderator"]},
            "custom.plan": "premium",
        }
    )
)

# Query users by name (text search)
response3 = self.client.query_users(
    payload=QueryUsersPayload(
        filter_conditions={
            "name": {"$autocomplete": "john"},
        }
    )
)
```

```ruby label="Ruby"
# Query users by custom field
payload = GetStream::Generated::Models::QueryUsersPayload.new(
  filter_conditions: {
    'custom.color' => 'red',
  },
)
response = client.common.query_users(payload)

# Query users with multiple conditions
payload2 = GetStream::Generated::Models::QueryUsersPayload.new(
  filter_conditions: {
    'role' => { '$in' => ['admin', 'moderator'] },
    'custom.plan' => 'premium',
  },
)
response2 = client.common.query_users(payload2)

# Query users by name (text search)
payload3 = GetStream::Generated::Models::QueryUsersPayload.new(
  filter_conditions: {
    'name' => { '$autocomplete' => 'john' },
  },
)
response3 = client.common.query_users(payload3)
```

```php label="PHP"
// Query users by custom field
$response = $client->queryUsers(
    new GeneratedModels\QueryUsersPayload(
        filterConditions: (object)['custom.color' => 'red']
    )
);

// Query users with multiple conditions
$response2 = $client->queryUsers(
    new GeneratedModels\QueryUsersPayload(
        filterConditions: (object)[
            'role' => (object)['$in' => ['admin', 'moderator']],
            'custom.plan' => 'premium',
        ]
    )
);

// Query users by name (text search)
$response3 = $client->queryUsers(
    new GeneratedModels\QueryUsersPayload(
        filterConditions: (object)[
            'name' => (object)['$autocomplete' => 'john'],
        ]
    )
);
```

```go label="Go"
// Query users by custom field
response, err := client.QueryUsers(context.Background(), &getstream.QueryUsersPayload{
    FilterConditions: map[string]any{
        "custom.color": "red",
    },
})

// Query users with multiple conditions
response2, err := client.QueryUsers(context.Background(), &getstream.QueryUsersPayload{
    FilterConditions: map[string]any{
        "role": map[string]any{"$in": []string{"admin", "moderator"}},
        "custom.plan": "premium",
    },
})

// Query users by name (text search)
response3, err := client.QueryUsers(context.Background(), &getstream.QueryUsersPayload{
    FilterConditions: map[string]any{
        "name": map[string]any{"$autocomplete": "john"},
    },
})
```

```csharp label="C#"
// Query users by custom field
var response = await _client.QueryUsersAsync(
    new QueryUsersRequest
    {
        Payload = new QueryUsersPayload
        {
            FilterConditions = new Dictionary<string, object>
            {
                ["custom.color"] = "red"
            }
        }
    }
);

// Query users with multiple conditions
var response2 = await _client.QueryUsersAsync(
    new QueryUsersRequest
    {
        Payload = new QueryUsersPayload
        {
            FilterConditions = new Dictionary<string, object>
            {
                ["role"] = new Dictionary<string, object>
                {
                    ["$in"] = new[] { "admin", "moderator" }
                },
                ["custom.plan"] = "premium"
            }
        }
    }
);

// Query users by name (text search)
var response3 = await _client.QueryUsersAsync(
    new QueryUsersRequest
    {
        Payload = new QueryUsersPayload
        {
            FilterConditions = new Dictionary<string, object>
            {
                ["name"] = new Dictionary<string, object>
                {
                    ["$autocomplete"] = "john"
                }
            }
        }
    }
);
```

```java label="Java"
// Query users by custom field
Map<String, Object> filter1 = new HashMap<>();
filter1.put("custom.color", "red");
QueryUsersRequest request1 = QueryUsersRequest.builder()
    .payload(QueryUsersPayload.builder()
        .filterConditions(filter1)
        .build())
    .build();
QueryUsersResponse response1 = client.queryUsers(request1).execute().getData();

// Query users with multiple conditions
Map<String, Object> filter2 = new HashMap<>();
filter2.put("role", Map.of("$in", List.of("admin", "moderator")));
filter2.put("custom.plan", "premium");
QueryUsersRequest request2 = QueryUsersRequest.builder()
    .payload(QueryUsersPayload.builder()
        .filterConditions(filter2)
        .build())
    .build();
QueryUsersResponse response2 = client.queryUsers(request2).execute().getData();

// Query users by name (text search)
Map<String, Object> filter3 = new HashMap<>();
filter3.put("name", Map.of("$autocomplete", "john"));
QueryUsersRequest request3 = QueryUsersRequest.builder()
    .payload(QueryUsersPayload.builder()
        .filterConditions(filter3)
        .build())
    .build();
QueryUsersResponse response3 = client.queryUsers(request3).execute().getData();
```

</Tabs>

## Updating users

You can update users in two ways:

- Replace updates: replace the entire user object with the one provided to the API call
- Partial update: choose which fields you want to change

<Tabs>

```js label="Node.js"
const user: UserRequest = {
  id: 'userid',
  role: 'user',
  custom: {
    color: 'blue',
  },
  name: 'This is a test user',
  image: 'link/to/profile/image',
};
client.upsertUsers([user]);

// or
client.updateUsersPartial({
  users: [
    {
      id: user.id,
      set: {
        'new-field': 'value',
      },
      unset: ['name'],
    },
  ],
});
```

```python label="Python"
users = {
    self.test_user_id: UserRequest(
        id=self.test_user_id, name="Test User 1", role="user"
    ),
    self.test_user_id_2: UserRequest(
        id=self.test_user_id_2, name="Test User 2", role="user"
    ),
}
response = self.client.update_users(users=users)
```

```ruby label="Ruby"
# Partially update user
partial_request = GetStream::Generated::Models::UpdateUsersPartialRequest.new(
  users: [
    {
      'id' => 'userid',
      'set' => {
        'name' => 'Updated Name',
      },
    },
  ],
)

response = client.common.update_users_partial(partial_request)
```

```php label="PHP"
// Example 1: Upsert users (replace update)
$client->updateUsers(new GeneratedModels\UpdateUsersRequest(
    users: [
        'userid' => [
            'id' => 'userid',
            'name' => 'This is a test user',
            'role' => 'user',
            'custom' => (object)[
                'color' => 'blue'
            ],
            'image' => 'link/to/profile/image'
        ]
    ]
));

// Example 2: Partial update users
$client->updateUsersPartial(new GeneratedModels\UpdateUsersPartialRequest(
    users: [
        [
            'id' => 'userid',
            'set' => (object)[
                'new-field' => 'value'
            ],
            'unset' => ['name']
        ]
    ]
));
```

```go label="Go"
// Example 1: Upsert users
user := getstream.UserRequest{
    ID:   "userid",
    Role: getstream.PtrTo("user"),
    Custom: map[string]any{
        "color": "blue",
    },
    Name:  getstream.PtrTo("This is a test user"),
    Image: getstream.PtrTo("link/to/profile/image"),
}

upsertRequest := &getstream.UpdateUsersRequest{
    Users: map[string]getstream.UserRequest{
        user.ID: user,
    },
}

upsertResponse, err := client.UpdateUsers(context.Background(), upsertRequest)
if err != nil {
    log.Fatal("Error upserting users:", err)
}
log.Printf("Users upserted successfully: %+v\n", upsertResponse)

// Example 2: Partial update users
partialUpdateRequest := &getstream.UpdateUsersPartialRequest{
    Users: []getstream.UpdateUserPartialRequest{
        {
            ID: user.ID,
            Set: map[string]any{
                "new-field": "value",
            },
            Unset: []string{"name"},
        },
    },
}

partialResponse, err := client.UpdateUsersPartial(context.Background(), partialUpdateRequest)
if err != nil {
    log.Fatal("Error partially updating users:", err)
}
log.Printf("Users partially updated successfully: %+v\n", partialResponse)
```

```csharp label="C#"
var updateUsersRequest = new UpdateUsersRequest
{
    Users = new Dictionary<string, UserRequest>
    {
        [_testUserId] = new UserRequest
        {
            ID = _testUserId,
            Name = "Test User 1",
            Role = "user"
        },
        [_testUserId2] = new UserRequest
        {
            ID = _testUserId2,
            Name = "Test User 2",
            Role = "user"
        },
        [_testUserId3] = new UserRequest
        {
            ID = _testUserId3,
            Name = "Test User 3",
            Role = "user"
        }
    }
};
var userResponse = await _client.UpdateUsersAsync(updateUsersRequest);
```

```java label="Java"
Map<String, UserRequest> usersMap = new HashMap<>();
usersMap.put(
    testUserId,
    UserRequest.builder().id(testUserId).name("Test User 1").role("user").build());
usersMap.put(
    testUserId2,
    UserRequest.builder().id(testUserId2).name("Test User 2").role("user").build());

UpdateUsersRequest updateUsersRequest = UpdateUsersRequest.builder().users(usersMap).build();

client.updateUsers(updateUsersRequest).execute();
```

</Tabs>


## Deactivating and deleting users

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

Note: Both deletion and deactivation are performed asynchronously by Stream API. A task ID is returned and you can use that to check the status of its processing.

### Deactivating users

<Tabs>

```js label="Node.js"
client.deactivateUser({
  user_id: '<id>',
});

// reactivate
client.reactivateUsers({
  user_ids: ['<id>'],
});

// deactivating users in bulk is performed asynchronously
const deactivateResponse = client.deactivateUsers({
  user_ids: ['<id1>', '<id2>'...],
});
```

```python label="Python"
# Deactivating a single user
self.client.deactivate_user(user_id=self.test_user_id)

# Deactivating users in bulk is performed asynchronously
response = self.client.deactivate_users(
    user_ids=[self.test_user_id, self.test_user_id_2]
)

# Reactivate users
self.client.reactivate_users(
    user_ids=[self.test_user_id, self.test_user_id_2]
)
```

```php label="PHP"
// 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']
    )
);
```

```go label="Go"
// Deactivating a single user
_, err = client.DeactivateUser(context.Background(), "user1", &getstream.DeactivateUserRequest{})
if err != nil {
    log.Fatal("Error deactivating user:", err)
}
log.Printf("User deactivated successfully")

// Deactivating users in bulk is performed asynchronously
_, err = client.DeactivateUsers(context.Background(), &getstream.DeactivateUsersRequest{
    UserIds: []string{"user2"},
})
if err != nil {
    log.Fatal("Error deactivating users:", err)
}
log.Printf("Users deactivated successfully")

// Reactivate users
reactivateRequest := &getstream.ReactivateUsersRequest{
    UserIds: []string{"user1", "user2"},
}

_, err = client.ReactivateUsers(context.Background(), reactivateRequest)
if err != nil {
    log.Fatal("Error reactivating users:", err)
}
log.Printf("Users reactivated successfully")
```

```csharp label="C#"
// Deactivating a single user
await _client.DeactivateUserAsync(_testUserId, new DeactivateUserRequest());

// Deactivating users in bulk is performed asynchronously
var deactivateRequest = new DeactivateUsersRequest
{
    UserIds = new[] { _testUserId, _testUserId2 }
};
await _client.DeactivateUsersAsync(deactivateRequest);

// Reactivate users
var reactivateRequest = new ReactivateUsersRequest
{
    UserIds = new[] { _testUserId, _testUserId2 }
};
await _client.ReactivateUsersAsync(reactivateRequest);
```

```java label="Java"
// Deactivating a single user
client.deactivateUser(testUserId, DeactivateUserRequest.builder().build()).execute();

// Deactivating users in bulk is performed asynchronously
DeactivateUsersRequest deactivateRequest = DeactivateUsersRequest.builder()
    .userIds(List.of(testUserId, testUserId2))
    .build();
client.deactivateUsers(deactivateRequest).execute();

// Reactivate users
ReactivateUsersRequest reactivateRequest = ReactivateUsersRequest.builder()
    .userIds(List.of(testUserId, testUserId2))
    .build();
client.reactivateUsers(reactivateRequest).execute();
```

</Tabs>

Deactivating users in bulk can take some time. This is how you can check the progress:

<Tabs>

```js label="Node.js"
// Example of monitoring the status of an async task
// The logic is same for all async tasks
const response = _; // Result of a Stream async API request

// You need to poll this endpoint
const taskResponse = await client.getTask({ id: response.task_id });
console.log(taskResponse.status === "completed");
```

```python label="Python"
# Example of monitoring the status of an async task
# The logic is same for all async tasks
response = _ # Result of a Stream async API request

# You need to poll this endpoint
task_response = client.get_task(response.task_id)
print(task_response.status == "completed")
```

```ruby label="Ruby"
# Example of monitoring the status of an async task
# The logic is same for all async tasks
response = _ # Result of a Stream async API request

# You need to poll this endpoint
task_response = client.get_task(response.data.task_id)
puts task_response.data.status == 'completed'
```

```php label="PHP"
// Example of monitoring the status of an async task
// The logic is same for all async tasks
$response = _ // Result of a Stream async API request

$taskId = $response->getData()->taskID;

// you need to poll this endpoint
$taskResponse = $client->getTask($taskId);

echo $taskResponse->getData()->status === 'completed';
```

```go label="Go"
// Example of monitoring the status of an async task
// The logic is same for all async tasks
response, err := _ // Result of a Stream async API request
if err != nil {
    log.Fatal(err)
}

// You need to poll this endpoint
taskResponse, err := client.GetTask(context.Background(), response.Data.TaskID, &getstream.GetTaskRequest{})
if err != nil {
    log.Fatal(err)
}

fmt.Println(taskResponse.Data.Status == "completed")
```

```csharp label="C#"
// Example of monitoring the status of an async task
// The logic is same for all async tasks
var response = _ // Result of a Stream async API request

// You need to poll this endpoint
var taskResponse = await _client.GetTaskAsync(response.TaskID);
Console.WriteLine(taskResponse.Status == "completed");
```

```java label="Java"
// Example of monitoring the status of an async task
// The logic is same for all async tasks
var response = _ // Result of a Stream async API request

// You need to poll this endpoint
var taskResponse = client.getTask(response.getTaskId()).execute().getData();
System.out.println("completed".equals(taskResponse.getStatus()));
```

</Tabs>


### Deleting users

<Tabs>

```js label="Node.js"
client.deleteUsers({ user_ids: ["<id>"] });

//restore
client.restoreUsers({ user_ids: ["<id>"] });
```

```python label="Python"
# Delete users
client.delete_users(user_ids=["<id>"])

# Restore users
client.restore_users(user_ids=["<id>"])
```

```ruby label="Ruby"
# Delete users
client.delete_users(
  GetStream::Generated::Models::DeleteUsersRequest.new(
    user_ids: ['<id>']
  )
)

# Restore users
client.restore_users(
  GetStream::Generated::Models::RestoreUsersRequest.new(
    user_ids: ['<id>']
  )
)
```

```php label="PHP"
// Delete users (soft delete by default)
$client->deleteUsers(
    new GeneratedModels\DeleteUsersRequest(
        userIds: ['<id>']
    )
);

// Delete users with options (hard delete)
$client->deleteUsers(
    new GeneratedModels\DeleteUsersRequest(
        userIds: ['<id>'],
        user: 'hard',
        messages: 'hard',
        conversations: 'hard',
        newChannelOwnerID: 'new-owner-id'
    )
);

// Restore users
$client->restoreUsers(
    new GeneratedModels\RestoreUsersRequest(
        userIds: ['<id>']
    )
);
```

```go label="Go"
// Delete users
deleteRequest := &getstream.DeleteUsersRequest{
    UserIds: []string{"<id>"},
}
_, err = client.DeleteUsers(context.Background(), deleteRequest)

// Restore users
restoreRequest := &getstream.RestoreUsersRequest{
    UserIds: []string{"<id>"},
}
_, err = client.RestoreUsers(context.Background(), restoreRequest)
```

```csharp label="C#"
// Delete users
await _client.DeleteUsersAsync(new DeleteUsersRequest
{
    UserIds = new List<string> { "<id>" }
});

// Restore users
await _client.RestoreUsersAsync(new RestoreUsersRequest
{
    UserIds = new List<string> { "<id>" }
});
```

```java label="Java"
// Delete users
client.deleteUsers(
    DeleteUsersRequest.builder()
        .userIds(Arrays.asList("<id>"))
        .build()
).execute();

// Restore users
client.restoreUsers(
    RestoreUsersRequest.builder()
        .userIds(Arrays.asList("<id>"))
        .build()
).execute();
```

</Tabs>

By default users are soft deleted. If you need control over deleting chat/video data please refer to the [Chat documentation](/chat/docs/react/update-users/#deleting-many-users).


## Authless users

Stream lets you give unauthenticated users access to a limited subset of Stream's capabilities. This is done by using either Guest or Anonymous users.

These two user types are ideal for use cases where users either need to be able to see feed activity prior to authenticating or in scenarios where the additional friction of creating a user account might be unnecessary for a user.

<admonition type="info">

Configuration for `guest` and `anonymous` roles can be managed in the [dashboard](https://getstream.io/dashboard/).

</admonition>

### Guest Users

Guest sessions can be created client-side and do not require any server-side authentication. Use cases like support or public or visible feeds often benefit from guest users, because you may want a visitor to be able to view or interact with feeds on your application without (or before) having a regular user account. Guest users are temporary users.

Guest users are not available to applications using multi-tenancy (teams).

<admonition type="info">

Guest users are counted towards your MAU usage.

</admonition>

You can generate a guest user in a front end client by using the following code:

<Tabs>

```js label="React"
import { useCreateFeedsClient } from "@stream-io/feeds-react-sdk";

const client = useCreateFeedsClient({
  apiKey,
  userData: { id: "tommaso" },
  tokenOrProvider: "guest",
});
```

```js label="React Native"
import { useCreateFeedsClient } from "@stream-io/feeds-react-native-sdk";

const client = useCreateFeedsClient({
  apiKey,
  userData: { id: "tommaso" },
  tokenOrProvider: "guest",
});
```

```js label="JavaScript"
import { FeedsClient } from "@stream-io/feeds-client";

const client = new FeedsClient("<API key>");
await client.connectGuest({ id: "tommaso" });
```

</Tabs>

To create a guest user from your server and obtain a token for the client:

<Tabs>

```js label="Node.js"
const response = await client.createGuest({
  user: { id: "tommaso", name: "Tommaso" },
});
// Return response.user and response.access_token to your client
```

```python label="Python"
response = self.client.create_guest(
    user=UserRequest(id="tommaso", name="Tommaso")
)
# Return response.user and response.access_token to your client
```

```ruby label="Ruby"
response = client.common.create_guest(
  GetStream::Generated::Models::CreateGuestRequest.new(
    user: { 'id' => 'tommaso', 'name' => 'Tommaso' }
  )
)
# Return response.user and response.access_token to your client
```

```php label="PHP"
$response = $client->createGuest(new GeneratedModels\CreateGuestRequest(
    user: [
        'id' => 'tommaso',
        'name' => 'Tommaso',
    ]
));
// Return $response->user and $response->access_token to your client
```

```go label="Go"
response, err := client.CreateGuest(context.Background(), &getstream.CreateGuestRequest{
    User: getstream.UserRequest{
        ID:   "tommaso",
        Name: getstream.PtrTo("Tommaso"),
    },
})
if err != nil {
    log.Fatal("Error creating guest:", err)
}
// Return response.Data.User and response.Data.AccessToken to your client
```

```csharp label="C#"
var request = new CreateGuestRequest
{
    User = new UserRequest { ID = "tommaso", Name = "Tommaso" }
};
var response = await _client.CreateGuestAsync(request);
// Return response.User and response.AccessToken to your client
```

```java label="Java"
CreateGuestRequest request = CreateGuestRequest.builder()
    .user(UserRequest.builder().id("tommaso").name("Tommaso").build())
    .build();
CreateGuestResponse response = client.createGuest(request).execute().getData();
// Return response.getUser() and response.getAccessToken() to your client
```

</Tabs>

<admonition type="info">

The user object schema is the same as the one described in the [Built-in fields for users](#built-in-fields-for-users) section above.

</admonition>

<admonition type="info">

Creation of guest users can be disabled for your application in the [dashboard](https://getstream.io/dashboard/).

</admonition>

### Anonymous Users

While anonymous, users have limited capabilities by default, but they can still read feed content where allowed.

<Tabs>

```js label="React"
import { useCreateFeedsClient } from "@stream-io/feeds-react-sdk";

const client = useCreateFeedsClient({
  apiKey,
  tokenOrProvider: "anonymous",
});
```

```js label="React Native"
import { useCreateFeedsClient } from "@stream-io/feeds-react-native-sdk";

const client = useCreateFeedsClient({
  apiKey,
  tokenOrProvider: "anonymous",
});
```

```js label="JavaScript"
import { FeedsClient } from "@stream-io/feeds-client";

const client = new FeedsClient("<API key>");
const connectResponse = await client.connectAnonymous();
console.log(connectResponse.me);
```

</Tabs>

<admonition type="info">

Anonymous users are not counted toward your MAU and can't establish WebSocket connection.

</admonition>

<admonition type="info">

Anonymous users are not allowed to perform any write operations.

</admonition>


---

This page was last updated at 2026-07-17T15:25:04.409Z.

For the most recent version of this documentation, visit [https://getstream.io/activity-feeds/docs/java/user-management/](https://getstream.io/activity-feeds/docs/java/user-management/).