The built-in notification feed comes with the necessary configurations, but it's also possible to create your own notification feed group:
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, },});
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)}
// 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);
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\") }}" }});
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\") }}" ))
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 ) ))
Note: Comments are not deduplicated. Each comment will create a separate notification activity, regardless of the deduplication window setting.
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.
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.
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.
When you have aggregation turned on, unread/unseen will refer to the number of aggregated groups.
The built-in notification groups can automatically create notifications for the most common interactions (see Built-in notification feed section).
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.
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>"));
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.
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)
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>.
// Eric follows JaneericFeed.follow( targetFid = janeFeed.fid, createNotificationActivity = true // When true Jane's notification feed will be updated with follow activity)// Eric comments on Jane's activityericFeed.addComment( 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 activityericFeed.addReaction( activityId = janeActivity.activityId, 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 SaraericFeed.addCommentReaction( commentId = saraComment.id, request = AddCommentReactionRequest( type = "like", createNotificationActivity = true // When true Sara's notification feed will be updated with comment reaction activity ),)
// Eric follows Janeawait ericTimeline.follow(janeFeed, { // When true Jane's notification feed will be updated with follow activity create_notification_activity: true,});// Eric comments on Jane's activityawait 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 activityawait 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 Saraawait ericClient.addCommentReaction({ id: saraComment.id, type: "like", // When true Sara's notification feed will be updated with comment reaction activity create_notification_activity: true,});
// Eric follows Janeawait ericTimeline.follow(janeFeed, { // When true Jane's notification feed will be updated with follow activity create_notification_activity: true,});// Eric comments on Jane's activityawait 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 activityawait 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 Saraawait ericClient.addCommentReaction({ id: saraComment.id, type: "like", // When true Sara's notification feed will be updated with comment reaction activity create_notification_activity: true,});
// Eric follows Janeawait 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 activityawait 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 activityawait 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 Saraawait 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});
// Eric follows Janeawait ericFeed.follow( targetFid: janeFeed.fid, createNotificationActivity: true, // When true Jane's notification feed will be updated with follow activity);// Eric comments on Jane's activityawait 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 activityawait 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 Saraawait 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 ),);
// Eric follows Janeawait 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 activityawait 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 activityawait 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 Saraawait 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",});
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)}
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"));
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.
// Alice updates her activity to mention BobericFeed.updateActivity( activityId = activityId, 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 CharlieericFeed.updateComment( commentId = commentId, comment = "Hey @Charlie!", mentionedUserIds = listOf("charlie"), handleMentionNotifications = true // When true, Charlie will receive a comment_mention notification)// Alice removes mentions from her activityericFeed.updateActivity( activityId = activityId, text = "Updated text without mentions", mentionedUserIds = emptyList(), handleMentionNotifications = true // When true, mention notifications for removed users are deleted)
// Alice updates her activity to mention Bobawait 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 Charlieawait 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 activityawait 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});
// Alice updates her activity to mention Bobawait 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 Charlieawait 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 activityawait ericFeed.updateActivity( activityId: activityId, request: UpdateActivityRequest( text: "Updated text without mentions", mentionedUserIDs: [], handleMentionNotifications: true, // When true, mention notifications for removed users are deleted ),);
// Alice updates her activity to mention Bobawait 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 Charlieawait 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 activityawait 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",});
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)}
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"));
Commenting and reacting to your own activities and comments will not create 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.
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.
// Eric unfollows JaneericFeed.unfollow( targetFid = janeFeed.fid, // When true the corresponding notification activity will be removed from Jane's notification feed deleteNotificationActivity = true)// Eric removes his commentericFeed.deleteComment( commentId = commentId, // When true the corresponding notification activity will be removed from Jane's notification feed deleteNotificationActivity = true)// Eric removes his activity reactionericFeed.deleteReaction( 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 reactionericFeed.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 mentionsericFeed.deleteActivity( activityId = activityId, // When true, mention notifications for all mentioned users will be removed deleteNotificationActivity = true)// Eric deletes multiple activities with mentions (batch operation)ericFeed.deleteActivities( activityIds = listOf(activityId1, activityId2), // When true, mention notifications for all mentioned users will be removed deleteNotificationActivity = true)
// Eric unfollows Janeawait ericTimeline.unfollow(janeFeed, { // When true the corresponding notification activity will be removed from Jane's notification feed delete_notification_activity: true,});// Eric removes his commentawait 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 reactionawait 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 reactionawait 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 mentionsawait 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,});
// Eric unfollows Janeawait ericFeed.unfollow( targetFid: janeFeed.fid, // When true the corresponding notification activity will be removed from Jane's notification feed deleteNotificationActivity: true,);// Eric removes his commentawait ericFeed.deleteComment( commentId: commentId, // When true the corresponding notification activity will be removed from Jane's notification feed deleteNotificationActivity: true,);// Eric removes his activity reactionawait 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 reactionawait 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 mentionsawait 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,);
// Eric unfollows Janeawait 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 commentawait 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 reactionawait 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 reactionawait 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 mentionsawait 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",});
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)}
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"));
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.
{ "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" } }}
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
{% if comment_id %} {{ comment_id }}_{{ type }}_{{ time.strftime('%Y-%m-%d') }}{% else %} {{ target_id }}_{{ type }}_{{ time.strftime('%Y-%m-%d') }}{% endif %}
require 'getstream_ruby'# Get or create notification feednotification_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 activitiesnotifications = response.aggregated_activities if response.respond_to?(:aggregated_activities)
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). 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.
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.
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.
To check if a notification group is read or seen, simply read the fields:
The server uses a hybrid algorithm to determine these values:
Check if the group ID is in the seen/read ID lists
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.
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.
{ "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:
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.
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.
notificationFeed.markActivity( request = MarkActivityRequest( // Mark all notifications as seen... markAllSeen = true, // ...or only selected ones markSeen = listOf( /* group names to mark as seen */ ) ))
await notificationFeed.markActivity({ // Mark all notifications as seen... mark_all_seen: true, // ...or only selected ones mark_seen: [ /* group names to mark as seen */ ],});
await notificationFeed.markActivity({ // Mark all notifications as seen... mark_all_seen: true, // ...or only selected ones mark_seen: [ /* group names to mark as seen */ ],});
await notificationFeed.markActivity({ // Mark all notifications as seen... mark_all_seen: true, // ...or only selected ones mark_seen: [ /* group names to mark as seen */ ],});
await notificationFeed.markActivity( request: const MarkActivityRequest( // Mark all notifications as seen... markAllSeen: true, // ...or only selected ones markSeen: [ /* group names to mark as seen */ ], ),);
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>",});
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)}
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"));
notificationFeed.markActivity( request = MarkActivityRequest( // Mark all notifications as read... markAllRead = true, // ...or only selected ones markRead = listOf( /* group names to mark as read */ ) ))
await notificationFeed.markActivity({ // Mark all notifications as read... mark_all_read: true, // ...or only selected ones mark_read: [ /* group names to mark as read */ ],});
await notificationFeed.markActivity({ // Mark all notifications as read... mark_all_read: true, // ...or only selected ones mark_read: [ /* group names to mark as read */ ],});
await notificationFeed.markActivity({ // Mark all notifications as read... mark_all_read: true, // ...or only selected ones mark_read: [ /* group names to mark as read */ ],});
await notificationFeed.markActivity( request: const MarkActivityRequest( // Mark all notifications as read... markAllRead: true, // ...or only selected ones markRead: [ // group names to mark as read ], ),);
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>",});
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)}
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);
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 for configuration details and examples.
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.
val feed = client.feed( query = FeedQuery( group = "user", id = "john", activityLimit = 10 ))// Page 1feed.getOrCreate()val activities = feed.state.activities // First 10 activities// Page 2val page2Activities: Result<List<ActivityData>> = feed.queryMoreActivities(limit = 10)val page1And2Activities = feed.state.activities
const feed = client.feed("user", "jack");// First pageawait feed.getOrCreate({ limit: 10,});// Second pageawait feed.getNextPage();console.log(feed.state.getLatestValue().is_loading_activities);// Truthy if feed has next pageconsole.log(feed.state.getLatestValue().next);console.log(feed.state.getLatestValue().activities);// Only if feed group has aggregation turned onconsole.log(feed.state.getLatestValue().aggregated_activities);
const feed = client.feed("user", "jack");// First pageawait feed.getOrCreate({ limit: 10,});const { activities, loadNextPage, is_loading, has_next_page } = useFeedActivities(feed) ?? {};// Only if feed group has aggregation turned onconst { aggregated_activities, is_loading, has_next_page } = useAggregatedActivities(feed) ?? {};
const feed = client.feed("user", "jack");// First pageawait feed.getOrCreate({ limit: 10,});const { activities, loadNextPage, is_loading, has_next_page } = useFeedActivities(feed) ?? {};// Only if feed group has aggregation turned onconst { aggregated_activities, is_loading, has_next_page } = useAggregatedActivities(feed) ?? {};
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.
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—notaggregated_activities.
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.
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.
await notificationFeed.markActivity({ mark_seen: [activity.id], // or mark_read: [activity.id]});