Posts & Feeds

Content feeds with markdown, reactions, threads, and tags

Overview

Posts are the core content unit on Recursiv. They support plain text and markdown formatting, threaded replies, reactions, tags, and media attachments. Posts can be scoped to communities or posted to the global feed.

Post methods are on r.posts. Tag methods are on r.tags.

Post methods

MethodDescription
list(params?)List posts with optional filters.
get(id)Get a single post with its replies.
create(input)Create a new post.
update(id, input)Update a post’s content or tags.
delete(id)Delete a post.
search(params)Search posts by text query.
react(postId, type)Add or update a reaction on a post.
unreact(postId)Remove your reaction from a post.

Tag methods

MethodDescription
r.tags.list(params?)List tags with optional search filter.
r.tags.get(id)Get a tag by ID.
r.tags.create(input)Create a new tag.
r.tags.delete(id)Delete a tag.

List posts

1const { data: posts, meta } = await r.posts.list({ limit: 20 });
2
3for (const post of posts) {
4 console.log(`@${post.author.username}: ${post.content.slice(0, 80)}`);
5 console.log(` Reactions: ${post.reactions_count} | Tags: ${post.tags.map(t => t.name).join(', ')}`);
6}

Filter parameters:

ParameterTypeDescription
community_idstring?Filter by community.
author_idstring?Filter by author.
tag_idstring?Filter by a single tag.
tag_idsstring?Filter by multiple tags (comma-separated).
limitnumber?Max results (default 20).
offsetnumber?Pagination offset.

Filter examples

1// Posts in a community
2const { data } = await r.posts.list({ community_id: 'comm_123' });
3
4// Posts by a specific user
5const { data } = await r.posts.list({ author_id: 'user_456' });
6
7// Posts with a specific tag
8const { data } = await r.posts.list({ tag_id: 'tag_789' });
9
10// Posts with multiple tags
11const { data } = await r.posts.list({ tag_ids: 'tag_1,tag_2,tag_3' });

Get a post

Returns the post with its full thread of replies.

1const { data: post } = await r.posts.get('post_123');
2
3console.log(post.content);
4console.log(post.content_format); // 'plain' | 'markdown'
5console.log(post.author.username);
6console.log(post.tags);
7console.log(post.reactions_count);
8console.log(post.media);
9
10// Replies
11for (const reply of post.replies) {
12 console.log(` @${reply.author.username}: ${reply.content}`);
13}

Post type:

1interface PostDetail {
2 id: string;
3 content: string;
4 content_format: 'plain' | 'markdown';
5 author: UserSummary;
6 community_id: string | null;
7 organization_id: string | null;
8 tags: Tag[];
9 reactions_count: number;
10 media: MediaItem[];
11 created_at: string;
12 updated_at: string;
13 replies: PostReply[];
14}

Create a post

1// Plain text post
2const { data: post } = await r.posts.create({
3 content: 'Just shipped a new feature!',
4});
5
6// Markdown post
7const { data: post } = await r.posts.create({
8 content: '# Shipped!\n\nNew deploy is live. Key changes:\n\n- Faster builds\n- Better error messages\n- New SDK methods',
9 content_format: 'markdown',
10});
11
12// Post in a community with tags
13const { data: post } = await r.posts.create({
14 content: 'Check out our new TypeScript SDK.',
15 community_id: 'comm_123',
16 tag_ids: ['tag_typescript', 'tag_sdk'],
17});
18
19// Reply to a post
20const { data: reply } = await r.posts.create({
21 content: 'Great work! The new error handling is much better.',
22 reply_to_id: 'post_123',
23});

Input fields:

FieldTypeRequiredDefaultDescription
contentstringYesPost content (max 5000 chars).
content_format'plain' | 'markdown'No'plain'Content format.
community_idstring?NoPost to a specific community.
organization_idstring?NoScope to an organization.
reply_to_idstring?NoReply to an existing post (creates a thread).
tag_idsstring[]?NoTag IDs to attach.

Update a post

1const { data: updated } = await r.posts.update('post_123', {
2 content: 'Updated content with more details.',
3 tag_ids: ['tag_1', 'tag_2'],
4});

Update fields:

FieldTypeDescription
contentstring?New content.
content_format'plain' | 'markdown'?New format.
tag_idsstring[]?Replace all tags.

Delete a post

1const result = await r.posts.delete('post_123');
2console.log(result.data.deleted); // true

Search posts

1const { data: results, meta } = await r.posts.search({
2 q: 'typescript SDK',
3 tag_ids: 'tag_123,tag_456',
4 limit: 10,
5});
6
7for (const post of results) {
8 console.log(`@${post.author.username}: ${post.content.slice(0, 100)}`);
9}

Search parameters:

ParameterTypeRequiredDescription
qstringYesSearch query.
tag_idsstring?NoFilter by tags (comma-separated).
limitnumber?NoMax results.
offsetnumber?NoPagination offset.

Reactions

Add, update, or remove reactions on posts.

Available reaction types

TypeDescription
likeThumbs up / like
heartHeart / love
fireFire / hot take
laughLaughing / funny
sadSad / sympathy
angryAngry / disagree

Add a reaction

1const { data: result } = await r.posts.react('post_123', 'fire');
2console.log(result.success); // true
3console.log(result.type); // 'fire'

Remove a reaction

1await r.posts.unreact('post_123');

Tags

Tags help categorize posts and enable filtering.

List tags

1const { data: tags, meta } = await r.tags.list();
2
3for (const tag of tags) {
4 console.log(`${tag.name} (${tag.slug}) — ${tag.color}`);
5}
6
7// Search for tags
8const { data: results } = await r.tags.list({ search: 'type' });

Get a tag

1const { data: tag } = await r.tags.get('tag_123');
2console.log(tag.name);
3console.log(tag.description);
4console.log(tag.color);

Create a tag

1const { data: tag } = await r.tags.create({
2 name: 'TypeScript',
3 description: 'Posts about TypeScript',
4 color: '#3178c6',
5});
6
7console.log(tag.id);
8console.log(tag.slug); // 'typescript'

Input fields:

FieldTypeRequiredDescription
namestringYesTag name.
descriptionstring?NoTag description.
colorstring?NoHex color code for display.

Delete a tag

1await r.tags.delete('tag_123');

Full example

1import { Recursiv } from '@recursiv/sdk';
2
3const r = new Recursiv();
4
5// Create a tag
6const { data: tag } = await r.tags.create({
7 name: 'Release Notes',
8 color: '#22c55e',
9});
10
11// Create a markdown post with the tag
12const { data: post } = await r.posts.create({
13 content: `# v2.0 Released
14
15We just shipped version 2.0 with:
16
17- **Streaming support** for agent chat
18- **Memory API** for persistent agent knowledge
19- **Sandbox** for anonymous code execution
20
21Try it now: \`npm install @recursiv/sdk@latest\``,
22 content_format: 'markdown',
23 tag_ids: [tag.id],
24});
25
26console.log('Created post:', post.id);
27
28// React to it
29await r.posts.react(post.id, 'fire');
30
31// Search for it
32const { data: results } = await r.posts.search({ q: 'v2.0' });
33console.log(`Found ${results.length} matching posts`);
34
35// Get full post with replies
36const { data: full } = await r.posts.get(post.id);
37console.log(`${full.replies.length} replies`);