User management

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

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.
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)

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

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();

Built-in fields for users

UserResponse

NameTypeDescriptionConstraints
avg_response_timeinteger--
ban_expiresnumberDate when ban expires-
bannedbooleanWhether a user is banned or notRequired
blocked_user_idsstring[]-Required
created_atnumberDate/time of creationRequired
customobjectCustom data for this objectRequired
deactivated_atnumberDate of deactivation-
deleted_atnumberDate/time of deletion-
devicesDeviceResponse[]List of devices user is using-
idstringUnique user identifierRequired
imagestring--
invisibleboolean-Required
languagestringPreferred language of a userRequired
last_activenumberDate of last activity-
namestringOptional name of user-
onlinebooleanWhether a user online or notRequired
privacy_settingsPrivacySettingsResponseUser privacy settings-
push_notificationsPushNotificationSettingsResponseUser push notification settings-
revoke_tokens_issued_beforenumberRevocation date for tokens-
rolestringDetermines the set of user permissionsRequired
shadow_bannedbooleanWhether a user is shadow bannedRequired
teamsstring[]List of teams user is a part ofRequired
teams_roleobject--
updated_atnumberDate/time of the last updateRequired

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.

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))

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.

Supported Filters

NameTypeDescriptionAllowed Operators
idstringID of the user$eq, $gt, $gte, $lt, $lte, $in, $autocomplete
rolestringRole of the user$eq, $gt, $gte, $lt, $lte, $in
bannedbooleanWhether the user is banned$eq
shadow_bannedbooleanWhether the user is shadow banned$eq
created_atstring, must be formatted as an RFC3339 timestampTime when the user was created$eq, $gt, $gte, $lt, $lte, $in
updated_atstring, must be formatted as an RFC3339 timestampTime when the user was updated$eq, $gt, $gte, $lt, $lte, $in
last_activestring, must be formatted as an RFC3339 timestampTime when the user was last active$eq, $gt, $gte, $lt, $lte, $in, $exists
teamsstringTeams the user belongs to$eq, $contains
namestringName of the user$eq, $autocomplete
usernamestringUsername of the user$eq, $autocomplete
custom propertiesstring, boolean, intCustom user properties$eq, $gt, $gte, $lt, $lte, $in

Supported Sort

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

NameDescription
idUser ID
created_atTime when the user was created
updated_atTime when the user was updated
last_activeTime when the user was last active
roleRole of the user

Supported Options

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

NameTypeDescriptionDefaultOptional
limitintegerNumber of users to return30
offsetintegerOffset for pagination0
id_gtstringID-based pagination. Return IDs greater than this ID. If this is not empty, the default sort order will be [{id: -1}].-
id_gtestringID-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_ltstringID-based pagination. Return IDs less than this ID. If this is not empty, the default sort order will be [{id: -1}].-
id_ltestringID-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_usersbooleanInclude deactivated users in the response-

The maximum offset value is 1000.

Filter examples

You can use various filter operators to query users:

// 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.age": map[string]any{"$gte": 18},
    },
})

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

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
// 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)

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

// 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")

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

// 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")

Deleting users

// 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)

The delete users endpoints supports the following parameters to control which data needs to be deleted and how. By default users and their data are soft-deleted.

NameTypeDescriptionOptional
userEnum (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.
Yes
conversationsEnum (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).
Yes
messagesEnum (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.
Yes
new_channel_owner_idstringChannels owned by hard-deleted users will be transferred to this userID. If you doesn't provide a value, the channel owner will have a system generated ID like delete-user-8219f6578a7395gYes
callsEnum (soft, hard)- Soft: marks calls and related data as deleted.
- Hard: deletes calls and related data completely
Note that this applies only to 1:1 calls, not group calls
Yes

Deleting users in bulk can take some time, this is how you can check the progress:

// 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")

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.

Configuration for guest and anonymous roles can be managed in the dashboard.

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).

Guest users are counted towards your MAU usage.

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

import { FeedsClient } from "@stream-io/feeds-client";

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

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

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

The user object schema is the same as the one described in the Built-in fields for users section above.

Creation of guest users can be disabled for your application in the dashboard.

Anonymous Users

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

import { FeedsClient } from "@stream-io/feeds-client";

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

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

Anonymous users are not allowed to perform any write operations.