# Notification Feeds

Notification feeds let you notify users about relevant interactions, for example

- someone started to follow them
- someone liked their post
- someone left a comment on their post

## Creating notification feeds

The built-in `notification` feed comes with the necessary configurations, but it's also possible to create your own notification feed group:

<Tabs>

```js label="Node"
const myNotificationGrpup = await serverClient.feeds.createFeedGroup({
  id: "myid",
  // Group by activity type and day
  aggregation: { format: '{{ type }}-{{ time.strftime("%Y-%m-%d") }}' },
  // Enable notification tracking
  notification: {
    track_read: true,
    track_seen: true,
  },
});
```

```go label="Go"
myNotificationGroup, err := client.Feeds().CreateFeedGroup(context.Background(), &getstream.CreateFeedGroupRequest{
    ID: "myid2",
    // Group by activity type and day
    Aggregation: &getstream.AggregationConfig{
        Format: getstream.PtrTo("{{ type }}-{{ time.strftime(\"%Y-%m-%d\") }}"),
    },
    // Enable notification tracking
    Notification: &getstream.NotificationConfig{
        TrackRead: getstream.PtrTo(true),
        TrackSeen: getstream.PtrTo(true),
    },
})
if err != nil {
    log.Fatal("Error creating feed group:", err)
}
```

```php label="php"
// Create notification feed group with aggregation and tracking
$request = new GeneratedModels\CreateFeedGroupRequest(
    id: "myid",
    defaultVisibility: 'public',
    // Group by activity type and day
    aggregation: new GeneratedModels\AggregationConfig(
        format: '{{ type }}-{{ time.strftime("%Y-%m-%d") }}'
    ),
    // Enable notification tracking
    notification: new GeneratedModels\NotificationConfig(
        trackRead: true,
        trackSeen: true
    )
);

// Create the feed group
$response = $feedsClient->createFeedGroup($request);
```

```csharp label="C#"
await _feedsV3Client.CreateFeedGroupAsync(new CreateFeedGroupRequest
{
    ID = "aggregated-group",
    DefaultVisibility = "public",
    ActivityProcessors = new List<ActivityProcessorConfig>
    {
        new() { Type = "default" }
    },
    Aggregation = new AggregationConfig
    {
        Format = "{{ type }}-{{ time.strftime(\"%Y-%m-%d\") }}"
    }
});
```

```python label="Python"
self.client.feeds.create_feed_group(
    id = feed_group_id,
    default_visibility="public",
    activity_processors=[
        ActivityProcessorConfig(type="default")
    ],
    aggregation=AggregationConfig(
        format="{{ type }}-{{ time.strftime(\"%Y-%m-%d\") }}"
    )
)
```

```rb label="Ruby"
require 'getstream_ruby'

my_notification_group = client.feeds.create_feed_group(
  GetStream::Generated::Models::CreateFeedGroupRequest.new(
    id: 'myid',
    # Group by activity type and day
    aggregation: GetStream::Generated::Models::AggregationConfig.new(
      format: '{{ type }}-{{ time.strftime("%Y-%m-%d") }}'
    ),
    # Enable notification tracking
    notification: GetStream::Generated::Models::NotificationConfig.new(
      track_read: true,
      track_seen: true
    )
  )
)
```

</Tabs>


### Notification configuration

The notification config includes several settings that control how notifications are tracked and deduplicated:

- **TrackSeen**: When enabled, tracks which notifications have been seen by the user
- **TrackRead**: When enabled, tracks which notifications have been read by the user
- **DeduplicationWindow**: Controls how duplicate notifications are handled (only available for the built-in `notification` feed group)
  - Empty string (`""`) = always deduplicate (default behavior)
  - Duration string (e.g., `"24h"`, `"7d"`) = time-based deduplication window

<admonition type="info">

**Note:** Comments are not deduplicated. Each comment will create a separate notification activity, regardless of the deduplication window setting.

</admonition>

The built-in `notification` feed group has deduplication enabled by default (always deduplicate). To change the deduplication window, you need to update the `notification` feed group.

### Aggregation format

The built-in `notification` feed uses the following aggregation format: `"{{ target_id }}_{ type }}_{{ time.strftime('%Y-%m-%d') }}"`. You can change this syntax by updating the `notification` feed group.

You can see all supported fields and syntax for aggregation in the [Aggregation guide](/activity-feeds/docs/go-golang/aggregation/).

It's possible to turn off aggregation, but still enable notification tracking. In that case every new activity will increase `unread`/`unseen` count. For more information see the [Flat notifications section](#flat-notification-feeds).

When you have aggregation turned on, `unread`/`unseen` will refer to the number of aggregated groups.

### Adding notification activities

The built-in `notification` groups can automatically create notifications for the most common interactions (see [Built-in notification feed section](#built-in-notification-feed)).

If you want to extend that, or create your own notification feed, you can add notification activities using server-side integration. You can add webhook handlers for the relevant events to create notifications without API calls from your client-side application to your server-side application.

It's important to note that `target_id` is only defined if a notification is created by Stream API. If you want to extend or replace this behavior by adding notification activities from your own application, you most likely need to extend the default aggregation format. You can see all supported fields and syntax for aggregation in the [Aggregation guide](/activity-feeds/docs/go-golang/aggregation/).

### Manual Activity Addition to Notification Feeds

You can directly add activities to notification feeds for complete control over notifications (only available server-side):

<Tabs>

```js label="Node"
// Add a custom notification directly to a user's notification feed
await serverClient.feeds.addActivity({
  feeds: ["notification:user-123"], // Target user's notification feed
  type: "milestone", // Custom activity type
  text: "You've reached 1000 followers!",
  user_id: "user_id",
  extra_data: {
    milestone_type: "followers",
    count: 1000,
  },
});

// Add activity to custom notification feed group
await serverClient.feeds.addActivity({
  feeds: ["alerts:user-123"], // Custom notification feed group
  type: "system_alert",
  text: "Your subscription expires in 3 days",
  user_id: "user_id",
});
```

```go label="Go"
ctx := context.Background()

// Add a custom notification directly to a user's notification feed
_, err = client.Feeds().AddActivity(ctx, &getstream.AddActivityRequest{
  Feeds:  []string{"notification:john"}, // Target user's notification feed
  Type:   "milestone",                       // Custom activity type
  Text:   getstream.PtrTo("You've reached 1000 followers!"),
  UserID: getstream.PtrTo("<user id>"),
  Custom: map[string]any{
    "milestone_type": "followers",
    "count":          1000,
  },
})
if err != nil {
  log.Fatal("Error adding milestone activity:", err)
}

// Add activity to custom notification feed group
_, err = client.Feeds().AddActivity(ctx, &getstream.AddActivityRequest{
  Feeds:  []string{"alerts:john"}, // Custom notification feed group
  Type:   "system_alert",
  Text:   getstream.PtrTo("Your subscription expires in 3 days"),
  UserID: getstream.PtrTo("<user id>"),
})
if err != nil {
  log.Fatal("Error adding system alert activity:", err)
}
```

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

// Add a custom notification directly to a user's notification feed
$feedsClient->addActivity(new GeneratedModels\AddActivityRequest(
    feeds: ["notification:john"], // Target user's notification feed
    type: "milestone", // Custom activity type
    text: "You've reached 1000 followers!",
    userID: "<user id>",
    custom: (object)[
        "milestone_type" => "followers",
        "count" => 1000,
    ]
));

// Add activity to custom notification feed group
$feedsClient->addActivity(new GeneratedModels\AddActivityRequest(
    feeds: ["alerts:john"], // Custom notification feed group
    type: "system_alert",
    text: "Your subscription expires in 3 days",
    userID: "<user id>"
));
```

</Tabs>


**Important:** When adding activities directly to notification feeds, ensure the activity `type` is included in the feed group's `push_types` configuration to trigger push notifications.

## Built-in notification feed

### Creating notification activities

The built-in `notification` feed allows you to automatically create notification activities. The following actions are supported and will automatically create notification activities for the target user depending on the action.

| Action                             | Trigger User                                          | Target User (Recipient)           | Notification Type  | Notification Text                     | Deduplicated? | Notes                                                                                                                                                                                                                                                                                                                                                                                            |
| ---------------------------------- | ----------------------------------------------------- | --------------------------------- | ------------------ | ------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **React to Activity**              | User who reacts (e.g., Bob)                           | Activity author (e.g., Alice)     | `reaction`         | `{user} reacted to your activity`     | ✅ Yes        | Multiple reactions from the same user on the same activity are deduplicated within the deduplication window                                                                                                                                                                                                                                                                                      |
| **React to Comment**               | User who reacts (e.g., Charlie)                       | Comment author (e.g., Bob)        | `comment_reaction` | `{user} reacted to your comment`      | ✅ Yes        | Multiple reactions from the same user on the same comment are deduplicated within the deduplication window. The activity author does NOT receive a notification for reactions                                                                                                                                                                                                                    |
| **Comment on Activity**            | User who comments (e.g., Bob)                         | Activity author (e.g., Alice)     | `comment`          | `{user} commented on your activity`   | ❌ No         | Each comment creates a new notification; not deduplicated                                                                                                                                                                                                                                                                                                                                        |
| **Reply to Comment**               | User who replies (e.g., Charlie)                      | Comment author (e.g., Bob)        | `comment_reply`    | `{user} replied to your comment`      | ❌ No         | Each reply creates a new notification; not deduplicated. The activity author does NOT receive a notification for replies                                                                                                                                                                                                                                                                         |
| **Follow User**                    | User who follows (e.g., Bob)                          | User being followed (e.g., Alice) | `follow`           | `{user} started following you`        | ✅ Yes        | Multiple follows/unfollows from the same user are deduplicated within the deduplication window                                                                                                                                                                                                                                                                                                   |
| **Mention in Activity**            | User who creates activity with mention (e.g., Alice)  | Mentioned user (e.g., Bob)        | `mention`          | `{user} mentioned you in an activity` | ✅ Yes        | Multiple mentions from the same user on the same activity are deduplicated within the deduplication window                                                                                                                                                                                                                                                                                       |
| **Mention in Comment**             | User who creates comment with mention (e.g., Charlie) | Mentioned user (e.g., Bob)        | `comment_mention`  | `{user} mentioned you in a comment`   | ✅ Yes        | Multiple mentions from the same user on the same comment are deduplicated within the deduplication window. The activity author does NOT receive a notification for mentions (they already get a comment notification)                                                                                                                                                                            |
| **Update Activity (add mentions)** | User who updates activity (e.g., Alice)               | Mentioned user (e.g., Bob)        | `mention`          | `{user} mentioned you in an activity` | ✅ Yes        | When mentions are added via `UpdateActivity` or `UpdateActivityPartial` with `handle_mention_notifications=true`, `mention` notifications are automatically created for newly mentioned users. Multiple mentions from the same user on the same activity are deduplicated within the deduplication window                                                                                        |
| **Update Comment (add mentions)**  | User who updates comment (e.g., Charlie)              | Mentioned user (e.g., Bob)        | `comment_mention`  | `{user} mentioned you in a comment`   | ✅ Yes        | When mentions are added via `UpdateComment` with `handle_mention_notifications=true`, `comment_mention` notifications are automatically created for newly mentioned users. Multiple mentions from the same user on the same comment are deduplicated within the deduplication window. The activity author does NOT receive a notification for mentions (they already get a comment notification) |

<admonition type="info">

Adding notifications with the create notification activity flag only works if the target user (the one who should receive the notification) has a feed with group `notification`, and id `<user id>`.

</admonition>

<Tabs>

```kotlin label="Kotlin"
// Eric follows Jane
ericFeed.follow(
    targetFid = janeFeed.fid,
    createNotificationActivity = true // When true Jane's notification feed will be updated with follow activity
)

// Eric comments on Jane's activity
ericFeed.addComment(
    ActivityAddCommentRequest(
        comment = "Agree!",
        activityId = janeActivity.id,
        createNotificationActivity = true // When true Jane's notification feed will be updated with comment activity
    )
)

// Eric reacts to Jane's activity
ericFeed.addActivityReaction(
    activityId = janeActivity.id,
    request = AddReactionRequest(
        type = "like",
        createNotificationActivity = true // When true Jane's notification feed will be updated with reaction activity
    ),
)

// Eric reacts to a comment posted to Jane's activity by Sara
ericFeed.addCommentReaction(
    commentId = saraComment.id,
    request = AddCommentReactionRequest(
        type = "like",
        createNotificationActivity = true // When true Sara's notification feed will be updated with comment reaction activity
    ),
)
```

```js label="JavaScript"
// Eric follows Jane
await ericTimeline.follow(janeFeed, {
  // When true Jane's notification feed will be updated with follow activity
  create_notification_activity: true,
});

// Eric comments on Jane's activity
await ericClient.addComment({
  comment: "Agree!",
  object_id: janeActivity.id,
  object_type: "activity",
  // When true Jane's notification feed will be updated with comment activity
  create_notification_activity: true,
});

// Eric reacts to Jane's activity
await ericClient.addReaction({
  activity_id: janeActivity.id,
  // When true Jane's notification feed will be updated with reaction activity
  type: "like",
  create_notification_activity: true,
});

// Eric reacts to a comment posted to Jane's activity by Sara
await ericClient.addCommentReaction({
  id: saraComment.id,
  type: "like",
  // When true Sara's notification feed will be updated with comment reaction activity
  create_notification_activity: true,
});
```

```js label="React"
// Eric follows Jane
await ericTimeline.follow(janeFeed, {
  // When true Jane's notification feed will be updated with follow activity
  create_notification_activity: true,
});

// Eric comments on Jane's activity
await ericClient.addComment({
  comment: "Agree!",
  object_id: janeActivity.id,
  object_type: "activity",
  // When true Jane's notification feed will be updated with comment activity
  create_notification_activity: true,
});

// Eric reacts to Jane's activity
await ericClient.addReaction({
  activity_id: janeActivity.id,
  // When true Jane's notification feed will be updated with reaction activity
  type: "like",
  create_notification_activity: true,
});

// Eric reacts to a comment posted to Jane's activity by Sara
await ericClient.addCommentReaction({
  id: saraComment.id,
  type: "like",
  // When true Sara's notification feed will be updated with comment reaction activity
  create_notification_activity: true,
});
```

```js label="React Native"
// Eric follows Jane
await ericTimeline.follow(janeFeed, {
  // When true Jane's notification feed will be updated with follow activity
  create_notification_activity: true,
  copy_custom_to_notification: true, // Copy follow custom data to the notification activity
});

// Eric comments on Jane's activity
await ericClient.addComment({
  comment: "Agree!",
  object_id: janeActivity.id,
  object_type: "activity",
  // When true Jane's notification feed will be updated with comment activity
  create_notification_activity: true,
  copy_custom_to_notification: true, // Copy comment custom data to the notification activity
});

// Eric reacts to Jane's activity
await ericClient.addReaction({
  activity_id: janeActivity.id,
  // When true Jane's notification feed will be updated with reaction activity
  type: "like",
  create_notification_activity: true,
  copy_custom_to_notification: true, // Copy reaction custom data to the notification activity
});

// Eric reacts to a comment posted to Jane's activity by Sara
await ericClient.addCommentReaction({
  id: saraComment.id,
  type: "like",
  // When true Sara's notification feed will be updated with comment reaction activity
  create_notification_activity: true,
  copy_custom_to_notification: true, // Copy comment reaction custom data to the notification activity
});
```

```dart label="Dart"
// Eric follows Jane
await ericFeed.follow(
  targetFid: janeFeed.fid,
  createNotificationActivity:
      true, // When true Jane's notification feed will be updated with follow activity
);
// Eric comments on Jane's activity
await ericFeed.addComment(
  request: ActivityAddCommentRequest(
    comment: 'Agree!',
    activityId: janeActivity.activityId,
    createNotificationActivity:
        true, // When true Jane's notification feed will be updated with comment activity
  ),
);
// Eric reacts to Jane's activity
await ericFeed.addActivityReaction(
  activityId: janeActivity.activityId,
  request: const AddReactionRequest(
    type: 'like',
    createNotificationActivity: true, // When true Jane's notification feed will be updated with reaction activity
  ),
);
// Eric reacts to a comment posted to Jane's activity by Sara
await ericFeed.addCommentReaction(
  commentId: saraComment.activityId,
  request: const AddCommentReactionRequest(
    type: 'like',
    createNotificationActivity:
        true, // When true Sara's notification feed will be updated with comment reaction activity
  ),
);
```

```js label="Node"
// Eric follows Jane
await serverClient.feeds.follow({
  source: ericTimeline.feed,
  target: janeFeed.feed,
  // When true Jane's notification feed will be updated with follow activity
  create_notification_activity: true,
  copy_custom_to_notification: true, // Copy follow custom data to the notification activity
});

// Eric comments on Jane's activity
await serverClient.feeds.addComment({
  comment: "Agree!",
  object_id: janeActivity.id,
  object_type: "activity",
  // When true Jane's notification feed will be updated with comment activity
  create_notification_activity: true,
  copy_custom_to_notification: true, // Copy comment custom data to the notification activity
  user_id: "eric",
});

// Eric reacts to Jane's activity
await serverClient.feeds.addReaction({
  activity_id: janeActivity.id,
  // When true Jane's notification feed will be updated with reaction activity
  type: "like",
  create_notification_activity: true,
  copy_custom_to_notification: true, // Copy reaction custom data to the notification activity
  user_id: "eric",
});

// Eric reacts to a comment posted to Jane's activity by Sara
await serverClient.feeds.addCommentReaction({
  id: saraComment.id,
  type: "like",
  // When true Sara's notification feed will be updated with comment reaction activity
  create_notification_activity: true,
  copy_custom_to_notification: true, // Copy comment reaction custom data to the notification activity
  user_id: "eric",
});
```

```go label="Go"
ctx := context.Background()

// Eric follows Jane
_, err = client.Feeds().Follow(ctx, &getstream.FollowRequest{
  Source:                     "user:eric",
  Target:                     "user:jane",
  CreateNotificationActivity: getstream.PtrTo(true), // When true Jane's notification feed will be updated with follow activity
})
if err != nil {
  log.Fatal("Error following user:", err)
}

// Eric comments on Jane's activity
_, err = client.Feeds().AddComment(ctx, &getstream.AddCommentRequest{
  Comment:                    "Agree!",
  ObjectID:                   "janeActivity.id", // This would be the actual activity ID
  ObjectType:                 "activity",
  CreateNotificationActivity: getstream.PtrTo(true), // When true Jane's notification feed will be updated with comment activity
  UserID:                     getstream.PtrTo("eric"),
})
if err != nil {
  log.Fatal("Error adding comment:", err)
}

// Eric reacts to Jane's activity
_, err = client.Feeds().AddReaction(ctx, "janeActivity.id", &getstream.AddReactionRequest{ // This would be the actual activity ID
  Type:                       "like",
  CreateNotificationActivity: getstream.PtrTo(true), // When true Jane's notification feed will be updated with reaction activity
  UserID:                     getstream.PtrTo("eric"),
})
if err != nil {
  log.Fatal("Error adding reaction:", err)
}

// Eric reacts to a comment posted to Jane's activity by Sara
_, err = client.Feeds().AddCommentReaction(ctx, "saraComment.id", &getstream.AddCommentReactionRequest{ // This would be the actual comment ID
  Type:                       "like",
  CreateNotificationActivity: getstream.PtrTo(true), // When true Sara's notification feed will be updated with comment reaction activity
  UserID:                     getstream.PtrTo("eric"),
})
if err != nil {
  log.Fatal("Error adding comment reaction:", err)
}
```

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

// Eric follows Jane
$feedsClient->follow(new GeneratedModels\FollowRequest(
    source: "user:eric",
    target: "user:jane",
    createNotificationActivity: true // When true Jane's notification feed will be updated with follow activity
));

// Eric comments on Jane's activity
$feedsClient->addComment(new GeneratedModels\AddCommentRequest(
    comment: "Agree!",
    objectID: "janeActivity.id", // This would be the actual activity ID
    objectType: "activity",
    createNotificationActivity: true, // When true Jane's notification feed will be updated with comment activity
    userID: "eric"
));

// Eric reacts to Jane's activity
$feedsClient->addActivityReaction("janeActivity.id", new GeneratedModels\AddReactionRequest( // This would be the actual activity ID
    type: "like",
    createNotificationActivity: true, // When true Jane's notification feed will be updated with reaction activity
    userID: "eric"
));

// Eric reacts to a comment posted to Jane's activity by Sara
$feedsClient->addCommentReaction("saraComment.id", new GeneratedModels\AddCommentReactionRequest( // This would be the actual comment ID
    type: "like",
    createNotificationActivity: true, // When true Sara's notification feed will be updated with comment reaction activity
    userID: "eric"
));
```

</Tabs>

### Updating mentions in activities and comments

You can also create or remove mention notifications when updating activities or comments. This flag defaults to `false` for all endpoints it's supported in.

<Tabs>

```kotlin label="Kotlin"
// Alice updates her activity to mention Bob
ericFeed.updateActivity(
    id = activityId,
    request = UpdateActivityRequest(
        text = "Hey @Bob check this out!",
        mentionedUserIds = listOf("bob"),
        handleMentionNotifications = true, // When true, Bob will receive a mention notification
    )
)

// Alice updates her comment to mention Charlie
ericFeed.updateComment(
    commentId = commentId,
    request = UpdateCommentRequest(
        comment = "Hey @Charlie!",
        mentionedUserIds = listOf("charlie"),
        handleMentionNotifications = true, // When true, Charlie will receive a comment_mention notification
    )
)

// Alice removes mentions from her activity
ericFeed.updateActivity(
    id = activityId,
    request = UpdateActivityRequest(
        text = "Updated text without mentions",
        mentionedUserIds = emptyList(),
        handleMentionNotifications = true, // When true, mention notifications for removed users are deleted
    )
)
```

```js label="JavaScript"
// Alice updates her activity to mention Bob
await ericClient.updateActivity({
  id: activityId,
  text: "Hey @Bob check this out!",
  mentioned_user_ids: ["bob"],
  handle_mention_notifications: true, // When true, Bob will receive a mention notification
});

// Alice updates her comment to mention Charlie
await ericClient.updateComment({
  id: commentId,
  comment: "Hey @Charlie!",
  mentioned_user_ids: ["charlie"],
  handle_mention_notifications: true, // When true, Charlie will receive a comment_mention notification
});

// Alice removes mentions from her activity
await ericClient.updateActivity({
  id: activityId,
  text: "Updated text without mentions",
  mentioned_user_ids: [],
  handle_mention_notifications: true, // When true, mention notifications for removed users are deleted
});
```

```dart label="Dart"
// Alice updates her activity to mention Bob
await ericFeed.updateActivity(
  activityId: activityId,
  request: UpdateActivityRequest(
    text: "Hey @Bob check this out!",
    mentionedUserIDs: ["bob"],
    handleMentionNotifications: true, // When true, Bob will receive a mention notification
  ),
);

// Alice updates her comment to mention Charlie
await ericFeed.updateComment(
  commentId: commentId,
  request: UpdateCommentRequest(
    comment: "Hey @Charlie!",
    mentionedUserIDs: ["charlie"],
    handleMentionNotifications: true, // When true, Charlie will receive a comment_mention notification
  ),
);

// Alice removes mentions from her activity
await ericFeed.updateActivity(
  activityId: activityId,
  request: UpdateActivityRequest(
    text: "Updated text without mentions",
    mentionedUserIDs: [],
    handleMentionNotifications: true, // When true, mention notifications for removed users are deleted
  ),
);
```

```js label="Node"
// Alice updates her activity to mention Bob
await serverClient.feeds.updateActivity({
  id: activityId,
  text: "Hey @Bob check this out!",
  mentioned_user_ids: ["bob"],
  handle_mention_notifications: true, // When true, Bob will receive a mention notification
  user_id: "alice",
});

// Alice updates her comment to mention Charlie
await serverClient.feeds.updateComment({
  id: commentId,
  comment: "Hey @Charlie!",
  mentioned_user_ids: ["charlie"],
  handle_mention_notifications: true, // When true, Charlie will receive a comment_mention notification
  user_id: "alice",
});

// Alice removes mentions from her activity
await serverClient.feeds.updateActivity({
  id: activityId,
  text: "Updated text without mentions",
  mentioned_user_ids: [],
  handle_mention_notifications: true, // When true, mention notifications for removed users are deleted
  user_id: "alice",
});
```

```go label="Go"
ctx := context.Background()

// Alice updates her activity to mention Bob
_, err = client.Feeds().UpdateActivity(ctx, activityId, &getstream.UpdateActivityRequest{
  Text:                       "Hey @Bob check this out!",
  MentionedUserIDs:           []string{"bob"},
  HandleMentionNotifications: getstream.PtrTo(true), // When true, Bob will receive a mention notification
  UserID:                     getstream.PtrTo("alice"),
})
if err != nil {
  log.Fatal("Error updating activity:", err)
}

// Alice updates her comment to mention Charlie
_, err = client.Feeds().UpdateComment(ctx, commentId, &getstream.UpdateCommentRequest{
  Comment:                    "Hey @Charlie!",
  MentionedUserIDs:           getstream.PtrTo([]string{"charlie"}),
  HandleMentionNotifications: getstream.PtrTo(true), // When true, Charlie will receive a comment_mention notification
  UserID:                     getstream.PtrTo("alice"),
})
if err != nil {
  log.Fatal("Error updating comment:", err)
}

// Alice removes mentions from her activity
_, err = client.Feeds().UpdateActivity(ctx, activityId, &getstream.UpdateActivityRequest{
  Text:                       "Updated text without mentions",
  MentionedUserIDs:           []string{},
  HandleMentionNotifications: getstream.PtrTo(true), // When true, mention notifications for removed users are deleted
  UserID:                     getstream.PtrTo("alice"),
})
if err != nil {
  log.Fatal("Error updating activity:", err)
}
```

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

// Alice updates her activity to mention Bob
$feedsClient->updateActivity(activityId, new GeneratedModels\UpdateActivityRequest(
    text: "Hey @Bob check this out!",
    mentionedUserIDs: ["bob"],
    handleMentionNotifications: true, // When true, Bob will receive a mention notification
    userID: "alice"
));

// Alice updates her comment to mention Charlie
$feedsClient->updateComment(commentId, new GeneratedModels\UpdateCommentRequest(
    comment: "Hey @Charlie!",
    mentionedUserIDs: ["charlie"],
    handleMentionNotifications: true, // When true, Charlie will receive a comment_mention notification
    userID: "alice"
));

// Alice removes mentions from her activity
$feedsClient->updateActivity(activityId, new GeneratedModels\UpdateActivityRequest(
    text: "Updated text without mentions",
    mentionedUserIDs: [],
    handleMentionNotifications: true, // When true, mention notifications for removed users are deleted
    userID: "alice"
));
```

</Tabs>

<admonition type="info">

Commenting and reacting to your own activities and comments will not create notification activities.

</admonition>

### Deleting notification activities

When you add notification activities with `create_notification_activity` you can also have the API automatically remove these when the corresponding trigger entity is deleted.
This is done by using the flag `delete_notification_activity` which defaults to `false` for all endpoints it's supported in.

| Action                                | Notification Type Deleted | Notes                                                                                                                                                                                                          |
| ------------------------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Remove Activity Reaction**          | `reaction`                | Only the notification activity created by the user removing the reaction is deleted. Other users' reaction notifications remain unaffected.                                                                    |
| **Remove Comment Reaction**           | `comment_reaction`        | Only the notification activity created by the user removing the reaction is deleted. Other users' comment reaction notifications remain unaffected.                                                            |
| **Delete Comment**                    | `comment`                 | Deletes the comment notification for the activity author.                                                                                                                                                      |
| **Delete Comment**                    | `comment_mention`         | When a comment with mentions is deleted, `comment_mention` notifications are deleted for all mentioned users if `delete_notification_activity=true`.                                                           |
| **Unfollow User**                     | `follow`                  | Only the notification activity created by the user performing the unfollow is deleted.                                                                                                                         |
| **Delete Activity**                   | `mention`                 | When an activity with mentions is deleted, `mention` notifications are deleted for all mentioned users if `delete_notification_activity=true`.                                                                 |
| **Delete Activities (batch)**         | `mention`                 | When multiple activities with mentions are deleted via `DeleteActivities` with `delete_notification_activity=true`, `mention` notifications are deleted for all mentioned users across all deleted activities. |
| **Update Activity (remove mentions)** | `mention`                 | When mentions are removed via `UpdateActivity` or `UpdateActivityPartial` with `handle_mention_notifications=true`, `mention` notifications are automatically removed for users no longer mentioned.           |
| **Update Comment (remove mentions)**  | `comment_mention`         | When mentions are removed via `UpdateComment` with `handle_mention_notifications=true`, `comment_mention` notifications are automatically removed for users no longer mentioned.                               |

<admonition type="info">

**Deletion Scope:** The deletion behavior differs depending on the action:

- **Reactions and Follows:** Only the notification activity created by the specific user performing the deletion is removed. Other users' notifications remain unaffected.
- **Activities and Comments with Mentions:** When an activity or comment containing mentions is deleted, notification activities are removed for **all** mentioned users if `delete_notification_activity=true`.

</admonition>

<Tabs>

```kotlin label="Kotlin"
// Eric unfollows Jane
ericFeed.unfollow(
    targetFid = janeFeed.fid,
    // When true the corresponding notification activity will be removed from Jane's notification feed
    deleteNotificationActivity = true
)

// Eric removes his comment
ericFeed.deleteComment(
    commentId = commentId,
    // When true the corresponding notification activity will be removed from Jane's notification feed
    deleteNotificationActivity = true
)

// Eric removes his activity reaction
ericFeed.deleteActivityReaction(
    activityId = janeActivity.id,
    type = "like",
    // When true the corresponding notification activity will be removed from Jane's notification feed
    deleteNotificationActivity = true
)

// Eric removes his comment reaction
ericFeed.deleteCommentReaction(
    commentId = saraComment.id,
    type = "like",
    // When true the corresponding notification activity will be removed from Jane's notification feed
    deleteNotificationActivity = true
)

// Eric deletes his activity with mentions
ericFeed.deleteActivity(
    id = activityId,
    // When true, mention notifications for all mentioned users will be removed
    deleteNotificationActivity = true
)

// Eric deletes multiple activities with mentions (batch operation)
client.deleteActivities(
    request = DeleteActivitiesRequest(
        ids = listOf(activityId1, activityId2),
        // When true, mention notifications for all mentioned users will be removed
        deleteNotificationActivity = true,
    )
)
```

```js label="JavaScript"
// Eric unfollows Jane
await ericTimeline.unfollow(janeFeed, {
  // When true the corresponding notification activity will be removed from Jane's notification feed
  delete_notification_activity: true,
});

// Eric removes his comment
await ericClient.deleteComment({
  id: commentId,
  // When true the corresponding notification activity will be removed from Jane's notification feed
  delete_notification_activity: true,
});

// Eric removes his activity reaction
await ericClient.deleteActivityReaction({
  activity_id: janeActivity.id,
  type: "like",
  // When true the corresponding notification activity will be removed from Jane's notification feed
  delete_notification_activity: true,
});

// Eric removes his comment reaction
await ericClient.deleteCommentReaction({
  id: saraComment.id,
  type: "like",
  // When true the corresponding notification activity will be removed from Jane's notification feed
  delete_notification_activity: true,
});

// Eric deletes his activity with mentions
await ericClient.deleteActivity({
  id: activityId,
  // When true, mention notifications for all mentioned users will be removed
  delete_notification_activity: true,
});

// Eric deletes multiple activities with mentions (batch operation)
await ericClient.deleteActivities({
  ids: [activityId1, activityId2],
  // When true, mention notifications for all mentioned users will be removed
  delete_notification_activity: true,
});
```

```dart label="Dart"
// Eric unfollows Jane
await ericFeed.unfollow(
  targetFid: janeFeed.fid,
  // When true the corresponding notification activity will be removed from Jane's notification feed
  deleteNotificationActivity: true,
);

// Eric removes his comment
await ericFeed.deleteComment(
  commentId: commentId,
  // When true the corresponding notification activity will be removed from Jane's notification feed
  deleteNotificationActivity: true,
);

// Eric removes his activity reaction
await ericFeed.deleteActivityReaction(
  activityId: janeActivity.activityId,
  type: 'like',
  // When true the corresponding notification activity will be removed from Jane's notification feed
  deleteNotificationActivity: true,
);

// Eric removes his comment reaction
await ericFeed.deleteCommentReaction(
  commentId: saraComment.id,
  type: 'like',
  // When true the corresponding notification activity will be removed from Jane's notification feed
  deleteNotificationActivity: true,
);

// Eric deletes his activity with mentions
await ericFeed.deleteActivity(
  activityId: activityId,
  // When true, mention notifications for all mentioned users will be removed
  deleteNotificationActivity: true,
);

// Eric deletes multiple activities with mentions (batch operation)
await ericFeed.deleteActivities(
  activityIds: [activityId1, activityId2],
  // When true, mention notifications for all mentioned users will be removed
  deleteNotificationActivity: true,
);
```

```js label="Node"
// Eric unfollows Jane
await serverClient.feeds.unfollow({
  source: ericTimeline.feed,
  target: janeFeed.feed,
  // When true the corresponding notification activity will be removed from Jane's notification feed
  delete_notification_activity: true,
});

// Eric removes his comment
await serverClient.feeds.deleteComment({
  id: commentId,
  // When true the corresponding notification activity will be removed from Jane's notification feed
  delete_notification_activity: true,
  user_id: "eric",
});

// Eric removes his activity reaction
await serverClient.feeds.deleteActivityReaction({
  activity_id: janeActivity.id,
  type: "like",
  // When true the corresponding notification activity will be removed from Jane's notification feed
  delete_notification_activity: true,
  user_id: "eric",
});

// Eric removes his comment reaction
await serverClient.feeds.deleteCommentReaction({
  id: saraComment.id,
  type: "like",
  // When true the corresponding notification activity will be removed from Jane's notification feed
  delete_notification_activity: true,
  user_id: "eric",
});

// Eric deletes his activity with mentions
await serverClient.feeds.deleteActivity({
  id: activityId,
  // When true, mention notifications for all mentioned users will be removed
  delete_notification_activity: true,
  user_id: "eric",
});

// Eric deletes multiple activities with mentions (batch operation)
await serverClient.feeds.deleteActivities({
  ids: [activityId1, activityId2],
  // When true, mention notifications for all mentioned users will be removed
  delete_notification_activity: true,
  user_id: "eric",
});
```

```go label="Go"
ctx := context.Background()

// Eric unfollows Jane
_, err = client.Feeds().Unfollow(ctx, &getstream.UnfollowRequest{
  Source:                     "user:eric",
  Target:                     "user:jane",
  DeleteNotificationActivity: getstream.PtrTo(true), // When true the corresponding notification activity will be removed from Jane's notification feed
})
if err != nil {
  log.Fatal("Error unfollowing user:", err)
}

// Eric removes his comment
_, err = client.Feeds().DeleteComment(ctx, "commentId", &getstream.DeleteCommentRequest{ // This would be the actual comment ID
  DeleteNotificationActivity: getstream.PtrTo(true), // When true the corresponding notification activity will be removed from Jane's notification feed
  UserID:                     getstream.PtrTo("eric"),
})
if err != nil {
  log.Fatal("Error deleting comment:", err)
}

// Eric removes his activity reaction
_, err = client.Feeds().DeleteReaction(ctx, "janeActivity.id", &getstream.DeleteReactionRequest{ // This would be the actual activity ID
  Type:                       "like",
  DeleteNotificationActivity: getstream.PtrTo(true), // When true the corresponding notification activity will be removed from Jane's notification feed
  UserID:                     getstream.PtrTo("eric"),
})
if err != nil {
  log.Fatal("Error deleting reaction:", err)
}

// Eric removes his comment reaction
_, err = client.Feeds().DeleteCommentReaction(ctx, "saraComment.id", &getstream.DeleteCommentReactionRequest{ // This would be the actual comment ID
  Type:                       "like",
  DeleteNotificationActivity: getstream.PtrTo(true), // When true the corresponding notification activity will be removed from Jane's notification feed
  UserID:                     getstream.PtrTo("eric"),
})
if err != nil {
  log.Fatal("Error deleting comment reaction:", err)
}

// Eric deletes his activity with mentions
_, err = client.Feeds().DeleteActivity(ctx, "activityId", &getstream.DeleteActivityRequest{ // This would be the actual activity ID
  DeleteNotificationActivity: getstream.PtrTo(true), // When true, mention notifications for all mentioned users will be removed
  UserID:                     getstream.PtrTo("eric"),
})
if err != nil {
  log.Fatal("Error deleting activity:", err)
}

// Eric deletes multiple activities with mentions (batch operation)
_, err = client.Feeds().DeleteActivities(ctx, &getstream.DeleteActivitiesRequest{
  ActivityIDs:                []string{"activityId1", "activityId2"},
  DeleteNotificationActivity: getstream.PtrTo(true), // When true, mention notifications for all mentioned users will be removed
  UserID:                     getstream.PtrTo("eric"),
})
if err != nil {
  log.Fatal("Error deleting activities:", err)
}
```

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

// Eric unfollows Jane
$feedsClient->unfollow(new GeneratedModels\UnfollowRequest(
    source: "user:eric",
    target: "user:jane",
    deleteNotificationActivity: true // When true the corresponding notification activity will be removed from Jane's notification feed
));

// Eric removes his comment
$feedsClient->deleteComment("commentId", new GeneratedModels\DeleteCommentRequest( // This would be the actual comment ID
    deleteNotificationActivity: true, // When true the corresponding notification activity will be removed from Jane's notification feed
    userID: "eric"
));

// Eric removes his activity reaction
$feedsClient->deleteActivityReaction("janeActivity.id", new GeneratedModels\DeleteReactionRequest( // This would be the actual activity ID
    type: "like",
    deleteNotificationActivity: true, // When true the corresponding notification activity will be removed from Jane's notification feed
    userID: "eric"
));

// Eric removes his comment reaction
$feedsClient->deleteCommentReaction("saraComment.id", new GeneratedModels\DeleteCommentReactionRequest( // This would be the actual comment ID
    type: "like",
    deleteNotificationActivity: true, // When true the corresponding notification activity will be removed from Jane's notification feed
    userID: "eric"
));

// Eric deletes his activity with mentions
$feedsClient->deleteActivity("activityId", new GeneratedModels\DeleteActivityRequest( // This would be the actual activity ID
    deleteNotificationActivity: true, // When true, mention notifications for all mentioned users will be removed
    userID: "eric"
));

// Eric deletes multiple activities with mentions (batch operation)
$feedsClient->deleteActivities(new GeneratedModels\DeleteActivitiesRequest(
    activityIDs: ["activityId1", "activityId2"],
    deleteNotificationActivity: true, // When true, mention notifications for all mentioned users will be removed
    userID: "eric"
));
```

</Tabs>

### Trigger and Target

The **trigger** is the entity that triggered the creation of the notification activity. This can be a **follow**, a **reaction** or a **comment**. When the trigger is a **comment** the comment data is available on the trigger object, this allows for deep linking back to the comment that triggered the notification.

The **target** is the receiver of the trigger action. This can be a **feed** (e.g., a user's feed), an **activity** or a **comment**. When the target is an **activity** the activity data will be present in the target object. When the target is a **comment** the parent activity data and comment data will be present in the target object.

**Example:** A reply to a comment will include the data of the reply comment in the trigger and the parent comment and activity in the target.

```json
{
  "target": {
    "id": "<activity_id>",
    "type": "<activity_type>",
    "user_id": "<activity_user_id>",
    "comment": {
      "id": "<parent_comment_id>",
      "comment": "this is the parent comment",
      "user_id": "<parent_comment_user_id>"
    }
  },
  "trigger": {
    "text": "<comment_user_id> replied to your comment",
    "type": "comment_reply",
    "comment": {
      "id": "<comment_id>",
      "user_id": "<comment_user_id>",
      "comment": "this is the reply comment"
    }
  }
}
```

### Aggregating on comments and activities

By default the built-in `notification` feed aggregates on activities only with the aggregation format `{{ target_id }}-{{ type }}-{{ time.strftime('%Y-%m-%d') }}`.
If you want to aggregate on comments and activities alike you need to update the aggregation format to

```json
{% if comment_id %}
  {{ comment_id }}_{{ type }}_{{ time.strftime('%Y-%m-%d') }}
{% else %}
  {{ target_id }}_{{ type }}_{{ time.strftime('%Y-%m-%d') }}
{% endif %}
```

This lets you show notifications like

- Your comment has 5 new likes or
- Your comment has 3 new replies

### Reading notification activities

<Tabs>

```swift label="Swift"
let notificationFeed = client.feed(group: "notification", id: "jane")
let notifications = try await notificationFeed.getOrCreate()
```

```kotlin label="Kotlin"
val notificationFeed = client.feed(group = "notification", id = "jane")
val notifications = notificationFeed.getOrCreate()
```

```js label="JavaScript"
// Read notifications
await notificationFeed.getOrCreate({
  limit: 20,
});
console.log(notificationFeed.currentState.is_loading_activities);
console.log(notificationFeed.currentState.aggregated_activities);

await notificationFeed.getNextPage();
```

```js label="React"
const notificationFeed = client.feed("notification", "jane");

await notificationFeed.getOrCreate({
  limit: 20,
});

const { aggregated_activities, isLoading, hasNextPage, loadNextPage } =
  useAggregatedActivities(feed) ?? {};
```

```js label="React Native"
const notificationFeed = client.feed("notification", "jane");

await notificationFeed.getOrCreate({
  limit: 20,
});

const { aggregated_activities, isLoading, hasNextPage, loadNextPage } =
  useAggregatedActivities(feed) ?? {};
```

```dart label="Dart"
final notificationFeed = client.feed(group: 'notification', id: 'john');
await notificationFeed.getOrCreate();
```

```js label="Node"
const notificationFeed = client.feed("notification", "jane");

// Read notifications
const notifications = (
  await notificationFeed.getOrCreate({
    limit: 20,
    user_id: "<user_id>",
  })
).aggregated_activities;
```

```go label="Go"
notificationFeed := client.Feeds().Feed("notification", "john")
// Read notifications
response, err := notificationFeed.GetOrCreate(context.Background(), &getstream.GetOrCreateFeedRequest{
  Limit:  getstream.PtrTo(20),
  UserID: getstream.PtrTo("john"),
})
if err != nil {
  log.Fatal(err)
}
log.Printf("Response: %+v\n", response.Data.AggregatedActivities)
```

```java label="Java"
testFeed = new Feed("user", testUserId, feeds);
testFeed2 = new Feed("user", testUserId2, feeds);

GetOrCreateFeedRequest feedRequest1 =
    GetOrCreateFeedRequest.builder().userID(testUserId).build();
GetOrCreateFeedRequest feedRequest2 =
    GetOrCreateFeedRequest.builder().userID(testUserId2).build();

GetOrCreateFeedResponse feedResponse1 = testFeed.getOrCreate(feedRequest1).getData();
GetOrCreateFeedResponse feedResponse2 = testFeed2.getOrCreate(feedRequest2).getData();
testFeedId = feedResponse1.getFeed().getFeed();
testFeedId2 = feedResponse2.getFeed().getFeed();
```

```php label="php"
use GetStream\GeneratedModels\GetOrCreateFeedRequest;

$notificationFeed = $feedsClient->feed("notification", "jane");

// Read notifications
$feedRequest = new GetOrCreateFeedRequest(
    limit: 20,
    userID: "jane"
);

$response = $notificationFeed->getOrCreateFeed($feedRequest);

// Access the aggregated activities
$notifications = $response->data->aggregatedActivities;
```

```csharp label="C#"
var feedResponse1 = await _feedsV3Client.GetOrCreateFeedAsync(
    FeedGroupID: "user",
    FeedID: _testFeedId,
    request: new GetOrCreateFeedRequest { UserID = _testUserId }
);
var feedResponse2 = await _feedsV3Client.GetOrCreateFeedAsync(
    FeedGroupID: "user",
    FeedID: _testFeedId2,
    request: new GetOrCreateFeedRequest { UserID = _testUserId2 }
);
```

```python label="Python"
feed_response_1 = self.test_feed.get_or_create(user_id=self.test_user_id)
feed_response_2 = self.test_feed_2.get_or_create(
    user_id=self.test_user_id_2
)
```

```rb label="Ruby"
require 'getstream_ruby'

# Get or create notification feed
notification_feed_request = GetStream::Generated::Models::GetOrCreateFeedRequest.new(
  limit: 20,
  user_id: 'jane'
)

response = client.feeds.get_or_create_feed('notification', 'jane', notification_feed_request)

# Access the aggregated activities
notifications = response.aggregated_activities if response.respond_to?(:aggregated_activities)
```

</Tabs>


This is what Jane's notification feed looks like after the above interactions (only relevant fields shown):

- Three aggregated activity groups:
  - `<activity id>-comment-2025-08-04`
  - `<activity id>-reaction-2025-08-04`
  - `<feed id>-follow-2025-08-04`
- `notification_context` has information about the activity/action that triggered the notification
  - Please note that `notification_context` field is only defined if you're using the built-in `notification` feed and `create_notification_activity` flag

When reading notifications, every aggregated activity group contains at most 100 activities (can be configured with [aggregation group limit](/activity-feeds/docs/go-golang/aggregation/#aggregation-group-limit)). The `user_count` field is also computed from the last n activities, defined by aggregation group limit.

If a group has more activities than the limit, `user_count_truncated` will be set to `true`, signaling that `user_count` may not be accurate. This enables creating notifications like "100+ people commented on your post". The `activity_count` field is always accurate, even if the group has more activities than the limit.

Example API response:

```json
{
  aggregated_activities: [
    {
      activity_count: 1,
      user_count: 1,
      user_count_truncated: false,
      is_seen: true,
      is_read: false,
      group: "activity123-comment-2025-08-04",
      activities: [
        {
          type: "comment",
          user: {
            id: "eric",
            name: "Eric",
            // other User fields
          },
          notification_context: {
            trigger: {
              text: "Eric commented on your activity",
              type: "comment",
            },
            target: {
              user_id: "jane",
              type: "post",
              text: "As earnestly shameless elsewhere defective estimable fulfilled of",
              id: "a0668408-0eb9-4906-a1cf-be79f988051d",
              attachments: [
                {
                  type: "image",
                  image_url: "https://...",
                },
              ],
            },
          },
          // Other activity fields
        },
      ],
    },
    {
      activity_count: 1,
      user_count: 1,
      is_seen: true,
      is_read: true,
      group: "activity123-reaction-2025-08-04",
      activities: [
        {
          type: "reaction",
          user: {
            id: "eric",
            name: "Eric",
          },
          notification_context: {
            target: {
              id: "8966090a-30bf-4fe2-b8bc-b0fe36200e56",
              user_id: "jane",
              type: "post",
              text: "Ask too matter formed county wicket oppose talent",
            },
            trigger: {
              type: "reaction",
              text: "Eric reacted to your activity",
            },
          },
        },
      ],
    },
    {
      activity_count: 1,
      user_count: 1,
      is_seen: false,
      is_read: false,
      group: "jane-follow-2025-08-04",
      activities: [
        {
          type: "follow",
          user: {
            id: "eric",
            name: "Eric",
          },
          notification_context: {
            target: {
              id: "jane",
              name: "Jane",
            },
            trigger: {
              type: "follow",
              text: "Eric started following you",
            },
          },
        },
      ],
    },
    {
      activity_count: 1,
      user_count: 1,
      is_seen: false,
      is_read: false,
      group: "comment456-comment_reply-2025-08-04",
      activities: [
        {
          type: "comment_reply",
          user: {
            id: "charlie",
            name: "Charlie"
          },
          notification_context: {
            target: {
              id: "8966090a-30bf-4fe2-b8bc-b0fe36200e56",
              user_id: "alice",
              type: "post",
              text: "Ask too matter formed county wicket oppose talent",
              comment: {
                id: "comment456",
                user_id: "bob",
                comment: "Great post! I totally agree with this."
              }
            },
            trigger: {
              type: "comment_reply",
              text: "Charlie replied to your comment"
            }
          }
        }
      ]
    },
  ];
}
```

## Push Notifications

For information on configuring push notifications, see [Feed Group Push Configuration](/activity-feeds/docs/javascript/feed-group-push-configuration/).

## Notification status

If notification tracking is turned on for the feed group (`track_seen` / `track_read`), the server stamps `is_seen` and `is_read` directly on each aggregated activity group. This means you no longer need to compute read/seen status client-side — just use the boolean fields from the response.

For example, take the notification system on Facebook. If you click the notification icon, all notifications get marked as seen. However, an individual notification only gets marked as read when you click on it.

### Server-side `is_seen` / `is_read`

Each aggregated activity group includes `is_seen` and `is_read` boolean fields when tracking is enabled:

```json
{
  "aggregated_activities": [
    {
      "group": "activity123-comment-2025-08-04",
      "is_seen": true,
      "is_read": false,
      "activity_count": 1,
      "activities": [...]
    }
  ]
}
```

<admonition type="info">

The `is_seen` and `is_read` fields are only present when `track_seen` / `track_read` are enabled on the feed group. When tracking is disabled, these fields are omitted from the response.

</admonition>

To check if a notification group is read or seen, simply read the fields:

<Tabs>

```js label="JavaScript"
const isRead = group.is_read;
const isSeen = group.is_seen;
```

```js label="React"
const isRead = aggregatedActivity.is_read;
const isSeen = aggregatedActivity.is_seen;
```

```js label="React Native"
const isRead = aggregatedActivity.is_read;
const isSeen = aggregatedActivity.is_seen;
```

```js label="Node"
const isRead = group.is_read;
const isSeen = group.is_seen;
```

```go label="Go"
group := response.Data.AggregatedActivities[0]
isRead := group.IsRead != nil && *group.IsRead
isSeen := group.IsSeen != nil && *group.IsSeen
```

```php label="php"
$group = $response->getData()->aggregatedActivities[0];
$isRead = $group->isRead;
$isSeen = $group->isSeen;
```

</Tabs>

The server uses a hybrid algorithm to determine these values:

1. Check if the group ID is in the seen/read ID lists
2. Fall back to timestamp comparison (`updated_at < last_seen_at`) for entries beyond the list cap

This makes the server-side fields more reliable than client-side computation, especially for feeds with many notifications. Client-side SDKs will update `is_seen` and `is_read` flags from notification WebSocket events.

**Non-aggregated feeds:** If aggregation is turned off but notification tracking is still enabled, `is_seen` and `is_read` are stamped on each individual activity. When aggregation is on, activities inside a group inherit the group's `is_seen` / `is_read` value.

### Unread/unseen counts

The `notification_status` in the response also includes `unread` and `unseen` counts. These are computed from the last 1000 activities, aggregated into a maximum of 100 groups. This means unread/unseen counts will never exceed 100.

```json
{
  "notification_status": {
    "unread": 12,
    "unseen": 0,
    "last_seen_at": "2025-08-04T12:00:00Z",
    "last_read_at": "2025-08-04T11:30:00Z",
    "seen_activities": [], // deprecated — use is_seen on each group instead
    "read_activities": ["activity123-reaction-2025-08-04"] // deprecated — use is_read on each group instead
  }
}
```

You can access `notification_status` (including `unread` and `unseen`) as follows:

<Tabs>

```js label="JavaScript"
const status = notificationFeed.state.getLatestValue().notification_status;
const unread = status?.unread ?? 0;
const unseen = status?.unseen ?? 0;
```

```js label="React"
const { unread, unseen } = useNotificationStatus(feed) ?? {};

// unread and unseen are reactive; use them for badges or counts
```

```js label="React Native"
const { unread, unseen } = useNotificationStatus(feed) ?? {};

// unread and unseen are reactive; use them for badges or counts
```

```js label="Node"
const response = await notificationFeed.getOrCreate({ user_id: "john" });
const status = response.notification_status;

const unread = status?.unread ?? 0;
const unseen = status?.unseen ?? 0;
```

```go label="Go"
response, err := notificationFeed.GetOrCreate(context.Background(), &getstream.GetOrCreateFeedRequest{
  UserID: getstream.PtrTo("john"),
})
if err != nil {
  log.Fatal(err)
}

status := response.Data.NotificationStatus
unread := 0
unseen := 0
if status != nil {
  unread = status.Unread
  unseen = status.Unseen
}
```

```php label="php"
$response = $notificationFeed->getOrCreate(new GeneratedModels\GetOrCreateFeedRequest(userID: "john"));
$status = $response->getData()->notificationStatus;

$unread = $status?->unread ?? 0;
$unseen = $status?->unseen ?? 0;
```

</Tabs>

### Legacy: `read_activities` / `seen_activities` arrays

<admonition type="warning">

**Deprecated:** The `read_activities` and `seen_activities` arrays in the `notification_status` response are deprecated. Use the `is_read` and `is_seen` fields on each aggregated activity group instead.

</admonition>

For backward compatibility, the `notification_status` response still includes `read_activities` and `seen_activities` ID lists. However, these lists are capped at 100 entries, which means they may be incomplete for feeds with many notifications. The server-side `is_seen` / `is_read` fields do not have this limitation.

### Legacy: Client-side computation

<admonition type="warning">

**Deprecated:** The following client-side pattern is deprecated. Use the server-side `is_read` / `is_seen` fields instead.

</admonition>

Older SDK versions required computing read/seen status client-side using timestamps and ID lists:

<Tabs>

```js label="JavaScript"
// Deprecated — use group.is_read / group.is_seen instead
const isRead =
  (lastReadAt && group.updated_at.getTime() < lastReadAt.getTime()) ||
  readActivities.includes(group.group);
const isSeen =
  (lastSeenAt && group.updated_at.getTime() < lastSeenAt.getTime()) ||
  seenActivities.includes(group.group);
```

```js label="React"
// Deprecated — use aggregatedActivity.is_read / aggregatedActivity.is_seen instead
const isRead = useIsAggregatedActivityRead({ feed, aggregatedActivity });
const isSeen = useIsAggregatedActivitySeen({ feed, aggregatedActivity });
```

```js label="React Native"
// Deprecated — use aggregatedActivity.is_read / aggregatedActivity.is_seen instead
const isRead = useIsAggregatedActivityRead({ feed, aggregatedActivity });
const isSeen = useIsAggregatedActivitySeen({ feed, aggregatedActivity });
```

```js label="Node"
// Deprecated — use group.is_read / group.is_seen instead
const isRead =
  (lastReadAt && group.updated_at.getTime() < lastReadAt.getTime()) ||
  readActivities.includes(group.group);
const isSeen =
  (lastSeenAt && group.updated_at.getTime() < lastSeenAt.getTime()) ||
  seenActivities.includes(group.group);
```

```go label="Go"
// Deprecated — use group.IsRead / group.IsSeen instead
group := response.Data.AggregatedActivities[0]
lastSeenAt := response.Data.NotificationStatus.LastSeenAt
seenActivities := response.Data.NotificationStatus.SeenActivities
lastReadAt := response.Data.NotificationStatus.LastReadAt
readActivities := response.Data.NotificationStatus.ReadActivities

isRead := (lastReadAt != nil && lastReadAt.Time != nil && group.UpdatedAt.Time != nil && group.UpdatedAt.Time.Before(*lastReadAt.Time)) || slices.Contains(readActivities, group.Group)

isSeen := (lastSeenAt != nil && lastSeenAt.Time != nil && group.UpdatedAt.Time != nil && group.UpdatedAt.Time.Before(*lastSeenAt.Time)) || slices.Contains(seenActivities, group.Group)
```

```php label="php"
// Deprecated — use $group->isRead / $group->isSeen instead
$group = $response->getData()->aggregatedActivities[0];
$lastSeenAt = $response->getData()->notificationStatus->lastSeenAt;
$seenActivities = $response->getData()->notificationStatus->seenActivities;
$lastReadAt = $response->getData()->notificationStatus->lastReadAt;
$readActivities = $response->getData()->notificationStatus->readActivities;

$isRead = ($lastReadAt && $group->updatedAt->getTimestamp() * 1e+9 < $lastReadAt->getTimestamp() * 1e+9) || ($readActivities && in_array($group->group, $readActivities));
$isSeen = ($lastSeenAt && $group->updatedAt->getTimestamp() * 1e+9 < $lastSeenAt->getTimestamp() * 1e+9) || ($seenActivities && in_array($group->group, $seenActivities));
```

</Tabs>

## Marking notifications as seen

<Tabs>

```kotlin label="Kotlin"
notificationFeed.markActivity(
    request = MarkActivityRequest(
        // Mark all notifications as seen...
        markAllSeen = true,
        // ...or only selected ones
        markSeen = listOf(
            /* group names to mark as seen */
        )
    )
)
```

```js label="JavaScript"
await notificationFeed.markActivity({
  // Mark all notifications as seen...
  mark_all_seen: true,
  // ...or only selected ones
  mark_seen: [
    /* group names to mark as seen */
  ],
});
```

```js label="React"
await notificationFeed.markActivity({
  // Mark all notifications as seen...
  mark_all_seen: true,
  // ...or only selected ones
  mark_seen: [
    /* group names to mark as seen */
  ],
});
```

```js label="React Native"
await notificationFeed.markActivity({
  // Mark all notifications as seen...
  mark_all_seen: true,
  // ...or only selected ones
  mark_seen: [
    /* group names to mark as seen */
  ],
});
```

```dart label="Dart"
await notificationFeed.markActivity(
  request: const MarkActivityRequest(
    // Mark all notifications as seen...
    markAllSeen: true,
    // ...or only selected ones
    markSeen: [
      /* group names to mark as seen */
    ],
  ),
);
```

```js label="Node"
await notificationFeed.markActivity({
  // Mark all notifications as seen...
  mark_all_seen: true,
  // ...or only selected ones
  mark_seen: [
    /* group names to mark as seen */
  ],
  user_id: "<user id>",
});
```

```go label="Go"
notificationFeed := client.Feeds().Feed("notification", "john")

_, err = notificationFeed.MarkActivity(context.Background(), &getstream.MarkActivityRequest{
  // Mark all notifications as seen...
  MarkAllSeen: getstream.PtrTo(true),
  // ...or only selected ones
  MarkSeen: []string{
    // group names to mark as seen
  },
  UserID:      getstream.PtrTo("john"),
})
if err != nil {
  log.Fatal("Error:", err)
}
```

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

$notificationFeed = $feedsClient->feed("notification", "john");

$notificationFeed->markActivity(new GeneratedModels\MarkActivityRequest(
    // Mark all notifications as seen...
    markAllSeen: true,
    // ...or only selected ones
    markSeen: [
        // group names to mark as seen
    ],
    userID: "john"
));
```

</Tabs>

## Marking notifications as read

<Tabs>

```kotlin label="Kotlin"
notificationFeed.markActivity(
    request = MarkActivityRequest(
        // Mark all notifications as read...
        markAllRead = true,
        // ...or only selected ones
        markRead = listOf(
            /* group names to mark as read */
        )
    )
)
```

```js label="JavaScript"
await notificationFeed.markActivity({
  // Mark all notifications as read...
  mark_all_read: true,
  // ...or only selected ones
  mark_read: [
    /* group names to mark as read */
  ],
});
```

```js label="React"
await notificationFeed.markActivity({
  // Mark all notifications as read...
  mark_all_read: true,
  // ...or only selected ones
  mark_read: [
    /* group names to mark as read */
  ],
});
```

```js label="React Native"
await notificationFeed.markActivity({
  // Mark all notifications as read...
  mark_all_read: true,
  // ...or only selected ones
  mark_read: [
    /* group names to mark as read */
  ],
});
```

```dart label="Dart"
await notificationFeed.markActivity(
  request: const MarkActivityRequest(
    // Mark all notifications as read...
    markAllRead: true,
    // ...or only selected ones
    markRead: [
      // group names to mark as read
    ],
  ),
);
```

```js label="Node"
await notificationFeed.markActivity({
  // Mark all notifications as read...
  mark_all_read: true,
  // ...or only selected ones
  mark_read: [
    /* group names to mark as read */
  ],
  user_id: "<user id>",
});
```

```go label="Go"
notificationFeed := client.Feeds().Feed("notification", "john")

_, err = notificationFeed.MarkActivity(context.Background(), &getstream.MarkActivityRequest{
  // Mark all notifications as read...
  MarkAllRead: getstream.PtrTo(true),
  // ...or only selected ones
  MarkRead: []string{
    // group names to mark as read
  },
  UserID:      getstream.PtrTo("john"),
})
if err != nil {
  log.Fatal("Error:", err)
}
```

```php label="php"
use GetStream\GeneratedModels\MarkActivityRequest;

// Create notification feed
$notificationFeed = $feedsClient->feed("notification", "john");

// Mark all notifications as read
$markRequest = new MarkActivityRequest(
    markAllRead: true,
    userID: "john"
);

$response = $notificationFeed->markActivity($markRequest);

// Or mark only selected notifications as read
$markSelectedRequest = new MarkActivityRequest(
    markRead: [
        // group names to mark as read
    ],
    userID: "john"
);

$response = $notificationFeed->markActivity($markSelectedRequest);
```

</Tabs>


## Ranking by read/seen status

When `track_read` or `track_seen` is enabled, you can use `is_read` and `is_seen` in ranking expressions to prioritize unread or unseen notifications. See [Ranking by read/seen status](/activity-feeds/docs/go-golang/custom-ranking/#ranking-by-readseen-status) for configuration details and examples.

## Realtime events

Two events are emitted when mark operations are performed:

- **`feeds.notification_feed.updated`** — contains the updated `notification_status`. For aggregated notification feeds it also contains the updated/added groups with `is_seen` and `is_read` flags.
- **`feeds.activity.added`** - contains the new activities for flat notifications

Clients can subscribe to these realtime events to update the UI without re-fetching the feed.

## Pagination

Pagination for notification (aggregated) feeds work the same way as it works for any other feed:

<Tabs>

```swift label="Swift"
let feed = client.feed(
    for: .init(
        group: "user",
        id: "john",
        activityLimit: 10
    )
)
// Page 1
try await feed.getOrCreate()
let activities = feed.state.activities // First 10 activities

// Page 2
let page2Activities = try await feed.queryMoreActivities(limit: 10)

let page1And2Activities = feed.state.activities
```

```kotlin label="Kotlin"
val feed = client.feed(
    query = FeedQuery(
        group = "user",
        id = "john",
        activityLimit = 10
    )
)
// Page 1
feed.getOrCreate()
val activities = feed.state.activities // The flow emits the first 10 activities

// Page 2
val page2Activities: Result<List<ActivityData>> = feed.queryMoreActivities(limit = 10)

val page1And2Activities = feed.state.activities
```

```js label="JavaScript"
const feed = client.feed("user", "jack");

// First page
await feed.getOrCreate({
  limit: 10,
});

// Second page
await feed.getNextPage();

console.log(feed.state.getLatestValue().is_loading_activities);
// Truthy if feed has next page
console.log(feed.state.getLatestValue().next);
console.log(feed.state.getLatestValue().activities);
// Only if feed group has aggregation turned on
console.log(feed.state.getLatestValue().aggregated_activities);
```

```js label="React"
const feed = client.feed("user", "jack");

// First page
await feed.getOrCreate({
  limit: 10,
});

const { activities, loadNextPage, is_loading, has_next_page } =
  useFeedActivities(feed) ?? {};
// Only if feed group has aggregation turned on
const { aggregated_activities, is_loading, has_next_page } =
  useAggregatedActivities(feed) ?? {};
```

```js label="React Native"
const feed = client.feed("user", "jack");

// First page
await feed.getOrCreate({
  limit: 10,
});

const { activities, loadNextPage, is_loading, has_next_page } =
  useFeedActivities(feed) ?? {};
// Only if feed group has aggregation turned on
const { aggregated_activities, is_loading, has_next_page } =
  useAggregatedActivities(feed) ?? {};
```

```dart label="Dart"
final feed = client.feedFromQuery(
  const FeedQuery(
    fid: FeedId(group: 'user', id: 'john'),
    activityLimit: 10,
  ),
);

// Page 1
await feed.getOrCreate();
final activities = feed.state.activities; // First 10 activities

// Page 2
final page2Activities = await feed.queryMoreActivities(limit: 10);

final page1And2Activities = feed.state.activities;
```

```js label="Node"
const feed = client.feeds.feed("user", "jack");
const firstPage = await feed.getOrCreate({
  limit: 10,
  user_id: "user_id",
});

const nextPage = await feed.getOrCreate({
  next: firstPage.next,
  limit: 10,
  user_id: "user_id",
});
```

```go label="Go"
feed := client.Feeds().Feed("user", "john")

// First page
firstPage, err := feed.GetOrCreate(context.Background(), &getstream.GetOrCreateFeedRequest{
  Limit:  getstream.PtrTo(10),
  UserID: getstream.PtrTo("john"),
})
if err != nil {
  log.Fatal("Error getting first page:", err)
}

// Second page request using next cursor
nextPage, err := feed.GetOrCreate(context.Background(), &getstream.GetOrCreateFeedRequest{
  Next:   firstPage.Data.Next,
  Limit:  getstream.PtrTo(10),
  UserID: getstream.PtrTo("john"),
})
if err != nil {
  log.Fatal("Error getting next page:", err)
}

log.Printf("First page activities count: %d", len(firstPage.Data.Activities))
log.Printf("Next page activities count: %d", len(nextPage.Data.Activities))
```

```java label="Java"
testFeed = new Feed("user", testUserId, feeds);
testFeed2 = new Feed("user", testUserId2, feeds);

GetOrCreateFeedRequest feedRequest1 =
    GetOrCreateFeedRequest.builder().userID(testUserId).build();
GetOrCreateFeedRequest feedRequest2 =
    GetOrCreateFeedRequest.builder().userID(testUserId2).build();

GetOrCreateFeedResponse feedResponse1 = testFeed.getOrCreate(feedRequest1).getData();
GetOrCreateFeedResponse feedResponse2 = testFeed2.getOrCreate(feedRequest2).getData();
testFeedId = feedResponse1.getFeed().getFeed();
testFeedId2 = feedResponse2.getFeed().getFeed();
```

```php label="php"
$feed = $feedsClient->feed('user', 'jack');

$feedResponse1 = $feed->getOrCreateFeed(
    new GeneratedModels\GetOrCreateFeedRequest(userID: "jack", limit: 10)
);

$feedResponse2 = $feed->getOrCreateFeed(
    new GeneratedModels\GetOrCreateFeedRequest(userID: "jack", limit: 10, next: $feedResponse1->getData()->next)
);
```

```csharp label="C#"
var feedResponse1 = await _feedsV3Client.GetOrCreateFeedAsync(
    FeedGroupID: "user",
    FeedID: _testFeedId,
    request: new GetOrCreateFeedRequest { UserID = _testUserId }
);
var feedResponse2 = await _feedsV3Client.GetOrCreateFeedAsync(
    FeedGroupID: "user",
    FeedID: _testFeedId2,
    request: new GetOrCreateFeedRequest { UserID = _testUserId2 }
);
```

```python label="Python"
feed_response_1 = self.test_feed.get_or_create(user_id=self.test_user_id)
feed_response_2 = self.test_feed_2.get_or_create(
    user_id=self.test_user_id_2
)
```

```ruby label="Ruby"
feed = client.feed('user', 'jack')

# First page
first_page = feed.get_or_create_feed(
  GetStream::Generated::Models::GetOrCreateFeedRequest.new(
    user_id: 'jack',
    limit: 10
  )
)

# Second page using next cursor from first page
if first_page.next
  second_page = feed.get_or_create_feed(
    GetStream::Generated::Models::GetOrCreateFeedRequest.new(
      user_id: 'jack',
      limit: 10,
      next: first_page.next
    )
  )
end
```

</Tabs>


## Flat notification feeds

If you **disable aggregation** on a notification feed group while keeping notification tracking enabled, you get a **flat notification feed**: every notification is a single activity in the feed (no grouping). Unread and unseen counts then refer to individual activities rather than aggregated groups.

Unread/unseen count is computed from latest 100 activities. This means unread/unseen count will never exceed 100.

### Turning off aggregation

Call **`updateFeedGroup`** server-side on the built-in **`notification`** feed group to turn off aggregation:

<Tabs>

```js label="Node"
await client.feeds.updateFeedGroup({
  id: "notification",
  notification: {
    track_read: true,
    track_seen: true,
  },
  push_notification: {
    enable_push: true,
    push_types: [
      /* list notification types here */
    ],
  },
});
```

```go label="Go"
_, err := client.Feeds().UpdateFeedGroup(context.Background(), "notification", &getstream.UpdateFeedGroupRequest{
  Notification: &getstream.NotificationConfig{
    TrackRead: getstream.PtrTo(true),
    TrackSeen: getstream.PtrTo(true),
  },
  PushNotification: &getstream.PushNotificationConfig{
    Enabled:       getstream.PtrTo(true),
    ActivityTypes: []string{}, // e.g. "follow", "comment", "mention"
  },
})
if err != nil {
  log.Fatal(err)
}
```

```php label="php"
use GetStream\GeneratedModels\UpdateFeedGroupRequest;
use GetStream\GeneratedModels\NotificationConfig;
use GetStream\GeneratedModels\PushNotificationConfig;

$feedsClient->updateFeedGroup('notification', new UpdateFeedGroupRequest(
  notification: new NotificationConfig(
    trackRead: true,
    trackSeen: true
  ),
  pushNotification: new PushNotificationConfig(
    enablePush: true,
    pushTypes: [], // list notification types here, e.g. ['follow', 'comment', 'mention']
  )
));
```

```csharp label="C#"
await feedsClient.UpdateFeedGroupAsync("notification", new UpdateFeedGroupRequest
{
  Notification = new NotificationConfig
  {
    TrackRead = true,
    TrackSeen = true,
  },
  PushNotification = new PushNotificationConfig
  {
    EnablePush = true,
    PushTypes = new List<string> { /* list notification types here */ },
  },
});
```

```python label="Python"
client.feeds.update_feed_group(
  id="notification",
  notification={
    "track_read": True,
    "track_seen": True,
  },
  push_notification={
    "enable_push": True,
    "push_types": [
      # list notification types here
    ],
  },
)
```

```rb label="Ruby"
require 'getstream_ruby'

client.feeds.update_feed_group(
  'notification',
  GetStream::Generated::Models::UpdateFeedGroupRequest.new(
    notification: GetStream::Generated::Models::NotificationConfig.new(
      track_read: true,
      track_seen: true
    ),
    push_notification: GetStream::Generated::Models::PushNotificationConfig.new(
      enable_push: true,
      push_types: [], # e.g. %w[follow comment mention]
    )
  )
)
```

```java label="Java"
import io.getstream.services.FeedsImpl;
import io.getstream.models.*;

FeedsImpl feedsClient = new FeedsImpl(new StreamHTTPClient("<API key>", "<API secret>"));

UpdateFeedGroupRequest request = UpdateFeedGroupRequest.builder()
  .notification(NotificationConfig.builder()
    .trackRead(true)
    .trackSeen(true)
    .build())
  .pushNotification(PushNotificationConfig.builder()
    .enablePush(true)
    .pushTypes(List.of()) // e.g. "follow", "comment", "mention"
    .build())
  .build();
feedsClient.updateFeedGroup("notification", request).execute();
```

</Tabs>

Turning off aggregation won't remove existing notification activities; they'll be returned in the `activities` array.

### Reading and paginating flat notifications

Load the first page with `getOrCreate`, then pass the returned `next` cursor on subsequent requests (or use your SDK’s pagination helpers). With aggregation off, use the **`activities`** list—**not** `aggregated_activities`.

<Tabs>

```kotlin label="Kotlin"
val notificationFeed = client.feed(group = "notification", id = userId)
notificationFeed.getOrCreate()

// With aggregation off, use `activities` (not `aggregatedActivities`)
val page1 = notificationFeed.state.activities.value

// Load more
notificationFeed.queryMoreActivities()
val page1And2 = notificationFeed.state.activities.value
```

```js label="JavaScript"
const notificationFeed = client.feed("notification", userId, {
  onNewActivity: () => "add-to-start",
});

await notificationFeed.getOrCreate({ limit: 20 });
const page1 = notificationFeed.state.getLatestValue().activities;

await notificationFeed.getNextPage();
const page1And2 = notificationFeed.state.getLatestValue().activities;
```

```js label="React"
const notificationFeed = client.feed("notification", userId, {
  onNewActivity: () => "add-to-start",
});

await notificationFeed.getOrCreate({ limit: 20 });

const { activities, loadNextPage, has_next_page } =
  useFeedActivities(notificationFeed) ?? {};
// `activities` is the flat notification list when aggregation is off
```

```js label="React Native"
const notificationFeed = client.feed("notification", userId, {
  onNewActivity: () => "add-to-start",
});

await notificationFeed.getOrCreate({ limit: 20 });

const { activities, loadNextPage, has_next_page } =
  useFeedActivities(notificationFeed) ?? {};
```

```js label="Node"
const notificationFeed = client.feeds.feed("notification", userId);

const first = await notificationFeed.getOrCreate({
  limit: 20,
  user_id: userId,
});
const page1 = first.activities;

const second = await notificationFeed.getOrCreate({
  limit: 20,
  next: first.next,
  user_id: userId,
});
```

```go label="Go"
notificationFeed := client.Feeds().Feed("notification", userId)

first, err := notificationFeed.GetOrCreate(context.Background(), &getstream.GetOrCreateFeedRequest{
  Limit:  getstream.PtrTo(20),
  UserID: getstream.PtrTo(userId),
})
if err != nil {
  log.Fatal(err)
}
page1 := first.Data.Activities

second, err := notificationFeed.GetOrCreate(context.Background(), &getstream.GetOrCreateFeedRequest{
  Next:   first.Data.Next,
  Limit:  getstream.PtrTo(20),
  UserID: getstream.PtrTo(userId),
})
_ = second
```

```php label="php"
$notificationFeed = $feedsClient->feed('notification', $userId);

$first = $notificationFeed->getOrCreate(
    new GeneratedModels\GetOrCreateFeedRequest(userID: $userId, limit: 20)
);
$page1 = $first->getData()->activities;

$second = $notificationFeed->getOrCreate(
    new GeneratedModels\GetOrCreateFeedRequest(
        userID: $userId,
        limit: 20,
        next: $first->getData()->next,
    )
);
```

```csharp label="C#"
var first = await feedsClient.GetOrCreateFeedAsync(
    "notification",
    userId,
    new GetOrCreateFeedRequest { UserID = userId, Limit = 20 }
);
var page1 = first.Activities;

var second = await feedsClient.GetOrCreateFeedAsync(
    "notification",
    userId,
    new GetOrCreateFeedRequest
    {
        UserID = userId,
        Limit = 20,
        Next = first.Next,
    }
);
```

```python label="Python"
notification_feed = client.feeds.feed("notification", user_id)
first = notification_feed.get_or_create(user_id=user_id, limit=20)
page1 = first["activities"]

second = notification_feed.get_or_create(
    user_id=user_id,
    limit=20,
    next=first["next"],
)
```

```ruby label="Ruby"
notification_feed = client.feed('notification', user_id)

first = notification_feed.get_or_create_feed(
  GetStream::Generated::Models::GetOrCreateFeedRequest.new(
    user_id: user_id,
    limit: 20
  )
)
page1 = first.activities

second = notification_feed.get_or_create_feed(
  GetStream::Generated::Models::GetOrCreateFeedRequest.new(
    user_id: user_id,
    limit: 20,
    next: first.next
  )
)
```

```java label="Java"
import io.getstream.services.Feed;
import io.getstream.services.FeedsImpl;
import io.getstream.services.framework.StreamHTTPClient;
import io.getstream.models.*;

FeedsImpl feedsClient = new FeedsImpl(new StreamHTTPClient("<API key>", "<API secret>"));
Feed notificationFeed = new Feed("notification", userId, feedsClient);

GetOrCreateFeedRequest firstReq = GetOrCreateFeedRequest.builder()
    .userID(userId)
    .limit(20)
    .build();
GetOrCreateFeedResponse first = notificationFeed.getOrCreate(firstReq).getData();
var page1 = first.getActivities();

GetOrCreateFeedRequest secondReq = GetOrCreateFeedRequest.builder()
    .userID(userId)
    .limit(20)
    .next(first.getNext())
    .build();
GetOrCreateFeedResponse second = notificationFeed.getOrCreate(secondReq).getData();
```

</Tabs>

### Per-activity `is_seen` and `is_read`

On flat notification feeds, each activity includes **`is_seen`** and **`is_read`** (when `track_seen` / `track_read` are enabled on the group). Use these booleans directly on each activity instead of on an aggregated group.

<Tabs>

```kotlin label="Kotlin"
val activity = notificationFeed.state.activities.value.firstOrNull()
val isRead = activity?.isRead == true
val isSeen = activity?.isSeen == true
```

```js label="JavaScript"
const activity = notificationFeed.state.getLatestValue().activities?.[0];
const isRead = activity?.is_read ?? false;
const isSeen = activity?.is_seen ?? false;
```

```js label="React"
// Inside your list render, each activity from useFeedActivities:
const isRead = activity.is_read;
const isSeen = activity.is_seen;
```

```js label="React Native"
const isRead = activity.is_read;
const isSeen = activity.is_seen;
```

```js label="Node"
const activity = first.activities[0];
const isRead = activity.is_read;
const isSeen = activity.is_seen;
```

```go label="Go"
act := first.Data.Activities[0]
isRead := act.IsRead != nil && *act.IsRead
isSeen := act.IsSeen != nil && *act.IsSeen
```

```php label="php"
$activity = $first->getData()->activities[0];
$isRead = $activity->isRead;
$isSeen = $activity->isSeen;
```

```csharp label="C#"
var activity = first.Activities[0];
var isRead = activity.IsRead ?? false;
var isSeen = activity.IsSeen ?? false;
```

```python label="Python"
activity = first["activities"][0]
is_read = activity.get("is_read") or False
is_seen = activity.get("is_seen") or False
```

```ruby label="Ruby"
activity = first.activities.first
is_read = activity.is_read == true
is_seen = activity.is_seen == true
```

```java label="Java"
Activity activity = first.getActivities().get(0);
boolean isRead = Boolean.TRUE.equals(activity.getIsRead());
boolean isSeen = Boolean.TRUE.equals(activity.getIsSeen());
```

</Tabs>

### Marking flat notifications as seen or read

The API is the same as for aggregated notification feeds (`markActivity`), but you pass **activity IDs** in `mark_seen` / `mark_read` instead of aggregation **group** strings. `mark_all_seen` and `mark_all_read` still mark every notification on the feed.

<Tabs>

```kotlin label="Kotlin"
notificationFeed.markActivity(
    request = MarkActivityRequest(
        markSeen = listOf(activity.id)
        // or markRead = listOf(activity.id)
    )
)
```

```js label="JavaScript"
await notificationFeed.markActivity({
  mark_seen: [activity.id],
  // or mark_read: [activity.id]
});
```

```js label="React"
await notificationFeed.markActivity({
  mark_seen: [activity.id],
});
```

```js label="React Native"
await notificationFeed.markActivity({
  mark_read: [activity.id],
});
```

```js label="Node"
await notificationFeed.markActivity({
  mark_seen: [activity.id],
  user_id: userId,
});
```

```go label="Go"
_, err = notificationFeed.MarkActivity(context.Background(), &getstream.MarkActivityRequest{
  MarkSeen: []string{activity.ID},
  UserID:   getstream.PtrTo(userId),
})
```

```php label="php"
$notificationFeed->markActivity(new GeneratedModels\MarkActivityRequest(
    markSeen: [$activity->id],
    userID: $userId,
));
```

```csharp label="C#"
await feedsClient.MarkActivityAsync(
    "notification",
    userId,
    new MarkActivityRequest
    {
        MarkSeen = new List<string> { activity.Id },
        UserID = userId,
    }
);
```

```python label="Python"
notification_feed = client.feeds.feed("notification", user_id)
notification_feed.mark_activity(
    user_id=user_id,
    mark_seen=[activity_id],
)
```

```ruby label="Ruby"
mark_request = GetStream::Generated::Models::MarkActivityRequest.new(
  mark_seen: [activity.id],
  user_id: user_id
)
client.feeds.mark_activity('notification', user_id, mark_request)
```

```java label="Java"
import io.getstream.services.Feed;
import io.getstream.services.FeedsImpl;
import io.getstream.services.framework.StreamHTTPClient;
import io.getstream.models.*;

FeedsImpl feedsClient = new FeedsImpl(new StreamHTTPClient("<API key>", "<API secret>"));
Feed notificationFeed = new Feed("notification", userId, feedsClient);

MarkActivityRequest markRequest = MarkActivityRequest.builder()
    .markSeen(List.of(activityId))
    .userID(userId)
    .build();
notificationFeed.markActivity(markRequest).execute();
```

</Tabs>


---

This page was last updated at 2026-05-22T16:31:48.529Z.

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