# Activities

## Creating Activities

The example below shows how to create an activity and add it to a feed.

<Tabs>

```js label="JavaScript"
// Add an activity to 1 feed
const response = await feed.addActivity({
  type: "post",
  text: "apple stock will go up",
});

console.log(response.activity);

//...or multiple feeds
const response = await client.addActivity({
  feeds: ["user:1", "stock:apple"],
  type: "post",
  text: "apple stock will go up",
});
```

```js label="Node.js"
// Add an activity to 1 feed or multiple feeds
const response = await client.feeds.addActivity({
  feeds: ["user:1", "stock:apple"],
  type: "post",
  text: "apple stock will go up",
  // Provide user id, the owner of the activity
  user_id: "<user id>",
});

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

</Tabs>

<Admonition type="info">

Adding activities uses upsert logic. If the activity already exists, it will be updated with the new data.

</Admonition>

<Admonition type="info" title="Auto-creating feeds">

You do not need to call `getOrCreate` on a feed before adding an activity to it. If you add an activity to a feed that does not exist yet, the feed is created automatically. This applies to both add activity and add activities (batch) requests.

</Admonition>

The above example was quite simple. Here are a few more examples:

### Image & Video

<Admonition type="info">

You can use [Stream's CDN](https://getstream.io/activity-feeds/docs/node/file-uploads/) for storing and quickly accessing files attached to activities.

</Admonition>

<Tabs>

```js label="JavaScript"
feed.addActivity({
  type: "post",
  text: "look at NYC",
  attachments: [
    {
      type: "image",
      image_url: "https://example.com/image.png",
      custom: {},
    },
  ],
});
```

```js label="Node.js"
client.feeds.addActivity({
  feeds: ["user:1"],
  type: "post",
  text: "look at NYC",
  attachments: [
    {
      type: "image",
      image_url: "https://example.com/image.png",
      custom: {},
    },
  ],
  // Provide user id, the owner of the activity
  user_id: "<user id>",
});
```

</Tabs>

### Sharing activities

When creating an activity, it's possible to set `parent_id`; setting this field will increase the `share_count` of the parent activity. This feature lets you implement "retweets".

When reading an activity with parent id set, you can access the parent activity with `activity.parent`.

<Tabs>

```js label="JavaScript"
const response = await feed.addActivity({
  type: "post",
  text: `Couldn't agree more!`,
  parent_id: activityToShare.id,
});

console.log(response.activity?.parent);
```

```js label="Node.js"
// Add an activity to 1 feed or multiple feeds
const response = await client.feeds.addActivity({
  feeds: ["user:1"],
  text: `Couldn't agree more!`,
  parent_id: activityToShare.id,
  // Provide user id, the owner of the activity
  user_id: "<user id>",
});

console.log(response.activity?.parent);
```

</Tabs>

### Restricting comment replies

When creating an activity it's possible to set who can add comments. Possible options:

- Everyone (this is the default setting)
- Only people I follow
- Nobody

<Admonition type="info">

The activity author is always allowed to add comments regardless of the reply settings.

</Admonition>

<Tabs>

```js label="JavaScript"
const response = await feed.addActivity({
  type: "post",
  text: "apple stock will go up",
  restrict_replies: "people_i_follow", // Options: "everyone", "people_i_follow", "nobody"
});
```

```js label="Node.js"
const response = await client.feeds.addActivity({
  feeds: ["user:1"],
  type: "post",
  text: "apple stock will go up",
  user_id: "<user id>",
  restrict_replies: "people_i_follow", // Options: "everyone", "people_i_follow", "nobody"
});
```

</Tabs>

### Activity request options

The following options can be provided when creating an activity:

<h5 id="AddActivityRequest"><a href="#AddActivityRequest">AddActivityRequest</a></h5><table><thead><tr><th>Name</th><th>Type</th><th>Description</th><th>Constraints</th></tr></thead><tbody><tr><td><code>attachments</code></td><td><code>Attachment[]</code></td><td>List of attachments for the activity</td><td>-</td></tr><tr><td><code>collection_refs</code></td><td><code>string[]</code></td><td>Collections that this activity references</td><td>-</td></tr><tr><td><code>copy_custom_to_notification</code></td><td><code>boolean</code></td><td>Whether to copy custom data to the notification activity (only applies when create_notification_activity is true)</td><td>-</td></tr><tr><td><code>create_notification_activity</code></td><td><code>boolean</code></td><td>Whether to create notification activities for mentioned users</td><td>-</td></tr><tr><td><code>custom</code></td><td><code>object</code></td><td>Custom data for the activity</td><td>-</td></tr><tr><td><code>enrich_own_fields</code></td><td><code>boolean</code></td><td>-</td><td>-</td></tr><tr><td><code>expires_at</code></td><td><code>string</code></td><td>Expiration time for the activity</td><td>-</td></tr><tr><td><code>feeds</code></td><td><code>string[]</code></td><td>List of feeds to add the activity to with a default max limit of 25 feeds</td><td>Required</td></tr><tr><td><code>filter_tags</code></td><td><code>string[]</code></td><td>Tags for filtering activities</td><td>-</td></tr><tr><td><code>id</code></td><td><code>string</code></td><td>Optional ID for the activity</td><td>-</td></tr><tr><td><code>interest_tags</code></td><td><code>string[]</code></td><td>Tags for indicating user interests</td><td>-</td></tr><tr><td><code>location</code></td><td><code>ActivityLocation</code></td><td>Geographic location related to the activity</td><td>-</td></tr><tr><td><code>mentioned_user_ids</code></td><td><code>string[]</code></td><td>List of users mentioned in the activity</td><td>-</td></tr><tr><td><code>parent_id</code></td><td><code>string</code></td><td>ID of parent activity for replies/comments</td><td>-</td></tr><tr><td><code>poll_id</code></td><td><code>string</code></td><td>ID of a poll to attach to activity</td><td>-</td></tr><tr><td><code>restrict_replies</code></td><td><code>string (everyone, people_i_follow, nobody)</code></td><td>Controls who can add comments/replies to this activity. One of: everyone, people_i_follow, nobody</td><td>-</td></tr><tr><td><code>search_data</code></td><td><code>object</code></td><td>Additional data for search indexing</td><td>-</td></tr><tr><td><code>skip_enrich_url</code></td><td><code>boolean</code></td><td>Whether to skip URL enrichment for the activity</td><td>-</td></tr><tr><td><code>skip_push</code></td><td><code>boolean</code></td><td>Whether to skip push notifications</td><td>-</td></tr><tr><td><code>text</code></td><td><code>string</code></td><td>Text content of the activity</td><td>-</td></tr><tr><td><code>type</code></td><td><code>string</code></td><td>Type of activity</td><td>Required</td></tr><tr><td><code>user_id</code></td><td><code>string</code></td><td>ID of the user creating the activity</td><td>-</td></tr><tr><td><code>visibility</code></td><td><code>string (public, private, tag)</code></td><td>Visibility setting for the activity. One of: public, private, tag</td><td>-</td></tr><tr><td><code>visibility_tag</code></td><td><code>string</code></td><td>If visibility is &#39;tag&#39;, this is the tag name and is required</td><td>-</td></tr></tbody></table>

### Activity size limit

Total activity size (including built-in and custom fields) must not exceed 10KB (the limit is checked when writing activities, not when reading them). If your use case requires a larger payload, [collections](https://getstream.io/activity-feeds/docs/node/collections/) help attach data to activities without increasing activity size.

## Reading Activities with Enrichment

When you read activities from feeds, they are automatically enriched with additional data:

- **Comments**: The latest 5 top-level comments (replies not included by default)
- **Reactions**: Recent reactions and reaction counts
- **User data**: Information about the activity author
- **Collections**: Any collections the activity references

To load more comments, replies, or additional data, use the dedicated loading methods described in the Comments and Reactions sections.

## Translation

Activity text can be translated on demand and projected when reading feeds and activities. See [Translation](https://getstream.io/activity-feeds/docs/node/translation/) for translate endpoints, read-time `language` projection, and the `i18n` object format.

## Overview of All Activity Fields

<h3 id="ActivityResponse"><a href="#ActivityResponse">ActivityResponse</a></h3><table><thead><tr><th>Name</th><th>Type</th><th>Description</th><th>Constraints</th></tr></thead><tbody><tr><td><code>attachments</code></td><td><code>Attachment[]</code></td><td>Media attachments for the activity</td><td>Required</td></tr><tr><td><code>bookmark_count</code></td><td><code>integer</code></td><td>Number of bookmarks on the activity</td><td>Required</td></tr><tr><td><code>collections</code></td><td><code>object</code></td><td>Enriched collection data referenced by this activity</td><td>Required</td></tr><tr><td><code>comment_count</code></td><td><code>integer</code></td><td>Number of comments on the activity</td><td>Required</td></tr><tr><td><code>comments</code></td><td><code>CommentResponse[]</code></td><td>Latest 5 comments of this activity (comment replies excluded)</td><td>Required</td></tr><tr><td><code>created_at</code></td><td><code>number</code></td><td>When the activity was created</td><td>Required</td></tr><tr><td><code>current_feed</code></td><td><code>FeedResponse</code></td><td>Feed context for this activity view. If an activity is added only to one feed, it&#39;s always set. If an activity is added to multiple feeds, it&#39;s only set when calling the GetOrCreateFeed endpoint.</td><td>-</td></tr><tr><td><code>custom</code></td><td><code>object</code></td><td>Custom data for the activity</td><td>Required</td></tr><tr><td><code>deleted_at</code></td><td><code>number</code></td><td>When the activity was deleted</td><td>-</td></tr><tr><td><code>edited_at</code></td><td><code>number</code></td><td>When the activity was last edited</td><td>-</td></tr><tr><td><code>expires_at</code></td><td><code>number</code></td><td>When the activity will expire</td><td>-</td></tr><tr><td><code>feeds</code></td><td><code>string[]</code></td><td>List of feed IDs containing this activity</td><td>Required</td></tr><tr><td><code>filter_tags</code></td><td><code>string[]</code></td><td>Tags for filtering</td><td>Required</td></tr><tr><td><code>friend_reaction_count</code></td><td><code>integer</code></td><td>Total count of reactions from friends on this activity</td><td>-</td></tr><tr><td><code>friend_reactions</code></td><td><code>FeedsReactionResponse[]</code></td><td>Reactions from users the current user follows or has mutual follows with</td><td>-</td></tr><tr><td><code>hidden</code></td><td><code>boolean</code></td><td>If this activity is hidden by this user (using activity feedback)</td><td>Required</td></tr><tr><td><code>id</code></td><td><code>string</code></td><td>Unique identifier for the activity</td><td>Required</td></tr><tr><td><code>interest_tags</code></td><td><code>string[]</code></td><td>Tags for user interests</td><td>Required</td></tr><tr><td><code>is_read</code></td><td><code>boolean</code></td><td>Whether this activity has been read. Only set for feed groups with notification config (track_seen/track_read enabled).</td><td>-</td></tr><tr><td><code>is_seen</code></td><td><code>boolean</code></td><td>Whether this activity has been seen. Only set for feed groups with notification config (track_seen/track_read enabled).</td><td>-</td></tr><tr><td><code>is_watched</code></td><td><code>boolean</code></td><td>-</td><td>-</td></tr><tr><td><code>latest_reactions</code></td><td><code>FeedsReactionResponse[]</code></td><td>Recent reactions to the activity</td><td>Required</td></tr><tr><td><code>location</code></td><td><code>ActivityLocation</code></td><td>Geographic location related to the activity</td><td>-</td></tr><tr><td><code>mentioned_users</code></td><td><code>UserResponse[]</code></td><td>Users mentioned in the activity</td><td>Required</td></tr><tr><td><code>metrics</code></td><td><code>object</code></td><td>-</td><td>-</td></tr><tr><td><code>moderation</code></td><td><code>ModerationV2Response</code></td><td>Moderation information</td><td>-</td></tr><tr><td><code>moderation_action</code></td><td><code>string</code></td><td>-</td><td>-</td></tr><tr><td><code>notification_context</code></td><td><code>NotificationContext</code></td><td>Notification context data for the activity (if this is a reaction, comment, follow, etc.)</td><td>-</td></tr><tr><td><code>own_bookmarks</code></td><td><code>BookmarkResponse[]</code></td><td>Current user&#39;s bookmarks for this activity</td><td>Required</td></tr><tr><td><code>own_reactions</code></td><td><code>FeedsReactionResponse[]</code></td><td>Current user&#39;s reactions to this activity</td><td>Required</td></tr><tr><td><code>parent</code></td><td><code>ActivityResponse</code></td><td>Parent activity (if this is a reply/comment)</td><td>-</td></tr><tr><td><code>poll</code></td><td><code>PollResponseData</code></td><td>Poll attached to this activity</td><td>-</td></tr><tr><td><code>popularity</code></td><td><code>integer</code></td><td>Popularity score of the activity</td><td>Required</td></tr><tr><td><code>preview</code></td><td><code>boolean</code></td><td>If this activity is obfuscated for this user. For premium content where you want to show a preview</td><td>Required</td></tr><tr><td><code>reaction_count</code></td><td><code>integer</code></td><td>Number of reactions to the activity</td><td>Required</td></tr><tr><td><code>reaction_groups</code></td><td><code>object</code></td><td>Grouped reactions by type</td><td>Required</td></tr><tr><td><code>restrict_replies</code></td><td><code>string (everyone, people_i_follow, nobody)</code></td><td>Controls who can add comments/replies to this activity. One of: everyone, people_i_follow, nobody</td><td>Required</td></tr><tr><td><code>score</code></td><td><code>number</code></td><td>Ranking score for this activity</td><td>Required</td></tr><tr><td><code>score_vars</code></td><td><code>object</code></td><td>Variable values used at ranking time. Only included when include_score_vars is enabled in enrichment options.</td><td>-</td></tr><tr><td><code>search_data</code></td><td><code>object</code></td><td>Data for search indexing</td><td>Required</td></tr><tr><td><code>selector_source</code></td><td><code>string</code></td><td>Which activity selector provided this activity (e.g., &#39;following&#39;, &#39;popular&#39;, &#39;interest&#39;). Only set when using multiple activity selectors with ranking.</td><td>-</td></tr><tr><td><code>share_count</code></td><td><code>integer</code></td><td>Number of times the activity was shared</td><td>Required</td></tr><tr><td><code>text</code></td><td><code>string</code></td><td>Text content of the activity</td><td>-</td></tr><tr><td><code>type</code></td><td><code>string</code></td><td>Type of activity</td><td>Required</td></tr><tr><td><code>updated_at</code></td><td><code>number</code></td><td>When the activity was last updated</td><td>Required</td></tr><tr><td><code>user</code></td><td><code>UserResponse</code></td><td>User who created the activity</td><td>Required</td></tr><tr><td><code>visibility</code></td><td><code>string (public, private, tag)</code></td><td>Visibility setting for the activity. One of: public, private, tag</td><td>Required</td></tr><tr><td><code>visibility_tag</code></td><td><code>string</code></td><td>If visibility is &#39;tag&#39;, this is the tag name</td><td>-</td></tr></tbody></table>

## Adding Many Activities

You can also batch add activities. Here's an example:

<Tabs>

```js label="JavaScript"
client.upsertActivities({
  activities: [
    {
      feeds: ["user:123"],
      id: "1",
      type: "post",
      text: "hi",
    },
    {
      feeds: ["user:456"],
      id: "2",
      type: "post",
      text: "hi",
    },
  ],
});
```

```js label="Node.js"
client.feeds.upsertActivities({
  activities: [
    {
      feeds: ["user:123"],
      id: "1",
      type: "post",
      text: "hi",
      user_id: "<user id>",
    },
    {
      feeds: ["user:456"],
      id: "2",
      type: "post",
      text: "hi",
      user_id: "<user id>",
    },
  ],
});
```

</Tabs>

## Visibility Levels

When creating an activity, you can set a visibility level for the activity:

<Admonition type="info">

Please note that activity visibility is not the same as [feed visibility](https://getstream.io/activity-feeds/docs/node/feed-and-activity-visibility/)

</Admonition>

- `public`: marks the activity as public - everyone who can view feed content, can see it
- `private`: marks the activity as private - only feed owner can see it
- `tag:mytag`: marks the activity as only visible to followers/members with the permission to see this tag

This visibility system is very flexible and allows you to build:

- Apps like Patreon where only certain levels of users can see your content
- Apps like Strava where it's possible to share your activity with nobody, everyone or your followers

<Tabs>

```js label="JavaScript"
feed.addActivity({
  type: "post",
  text: "Premium content",
  visibility: "tag",
  visibility_tag: "premium",
});
// Premium users can see full activity, others a preview
```

```js label="Node.js"
client.feeds.addActivity({
  feeds: ["user:1"],
  type: "post",
  text: "Premium content",
  visibility: "tag",
  visibility_tag: "premium",
  user_id: "<user id>",
});
// Premium users can see full activity, others a preview
```

</Tabs>

For all the details on tag visibility read the [Membership levels guide](https://getstream.io/activity-feeds/docs/node/membership-levels/).

## Partial activity updates

A partial update can be used to set or unset specific fields and leave other fields unchanged (also known as a patch-style update). Both `set` and `unset` can be used in the same request. The dotted-notation is also available for both `set` and `unset` for the `custom` field.

<Admonition type="info">

You can set `run_activity_processors` to `true` to run [activity processors](https://getstream.io/activity-feeds/docs/node/activity-processors/) on the updated activity. Processors will only run if the activity text and/or attachments are changed. This flag defaults to `false`.

**Interest tags behavior:**

- If `run_activity_processors` is `true`: New tags generated from text/image are appended to existing tags.
- If `run_activity_processors` is `false` or not set: Existing tags are preserved unchanged.

</Admonition>

<Tabs>

```js label="JavaScript"
// Partially set some fields
const response = await client.updateActivityPartial({
  id: activity.id,
  set: {
    text: "Japan has over 6,800 islands.",
  },
  run_activity_processors: true, // Run processors if text/attachments changed
});
console.log(response.activity.edited_at);

// Partially unset some fields
const response = await client.updateActivityPartial({
  id: activity.id,
  unset: ["custom.color"],
});
console.log(response.activity.edited_at);
```

```js label="Node.js"
// Partially set some fields
const response = await client.feeds.updateActivityPartial({
  id: activity.id,
  set: {
    text: "Japan has over 6,800 islands.",
  },
  user_id: "<user id>",
  run_activity_processors: true, // Run processors if text/attachments changed
});
console.log(response.activity.edited_at);

// Partially unset some fields
const response = await client.feeds.updateActivityPartial({
  id: activity.id,
  unset: ["custom.color"],
  user_id: "<user id>",
});
console.log(response.activity.edited_at);
```

</Tabs>

## Batch Partial Activity Updates

You can perform partial updates on multiple activities in a single batch operation. This is more efficient than updating activities one by one when you need to update several activities at once.

```csharp label="C#"
// Update multiple activities with different changes
var partialUpdateRequest = new UpdateActivitiesPartialBatchRequest
{
    Changes = new List<UpdateActivityPartialChangeRequest>
    {
        new UpdateActivityPartialChangeRequest
        {
            ActivityID = activityId1,
            Set = new Dictionary<string, object>
            {
                ["text"] = "Updated text for activity 1",  // Update main text
                ["likes"] = 25,  // Update custom field
                ["status"] = "featured"  // Update custom field
            },
            Unset = new List<string> { "priority" }  // Remove custom field
        },
        new UpdateActivityPartialChangeRequest
        {
            ActivityID = activityId2,
            Set = new Dictionary<string, object>
            {
                ["text"] = "Updated text for activity 2",  // Update main text
                ["likes"] = 15,  // Update custom field
                ["status"] = "published"  // Update custom field
            },
            Unset = new List<string> { "views", "category" }  // Remove multiple custom fields
        }
    }
};

var response = await _feedsV3Client.UpdateActivitiesPartialBatchAsync(partialUpdateRequest);
Console.WriteLine($"Updated {response.Data.Activities.Count} activities");
```

This operation allows you to:

- Update specific fields in multiple activities
- Use both `set` (to update/add fields) and `unset` (to remove fields) operations
- Process all changes in a single API call for better performance

**Updatable Fields:**

The following reserved fields can be updated:

- `text` - Activity text content
- `attachments` - Media attachments
- `visibility` - Activity visibility level
- `visibility_tag` - Visibility tag
- `expires_at` - Expiration timestamp
- `filter_tags` - Filter tags for querying
- `interest_tags` - Interest tags
- `collection_refs` - Collection references
- `feeds` - Feeds the activity belongs to
- `mentioned_user_ids` - Mentioned users
- `poll_id` - Associated poll
- Any custom fields (stored in `custom` object)

**Note:** The `type` field cannot be changed after an activity is created. Other immutable fields include `id`, `user_id`, `created_at`, etc.

## Updating Activities

This example shows how to fully update an activity:

<Admonition type="info">

You can set `run_activity_processors` to `true` to run [activity processors](https://getstream.io/activity-feeds/docs/node/activity-processors/) on the updated activity. Processors will only run if the activity text and/or attachments are changed. This flag defaults to `false`.

**Interest tags behavior:**

- If `run_activity_processors` is `false` or not set, and no `interest_tags` are provided: `interest_tags` are set to an empty array.
- If `run_activity_processors` is `true` and no `interest_tags` are sent: New tags are generated from text/image (replaces existing tags).
- If `run_activity_processors` is `true` and `interest_tags` are sent: Generated tags are merged with the provided tags.
- You can modify existing tags by providing them in the request before updating.

</Admonition>

<Tabs>

```js label="JavaScript"
// NOTE: updateActivity does a full replace of the activity.
// Use updateActivityPartial if you only want to update specific fields.
const response = await client.updateActivity({
  id: "123",
  text: "Updated text",
  run_activity_processors: true, // Run processors if text/attachments changed
});
console.log(response.activity.edited_at);
```

```js label="Node.js"
// NOTE: updateActivity does a full replace of the activity.
// Use updateActivityPartial if you only want to update specific fields.
const response = await client.feeds.updateActivity({
  id: "123",
  text: "Updated text",
  user_id: "<user id>",
  run_activity_processors: true, // Run processors if text/attachments changed
});
console.log(response.activity.edited_at);
```

</Tabs>

## Deleting Activities

This example shows how to delete an activity:

<Tabs>

```js label="JavaScript"
client.deleteActivity({
  id: "123",
  hard_delete: false, // Soft delete sets deleted_at but retains the data, hard delete fully removes it
});

// Batch delete activities
client.deleteActivities({
  id: ["123", "456"],
  hard_delete: false,
});
```

```js label="Node.js"
client.feeds.deleteActivity({
  id: "123",
  hard_delete: false, // Soft delete sets deleted_at but retains the data, hard delete fully removes it
  user_id: "<user id>",
});

// Batch delete activities
client.feeds.deleteActivities({
  id: ["123", "456"],
  user_id: "<user id>",
  hard_delete: false,
});
```

</Tabs>

<Admonition type="info">

Note: Deleting activities has automatic side effects that are irreversible.

- On hard delete, [comments](https://getstream.io/activity-feeds/docs/node/comments) on the activity are removed.
- [Bookmarks](https://getstream.io/activity-feeds/docs/node/bookmarks) related to the deleted activity are removed.
- [Pins](https://getstream.io/activity-feeds/docs/node/pins) related to the deleted activity are removed.

On soft delete, comments are kept so a [restored](#restore-activity) activity retains its original context. [Restoring](#restore-activity) an activity will not bring back bookmarks or pins.

</Admonition>

## Restore Activity

If an activity was soft-deleted, it can be restored using the restore endpoint. Only the activity owner can restore their own activities (for client-side requests). Hard-deleted activities cannot be restored.

<Tabs>

```js label="JavaScript"
// Restore a soft-deleted activity
const restoredActivity = await client.restoreActivity({
  id: "123",
});
```

```js label="Node.js"
// Restore a soft-deleted activity
const restoredActivity = await client.feeds.restoreActivity({
  id: "123",
  user_id: "<user id>",
});
```

</Tabs>

When an activity is restored, a `feeds.activity.restored` event is sent to all feeds the activity is part of. There is no default event handler in client-side SDKs but it's possible to [add a custom handler](https://getstream.io/activity-feeds/docs/node/events/).

## Get activity

Fetching a single activity.

When implementing an activity details page on the client-side, it may be important to receive updates for the activity. The activity state is automatically updated on HTTP requests initiated from the client. To receive real-time updates (to receive updates from other users, for example someone else liked this post), you have to watch the feed the activity belongs to (or one of the feeds, in case it belongs to multiple feeds).

When fetching an activity that belongs to multiple feeds, `activity.current_feed` will be empty. If you wish to display feed information alongside the activity, you have to fetch any of the containing feeds with a separate API call.

<Tabs>

```js label="JavaScript"
const activityWithStateUpdates =
  client.activityWithStateUpdates(activityId);
await activityWithStateUpdates.get({
  // Optionally fetch comments too
  comments: {
    limit: 10,
    depth: 2,
  },
});

// Subscribe to state updates
activityWithStateUpdates.state.subscribe((state) => {
  console.log(state.activity);
  console.log(state.comments_by_entity_id);
  // True if activity is being fetched
  console.log(state.is_loading);
});
// Comment pagination
activityWithStateUpdates.loadNextPageActivityComments;
activityWithStateUpdates.loadNextPageCommentReplies;

// Optionally start watching the feed
// If activity belongs to multiple feeds, it's up to you to choose which feed to watch
const fid = activityWithStateUpdates.currentState.activity!.feeds[0];
const [group, id] = fid.split(':');
const feed = client.feed(group, id);
let shouldWatch = false;
if (!feed.currentState.watch) {
  await feed.getOrCreate({
    watch: true,
    limit: 0,
    followers_pagination: { limit: 0 },
    following_pagination: { limit: 0 },
  });
}

// When leaving the page...
// Dispose the activity; this avoids refetching the activity if WebSocket reconnects
activityWithStateUpdates.dispose();
// You should stop watching the feed, unless your app has another component that watches the same feed
if (shouldWatch) {
  await feed.stopWatching();
}

// If you don't care about state updates, no need to call activityWithStateUpdates
await client.getActivity({
  id: activityId,
});
```

```js label="Node.js"
const activity = await client.feeds.getActivity({ id: "activity123" });
```

</Tabs>

## Adding activities to multiple feeds

The Stream API allows you to post an activity to multiple feeds, the maximum is 25.

When an activity is posted to multiple feeds, `activity.current_feed` field is only set when reading a feed (`feed.getOrCreate`):

- When reading with `current` selector, it's set to the feed we're reading
- For any other selector it'll be one of the feeds the activity is posted to
  - If the user follows one of the feeds, it's set to the followed feed (if more than one feed is followed, it'll be one of the feeds)
- For all other API calls (for example `queryActivities`, `getActivity`) and WebSocket events it's not set

<Tabs>

```js label="JavaScript"
// Add an activity to 1 feed
const response = await feed.addActivity({
  type: "post",
  text: "apple stock will go up",
});

console.log(response.activity);

//...or multiple feeds
const response = await client.addActivity({
  feeds: ["user:1", "stock:apple"],
  type: "post",
  text: "apple stock will go up",
});
```

```js label="Node.js"
// Add an activity to 1 feed or multiple feeds
const response = await client.feeds.addActivity({
  feeds: ["user:1", "stock:apple"],
  type: "post",
  text: "apple stock will go up",
  // Provide user id, the owner of the activity
  user_id: "<user id>",
});

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

</Tabs>

<Admonition type="info">

Adding activities uses upsert logic. If the activity already exists, it will be updated with the new data.

</Admonition>

<Admonition type="info" title="Auto-creating feeds">

You do not need to call `getOrCreate` on a feed before adding an activity to it. If you add an activity to a feed that does not exist yet, the feed is created automatically. This applies to both add activity and add activities (batch) requests.

</Admonition>

## Activity list controls - client-side SDKs

Client-side SDKs allow controlling when and where to add new activities as they arrive. By default, only activities added by the current user and matching the `filter` provided to `getOrCreate` are added to the list (this is how social media apps usually work), and these activities are added to the start of the list. But it's possible to override this behavior:

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

const feed = client.feed("user", userId, {
  onNewActivity: ({ activity, currentUser }) => {
    const requestConfig = feed.currentState.last_get_or_create_request_config;
    if (!activityFilter(activity, requestConfig)) return "ignore";
    return activity.user.id === currentUser?.id ? "add-to-start" : "ignore";
  },
});
```


---

This page was last updated at 2026-09-08T17:13:37.747Z.

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