use GetStream\GeneratedModels\CreateFeedGroupRequest;
use GetStream\GeneratedModels\ActivitySelectorConfig;
use GetStream\GeneratedModels\RankingConfig;
use GetStream\GeneratedModels\ActivityProcessorConfig;
use GetStream\GeneratedModels\ActivityFilterConfig;
// Create feed group request
$request = new CreateFeedGroupRequest(
id: "myid",
activitySelectors: [
new ActivitySelectorConfig(type: "following")
],
ranking: new RankingConfig(type: "recency"),
activityProcessors: [
new ActivityProcessorConfig(type: "text_interest_tags")
],
activityFilter: new ActivityFilterConfig(
excludeOwnerActivities: true
),
custom: (object) [
"description" => "My custom feed group"
]
);
// Create the feed group
$response = $feedsClient->createFeedGroup($request);Intro & Defaults
Feed groups are templates that define how different feeds behave in your application. The configuration options let you create feeds that work differently depending on your use case.
By adjusting settings like ranking, aggregation, activity selectors, and processors, you can tailor each feed group to serve specific purposes in your app.
Note that any write operation to feed groups/views can take up to 30 seconds to propagate to all API nodes.
Built-in groups
There are several feed groups setup by default.
| Group | Description |
|---|---|
user | A feed setup for the content a user creates. Typically you add activities here when someone writes a post |
timeline | The timeline feed is used when you're following. So if user Charlie is following John, timeline:charlie would follow user:john |
foryou | A version of the timeline feed that adds popular content, and prioritizes popularity over recency |
notification | A notification feed. Think of the bell icon you see in most apps |
story | A feed set up for users to post story activities (activities with expiration data) |
stories | A timeline feed which can be used to follow other users' stories. |
You can update the default feed group configurations or create your own feed groups.
Create feed groups
Here's how to create a feed group using the API:
Applications can't have more than 100 feed groups
Alternatively you can use the getOrCreateFeedGroup endpoint to create and apply settings, or return the existing group.
Exclude owner activities
Set activity_filter.exclude_owner_activities on a feed group to hide the owner's own activities when reading that owner's feed.
Each feed has a created_by_id. When this flag is on, the filter drops activities whose user_id matches the feed's created_by_id, so the feed owner's own posts are hidden from their own feed, but still visible to followers who see the fanout in their own feeds.
- Reading
user:jimmyhides activities whereuser_idisjimmy. - Reading
user:amystill shows activities fanned out fromuser:jimmy(and hides Amy's own).
The flag is set once at the feed group level (not per view) and applied automatically per feed. It works on the user, timeline, and foryou built-in groups, and on custom groups.
Where it's not valid: the API rejects activity_filter on:
notification,story, orstoriesbuilt-in groups- feed groups that also set
aggregation,notification, orstoriesconfig
For self-notifications, no configuration is needed. The Feeds API already skips creating notifications when the actor is the activity owner.
Update semantics: feed group updates use full-replacement semantics. Omitting activity_filter on an UpdateFeedGroup call clears the stored value, so include it on every update if you want to preserve it.
When flag changes take effect: changes apply on the next read. On ranked feeds (for example foryou, or groups with a ranking config), updating the feed group invalidates outstanding pagination tokens, so clients mid-pagination may need to restart from the first page.
Overview of the feed group model
FeedGroupResponse fields:
| Field | Description |
|---|---|
id | Feed group identifier. |
default_visibility | Default activity visibility (public, visible, followers, members, private). |
activity_selectors | Activity source selection rules. |
activity_processors | Enrichment and processing pipeline configuration. |
ranking | Ranking formula/configuration for feed ordering. |
aggregation | Aggregation settings for grouped reads. |
activity_filter | Read-time filters, including exclude_owner_activities. |
notification | Notification configuration for notification feeds. |
stories | Stories configuration for stories feeds. |
push_notification | Push notification behavior configuration. |
custom | Custom metadata object. |
created_at | Feed group creation timestamp. |
updated_at | Feed group last updated timestamp. |
deleted_at | Deletion timestamp (present when soft-deleted). |
List feed groups
To list existing feed groups and their configurations:
// List all feed groups (excluding soft-deleted ones)
$response = $feedsClient->listFeedGroups(false);
// List all feed groups including soft-deleted ones
$response = $feedsClient->listFeedGroups(true);Activity Ranking
When ranking activities you can specify a ranking formula.
$createResponse = $feedsClient->createFeedGroup(
new GeneratedModels\CreateFeedGroupRequest(
id: "mytimeline",
defaultVisibility: 'public',
ranking: new GeneratedModels\RankingConfig(
type: 'expression',
score: 'decay_linear(time) * popularity'
),
activitySelectors: [
new GeneratedModels\ActivitySelectorConfig(
type: 'following'
)
]
)
);For you Feeds
Many apps want to have a "for you" or personalized feed. There are a couple benefits to a personalized feed:
- Works well even if your users don't spend much time setting up follows
- Can be a mechanism to discover new content or things to follow
Stream offers a few built-in methods to create a for you feed and gives you the API access to do more advanced customization if needed.
This next example is a bit more complicated. It uses an activity processor to add topic data to activities, activity selectors to pull in content from different sources, and ranks content you're likely to engage with higher in the feed.
use GetStream\GeneratedModels\CreateFeedGroupRequest;
use GetStream\GeneratedModels\ActivitySelectorConfig;
use GetStream\GeneratedModels\RankingConfig;
use GetStream\GeneratedModels\ActivityProcessorConfig;
use GetStream\GeneratedModels\GetOrCreateFeedRequest;
// Create feed group with activity processors, activity selectors, and ranking
$createFeedGroupRequest = new CreateFeedGroupRequest(
id: "mytimeline",
// Run the activity processors to analyse topics for text & images
activityProcessors: [
new ActivityProcessorConfig(type: "text_interest_tags"),
new ActivityProcessorConfig(type: "image_interest_tags"),
],
// Activity selectors change which activities are included in the feed
// The default "following" selectors gets activities from the feeds you follow
// The "popular" activity selectors includes the popular activities
// And "interest" activities similar to activities you've engaged with in the past
// You can use multiple selectors in 1 feed
activitySelectors: [
new ActivitySelectorConfig(type: "popular"),
new ActivitySelectorConfig(type: "following"),
new ActivitySelectorConfig(type: "interest"),
],
// Rank for a user based on interest score
// This calculates a score 0-1.0 of how well the activity matches the user's prior interest
ranking: new RankingConfig(
type: "interest",
score: "decay_linear(time) * interest_score * decay_linear(popularity)"
)
);
// Create the feed group
$response = $feedsClient->createFeedGroup($createFeedGroupRequest);
// Create and get the feed for a specific user
$forYouFeed = $feedsClient->feed("mytimeline", "thierry");
$feedRequest = new GetOrCreateFeedRequest(userID: "thierry");
$feedResponse = $forYouFeed->getOrCreateFeed($feedRequest);Aggregation & Notification Feeds
Aggregation groups similar activities together, which is useful for notification feeds and reducing noise.
Aggregation Format
You can create your own aggregated feeds:
// 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);Notification Feed Example
The built-in notification feed comes with the necessary configurations:
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;Marking Notifications as Read
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);Updating feed groups
It's possible to update any custom or built-in feed group:
use GetStream\GeneratedModels\UpdateFeedGroupRequest;
// Create update request
$updateRequest = new UpdateFeedGroupRequest(
// Fields to update
// activityProcessors: [...],
// activitySelectors: [...],
// ranking: new RankingConfig(...),
// custom: (object) [...]
);
// Update the feed group
$response = $feedsClient->updateFeedGroup("myid", $updateRequest);Deleting feed groups
Deleting feed groups will not cascade delete associated resources. It will make feeds within the feed group unreadable but any fanout that has happened before deletion will stay in effect.
If you need a clean slate during development it is advised to create a new feed group instead.
// Delete the feed group (soft delete by default)
$response = $feedsClient->deleteFeedGroup("mytimeline", false);
// For hard delete, pass true as the second parameter
// $response = $feedsClient->deleteFeedGroup("mytimeline", true);