// Add an activity to 1 feed
let activity = try await feed.addActivity(
request: .init(
text: "hello world",
type: "post"
)
)
// Add an activity to multiple feeds
let multiFeedActivity = try await client.addActivity(
request: .init(
feeds: ["user:1", "stock:apple"],
text: "apple stock will go up",
type: "post"
)
)
Activity Feeds V3 is in closed alpha — do not use it in production (just yet).
Activities
Creating Activities
The example below shows how to create an activity and add it to a feed.
// Add an activity to 1 feed
val activity: Result<ActivityData> = feed.addActivity(
request = FeedAddActivityRequest(
text = "hello world",
type = "post"
)
)
// Add an activity to multiple feeds
val multiFeedActivity: Result<ActivityData> = client.addActivity(
request = AddActivityRequest(
fids = listOf("user:1", "stock:apple"),
text = "apple stock will go up",
type = "post"
)
)
// Add an activity to 1 feed
const response = await feed.addActivity({
type: "post",
text: "apple stock will go up",
});
console.log(response.activity);
//...or multiple feeds
const response = await client.addActivity({
feeds: ["user:1", "stock:apple"],
type: "post",
text: "apple stock will go up",
});
// Add an activity to 1 feed or multiple feeds
const response = await client.feeds.addActivity({
feeds: ["user:1", "stock:apple"],
type: "post",
text: "apple stock will go up",
// Provide user id, the owner of the activity
user_id: "<user id>",
});
console.log(response.activity);
feedIdentifier := fmt.Sprintf("%s:%s", userFeedType, testUserID)
response, err := feedsClient.AddActivity(ctx, &getstream.AddActivityRequest{
Type: "post",
Feeds: []string{feedIdentifier},
Text: getstream.PtrTo("This is a test activity from Go SDK"),
UserID: &testUserID,
Custom: map[string]interface{}{
"test_field": "test_value",
"timestamp": time.Now().Unix(),
},
})
AddActivityRequest activity =
AddActivityRequest.builder()
.type("post")
.feeds(List.of(testFeedId))
.text("This is a test activity from Java SDK")
.userID(testUserId)
.build();
AddActivityResponse response = feeds.addActivity(activity).execute().getData();
$activity = new GeneratedModels\AddActivityRequest(
type: 'post',
feeds: [$this->testFeed->getFeedIdentifier()],
text: 'This is a test activity from PHP SDK',
userID: $this->testUserId,
custom: (object)[
'test_field' => 'test_value',
'timestamp' => time()
]
);
$response = $this->feedsV3Client->addActivity($activity);
var activity = new AddActivityRequest
{
Type = "post",
Text = "This is a test activity from .NET SDK",
UserID = _testUserId,
Feeds = new List<string> { $"user:{_testFeedId}" }
};
var response = await _feedsV3Client.AddActivityAsync(activity);
response = self.client.feeds.add_activity(
type="post",
feeds=[self.test_feed.get_feed_identifier()],
text="This is a test activity from Python SDK",
user_id=self.test_user_id,
custom={
"test_field": "test_value",
"timestamp": int(datetime.now().timestamp()),
},
)
The above example was quite simple. Here are a few more examples:
Image & Video
let imageActivity = try await feed.addActivity(
request: .init(
attachments: [
Attachment(
imageUrl: "https://example.com/image.jpg",
type: "image"
)
],
text: "look at NYC",
type: "post"
)
)
val imageActivity: Result<ActivityData> = feed.addActivity(
request = FeedAddActivityRequest(
attachments = listOf(
Attachment(
imageUrl = "https://example.com/image.jpg",
type = "image"
)
),
text = "look at NYC",
type = "post"
)
)
feed.addActivity({
type: "post",
text: "look at NYC",
attachments: [
{
type: "image",
image_url: "https://example.com/image.png",
custom: {},
},
],
});
client.feeds.addActivity({
feeds: ["user:1"],
type: "post",
text: "look at NYC",
attachments: [
{
type: "image",
image_url: "https://example.com/image.png",
custom: {},
},
],
// Provide user id, the owner of the activity
user_id: "<user id>",
});
feedIdentifier := fmt.Sprintf("%s:%s", userFeedType, testUserID)
response, err := feedsClient.AddActivity(ctx, &getstream.AddActivityRequest{
Type: "video",
Feeds: []string{feedIdentifier},
Text: getstream.PtrTo("Check out this amazing video!"),
UserID: &testUserID,
Attachments: []getstream.Attachment{
{
AssetUrl: getstream.PtrTo("https://example.com/amazing-video.mp4"),
Type: getstream.PtrTo("video"),
Title: getstream.PtrTo("Amazing Video"),
Custom: map[string]interface{}{
"duration": 120,
},
},
},
Custom: map[string]interface{}{
"video_quality": "4K",
"duration_seconds": 120,
},
})
AddActivityRequest activity =
AddActivityRequest.builder()
.type("video")
.feeds(List.of(testFeedId))
.text("Check out this amazing video!")
.userID(testUserId)
.build();
AddActivityResponse response = feeds.addActivity(activity).execute().getData();
$activity = new GeneratedModels\AddActivityRequest(
type: 'video',
feeds: [$this->testFeed->getFeedIdentifier()],
text: 'Check out this amazing video!',
userID: $this->testUserId,
attachments: [
new GeneratedModels\Attachment(
assetUrl: 'https://example.com/amazing-video.mp4',
type: 'video',
title: 'Amazing Video',
custom: (object)['duration' => 120]
)
],
custom: (object)[
'video_quality' => '4K',
'duration_seconds' => 120
]
);
$response = $this->feedsV3Client->addActivity($activity);
var activity = new AddActivityRequest
{
Type = "video",
Text = "Check out this amazing video!",
UserID = _testUserId,
Feeds = new List<string> { $"user:{_testFeedId}" }
// Note: Video attachments would be added here in a real scenario
};
var response = await _feedsV3Client.AddActivityAsync(activity);
response = self.client.feeds.add_activity(
type="video",
feeds=[self.test_feed.get_feed_identifier()],
text="Check out this amazing video!",
user_id=self.test_user_id,
attachments=[
Attachment(
custom={"duration": 120},
asset_url="https://example.com/amazing-video.mp4",
type="video",
title="Amazing Video",
)
],
custom={"video_quality": "4K", "duration_seconds": 120},
)
Stories
For a story you typically want multiple attachments and an expiration set:
let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: Date())!
let storyActivity = try await feed.addActivity(
request: .init(
attachments: [
Attachment(imageUrl: "https://example.com/image1.jpg", type: "image"),
Attachment(assetUrl: "https://example.com/video1.mp4", type: "video")
],
expiresAt: ISO8601DateFormatter().string(from: tomorrow),
text: "My story",
type: "story"
)
)
val tomorrow = Instant.now().plus(1, ChronoUnit.DAYS)
val storyActivity: Result<ActivityData> = feed.addActivity(
request = FeedAddActivityRequest(
attachments = listOf(
Attachment(imageUrl = "https://example.com/image1.jpg", type = "image"),
Attachment(assetUrl = "https://example.com/video1.mp4", type = "video")
),
expiresAt = tomorrow.toString(),
text = "My story",
type = "story"
)
)
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
feed.addActivity({
type: "story",
text: "My story",
expires_at: tomorrow.toISOString(),
attachments: [
{
type: "image",
image_url: "https://example.com/image.png",
custom: {},
},
{
type: "video",
image_url: "https://example.com/video.mp4",
custom: {},
},
],
});
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
client.feeds.addActivity({
feeds: ["story:1"],
type: "story",
text: "My story",
expires_at: tomorrow.toISOString(),
attachments: [
{
type: "image",
image_url: "https://example.com/image.png",
custom: {},
},
{
type: "video",
image_url: "https://example.com/video.mp4",
custom: {},
},
],
user_id: "<user id>",
});
tomorrow := time.Now().Add(24 * time.Hour)
feedIdentifier := fmt.Sprintf("%s:%s", userFeedType, testUserID)
response, err := feedsClient.AddActivity(ctx, &getstream.AddActivityRequest{
Type: "story",
Feeds: []string{feedIdentifier},
Text: getstream.PtrTo("My daily story - expires tomorrow!"),
UserID: &testUserID,
ExpiresAt: getstream.PtrTo(tomorrow.Format(time.RFC3339)),
Attachments: []getstream.Attachment{
{
ImageUrl: getstream.PtrTo("https://example.com/story-image.jpg"),
Type: getstream.PtrTo("image"),
},
{
AssetUrl: getstream.PtrTo("https://example.com/story-video.mp4"),
Type: getstream.PtrTo("video"),
Custom: map[string]interface{}{
"duration": 15,
},
},
},
Custom: map[string]interface{}{
"story_type": "daily",
"auto_expire": true,
},
})
java.time.Instant tomorrow =
java.time.Instant.now().plus(1, java.time.temporal.ChronoUnit.DAYS);
String expiresAt = tomorrow.toString(); // RFC3339 format
Map<String, Object> customData = new HashMap<>();
customData.put("story_type", "daily");
customData.put("auto_expire", true);
AddActivityRequest activity =
AddActivityRequest.builder()
.type("story")
.feeds(List.of(testFeedId))
.text("My daily story - expires tomorrow!")
.userID(testUserId)
.expiresAt(expiresAt)
.attachments(
List.of(
Attachment.builder()
.imageUrl("https://example.com/story-image.jpg")
.type("image")
.build(),
Attachment.builder()
.assetUrl("https://example.com/story-video.mp4")
.type("video")
.custom(Map.of("duration", 15))
.build()))
.custom(customData)
.build();
AddActivityResponse response = feeds.addActivity(activity).execute().getData();
$tomorrow = new \DateTime('+1 day');
$activity = new GeneratedModels\AddActivityRequest(
type: 'story',
feeds: [$this->testFeed->getFeedIdentifier()],
text: 'My daily story - expires tomorrow!',
userID: $this->testUserId,
expiresAt: $tomorrow->format('c'), // ISO 8601 format
attachments: [
new GeneratedModels\Attachment(
imageUrl: 'https://example.com/story-image.jpg',
type: 'image'
),
new GeneratedModels\Attachment(
assetUrl: 'https://example.com/story-video.mp4',
type: 'video',
custom: (object)['duration' => 15]
)
],
custom: (object)[
'story_type' => 'daily',
'auto_expire' => true
]
);
$response = $this->feedsV3Client->addActivity($activity);
var tomorrow = DateTime.UtcNow.AddDays(1);
var activity = new AddActivityRequest
{
Type = "story",
Text = "My daily story - expires tomorrow!",
UserID = _testUserId,
Feeds = new List<string> { $"user:{_testFeedId}" },
ExpiresAt = tomorrow.ToString("yyyy-MM-ddTHH:mm:ssZ"),
Attachments = new List<Attachment>
{
new Attachment
{
ImageUrl = "https://example.com/story-image.jpg",
Type = "image"
},
new Attachment
{
AssetUrl = "https://example.com/story-video.mp4",
Type = "video"
}
},
Custom = new Dictionary<string, object>
{
["story_type"] = "daily",
["auto_expire"] = true
}
};
var response = await _feedsV3Client.AddActivityAsync(activity);
tomorrow = datetime.now() + timedelta(days=1)
response = self.client.feeds.add_activity(
type="story",
feeds=[self.test_feed.get_feed_identifier()],
text="My daily story - expires tomorrow!",
user_id=self.test_user_id,
expires_at=tomorrow.strftime("%Y-%m-%dT%H:%M:%SZ"),
attachments=[
Attachment(
custom={},
image_url="https://example.com/story-image.jpg",
type="image",
),
Attachment(
custom={"duration": 15},
asset_url="https://example.com/story-video.mp4",
type="video",
),
],
custom={"story_type": "daily", "auto_expire": True},
)
Overview of All Activity Fields
Here’s an overview of all the fields you can add when creating an activity:
Field | Description |
---|---|
id | Either set your own id, or let our server generate it. Setting your own ID can be convenient if your activity maps 1 to 1 to something in your database. |
type | The type of activity. Defaults to “post” if not provided. |
feeds | The list of feeds (format: “group id:feed id”) to add this activity to |
text | The text for this activity |
attachments | A list of attachments. Video, images, location, etc. Also supports custom attachments |
custom | Any custom data you want to add |
visibility | Visibility levels for the activity |
location | Specify an activity location. This allows for feeds to show content close to you. Format: ActivityLocation(lat: 40.014984, lng: -105.270546) |
expiresAt | When the activity expires. After this timestamp it’s only visible to the person who created this activity (or server side API calls) |
mentionedUserIds | A list of users mentioned in this activity |
parentId | The parent activity. Used for replies/reshares etc |
searchData | Any extra data you want to search on for this activity |
filterTags | Array of strings that you can filter on when querying the feed |
interestTags | Either set the interest tags manually or enable an activity processor to have AI determine the topics for this activity. Used for “for you” style feeds |
Adding Many Activities
You can also batch add activities. Here’s an example:
let activities = [
ActivityRequest(
feeds: ["user:123"],
id: "1",
text: "hi",
type: "post"
),
ActivityRequest(
feeds: ["user:456"],
id: "2",
text: "hi",
type: "post"
)
]
let upsertedActivities = try await client.upsertActivities(activities)
val activities = listOf(
ActivityRequest(
feeds = listOf("user:123"),
id = "1",
text = "hi",
type = "post"
),
ActivityRequest(
feeds = listOf("user:456"),
id = "2",
text = "hi",
type = "post"
)
)
val upsertedActivities: Result<List<ActivityData>> = client.upsertActivities(activities)
client.upsertActivities({
activities: [
{
feeds: ["user:123"],
id: "1",
type: "post",
text: "hi",
},
{
feeds: ["user:456"],
id: "2",
type: "post",
text: "hi",
},
],
});
client.feeds.upsertActivities({
activities: [
{
feeds: ["user:123"],
id: "1",
type: "post",
text: "hi",
user_id: "<user id>",
},
{
feeds: ["user:456"],
id: "2",
type: "post",
text: "hi",
user_id: "<user id>",
},
],
});
activities := []getstream.ActivityRequest{
{
Type: "post",
Text: getstream.PtrTo("Batch activity 1"),
UserID: &testUserID,
},
{
Type: "post",
Text: getstream.PtrTo("Batch activity 2"),
UserID: &testUserID,
},
}
response, err := feedsClient.UpsertActivities(ctx, &getstream.UpsertActivitiesRequest{
Activities: activities,
})
List<ActivityRequest> activities =
List.of(
ActivityRequest.builder()
.type("post")
.text("Batch activity 1")
.userID(testUserId)
.build(),
ActivityRequest.builder()
.type("post")
.text("Batch activity 2")
.userID(testUserId)
.build());
UpsertActivitiesRequest request =
UpsertActivitiesRequest.builder().activities(activities).build();
UpsertActivitiesResponse response = feeds.upsertActivities(request).execute().getData();
$activities = [
[
'type' => 'post',
'text' => 'Batch activity 1',
'user_id' => $this->testUserId,
],
[
'type' => 'post',
'text' => 'Batch activity 2',
'user_id' => $this->testUserId,
]
];
$response = $this->feedsV3Client->upsertActivities(
new GeneratedModels\UpsertActivitiesRequest(activities: $activities)
);
var activities = new List<ActivityRequest>
{
new ActivityRequest
{
Type = "post",
Text = "Batch activity 1",
UserID = _testUserId
},
new ActivityRequest
{
Type = "post",
Text = "Batch activity 2",
UserID = _testUserId
}
};
var response = await _feedsV3Client.UpsertActivitiesAsync(
new UpsertActivitiesRequest { Activities = activities }
);
activities = [
ActivityRequest(
type="post",
text="Batch activity 1",
user_id=self.test_user_id,
feeds=[self.test_feed.get_feed_identifier()],
),
ActivityRequest(
type="post",
text="Batch activity 2",
user_id=self.test_user_id,
feeds=[self.test_feed.get_feed_identifier()],
),
]
response = self.client.feeds.upsert_activities(activities=activities)
Updating & Deleting Activities
This example shows how to update or delete an activity:
// Update an activity
let updatedActivity = try await feed.updateActivity(
id: "123",
request: .init(
custom: ["custom": "custom"],
text: "Updated text"
)
)
// Delete an activity
let hardDelete = false // Soft delete sets deleted at but retains the data, hard delete fully removes it
try await feed.deleteActivity(id: "123", hardDelete: hardDelete)
// Batch delete activities
try await client.deleteActivities(
request: .init(
activityIds: ["123", "456"],
hardDelete: false
)
)
// Update an activity
val updatedActivity: Result<ActivityData> = feed.updateActivity(
id = "123",
request = UpdateActivityRequest(
custom = mapOf("custom" to "custom"),
text = "Updated text"
)
)
// Delete an activity
val hardDelete = false // Soft delete sets deleted at but retains the data, hard delete fully removes it
feed.deleteActivity(id = "123", hardDelete = hardDelete)
// Batch delete activities
client.deleteActivities(
request = DeleteActivitiesRequest(
ids = listOf("123", "456"),
hardDelete = false
)
)
// Update an activity
client.updateActivity({
id: "123",
text: "Updated text",
custom: {
color: "blue",
},
});
client.deleteActivity({
id: "123",
hard_delete: false, // Soft delete sets deleted at but retains the data, hard delete fully removes it
});
// Batch delete activities
client.deleteActivities({
id: ["123", "456"],
hard_delete: false,
});
// Update an activity
client.feeds.updateActivity({
id: "123",
text: "Updated text",
custom: {
color: "blue",
},
user_id: "<user id>",
});
client.feeds.deleteActivity({
id: "123",
hard_delete: false, // Soft delete sets deleted at but retains the data, hard delete fully removes it
user_id: "<user id>",
});
// Batch delete activities
client.feeds.deleteActivities({
id: ["123", "456"],
user_id: "<user id>",
hard_delete: false,
});
response, err := feedsClient.UpdateActivity(ctx, activityID, &getstream.UpdateActivityRequest{
Text: getstream.PtrTo("Updated activity text from Go SDK"),
UserID: &testUserID, // Required for server-side auth
Custom: map[string]interface{}{
"updated": true,
"update_time": time.Now().Unix(),
},
})
Map<String, Object> customData = new HashMap<>();
customData.put("updated", true);
customData.put("update_time", System.currentTimeMillis() / 1000);
UpdateActivityRequest updateRequest =
UpdateActivityRequest.builder()
.text("Updated activity text from Java SDK")
.userID(testUserId) // Required for server-side auth
.custom(customData)
.build();
UpdateActivityResponse response =
feeds.updateActivity(activityId, updateRequest).execute().getData();
$response = $this->feedsV3Client->updateActivity(
$activityId,
new GeneratedModels\UpdateActivityRequest(
text: 'Updated activity text from PHP SDK',
userID: $this->testUserId, // Required for server-side auth
custom: (object)[
'updated' => true,
'update_time' => time()
]
)
);
var response = await _feedsV3Client.UpdateActivityAsync(
activityId,
new UpdateActivityRequest
{
Text = "Updated activity text from .NET SDK",
UserID = _testUserId, // Required for server-side auth
Custom = new Dictionary<string, object>
{
["updated"] = true,
["update_time"] = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
}
}
);
response = self.client.feeds.update_activity(
activity_id,
text="Updated activity text from Python SDK",
user_id=self.test_user_id, # Required for server-side auth
custom={"updated": True, "update_time": int(datetime.now().timestamp())},
)