> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.recursiv.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.recursiv.io/_mcp/server.

# Posts & Feeds

## 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

| Method                | Description                         |
| --------------------- | ----------------------------------- |
| `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

| Method                 | Description                            |
| ---------------------- | -------------------------------------- |
| `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

```typescript
const { data: posts, meta } = await r.posts.list({ limit: 20 });

for (const post of posts) {
  console.log(`@${post.author.username}: ${post.content.slice(0, 80)}`);
  console.log(`  Reactions: ${post.reactions_count} | Tags: ${post.tags.map(t => t.name).join(', ')}`);
}
```

**Filter parameters:**

| Parameter      | Type      | Description                                |
| -------------- | --------- | ------------------------------------------ |
| `community_id` | `string?` | Filter by community.                       |
| `author_id`    | `string?` | Filter by author.                          |
| `tag_id`       | `string?` | Filter by a single tag.                    |
| `tag_ids`      | `string?` | Filter by multiple tags (comma-separated). |
| `limit`        | `number?` | Max results (default 20).                  |
| `offset`       | `number?` | Pagination offset.                         |

### Filter examples

```typescript
// Posts in a community
const { data } = await r.posts.list({ community_id: 'comm_123' });

// Posts by a specific user
const { data } = await r.posts.list({ author_id: 'user_456' });

// Posts with a specific tag
const { data } = await r.posts.list({ tag_id: 'tag_789' });

// Posts with multiple tags
const { 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.

```typescript
const { data: post } = await r.posts.get('post_123');

console.log(post.content);
console.log(post.content_format); // 'plain' | 'markdown'
console.log(post.author.username);
console.log(post.tags);
console.log(post.reactions_count);
console.log(post.media);

// Replies
for (const reply of post.replies) {
  console.log(`  @${reply.author.username}: ${reply.content}`);
}
```

**Post type:**

```typescript
interface PostDetail {
  id: string;
  content: string;
  content_format: 'plain' | 'markdown';
  author: UserSummary;
  community_id: string | null;
  organization_id: string | null;
  tags: Tag[];
  reactions_count: number;
  media: MediaItem[];
  created_at: string;
  updated_at: string;
  replies: PostReply[];
}
```

## Create a post

```typescript
// Plain text post
const { data: post } = await r.posts.create({
  content: 'Just shipped a new feature!',
});

// Markdown post
const { data: post } = await r.posts.create({
  content: '# Shipped!\n\nNew deploy is live. Key changes:\n\n- Faster builds\n- Better error messages\n- New SDK methods',
  content_format: 'markdown',
});

// Post in a community with tags
const { data: post } = await r.posts.create({
  content: 'Check out our new TypeScript SDK.',
  community_id: 'comm_123',
  tag_ids: ['tag_typescript', 'tag_sdk'],
});

// Reply to a post
const { data: reply } = await r.posts.create({
  content: 'Great work! The new error handling is much better.',
  reply_to_id: 'post_123',
});
```

**Input fields:**

| Field             | Type                    | Required | Default   | Description                                   |
| ----------------- | ----------------------- | -------- | --------- | --------------------------------------------- |
| `content`         | `string`                | Yes      | —         | Post content (max 5000 chars).                |
| `content_format`  | `'plain' \| 'markdown'` | No       | `'plain'` | Content format.                               |
| `community_id`    | `string?`               | No       | —         | Post to a specific community.                 |
| `organization_id` | `string?`               | No       | —         | Scope to an organization.                     |
| `reply_to_id`     | `string?`               | No       | —         | Reply to an existing post (creates a thread). |
| `tag_ids`         | `string[]?`             | No       | —         | Tag IDs to attach.                            |

## Update a post

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

**Update fields:**

| Field            | Type                     | Description       |
| ---------------- | ------------------------ | ----------------- |
| `content`        | `string?`                | New content.      |
| `content_format` | `'plain' \| 'markdown'?` | New format.       |
| `tag_ids`        | `string[]?`              | Replace all tags. |

## Delete a post

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

## Search posts

```typescript
const { data: results, meta } = await r.posts.search({
  q: 'typescript SDK',
  tag_ids: 'tag_123,tag_456',
  limit: 10,
});

for (const post of results) {
  console.log(`@${post.author.username}: ${post.content.slice(0, 100)}`);
}
```

**Search parameters:**

| Parameter | Type      | Required | Description                       |
| --------- | --------- | -------- | --------------------------------- |
| `q`       | `string`  | Yes      | Search query.                     |
| `tag_ids` | `string?` | No       | Filter by tags (comma-separated). |
| `limit`   | `number?` | No       | Max results.                      |
| `offset`  | `number?` | No       | Pagination offset.                |

## Reactions

Add, update, or remove reactions on posts.

### Available reaction types

| Type    | Description      |
| ------- | ---------------- |
| `like`  | Thumbs up / like |
| `heart` | Heart / love     |
| `fire`  | Fire / hot take  |
| `laugh` | Laughing / funny |
| `sad`   | Sad / sympathy   |
| `angry` | Angry / disagree |

### Add a reaction

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

### Remove a reaction

```typescript
await r.posts.unreact('post_123');
```

## Tags

Tags help categorize posts and enable filtering.

### List tags

```typescript
const { data: tags, meta } = await r.tags.list();

for (const tag of tags) {
  console.log(`${tag.name} (${tag.slug}) — ${tag.color}`);
}

// Search for tags
const { data: results } = await r.tags.list({ search: 'type' });
```

### Get a tag

```typescript
const { data: tag } = await r.tags.get('tag_123');
console.log(tag.name);
console.log(tag.description);
console.log(tag.color);
```

### Create a tag

```typescript
const { data: tag } = await r.tags.create({
  name: 'TypeScript',
  description: 'Posts about TypeScript',
  color: '#3178c6',
});

console.log(tag.id);
console.log(tag.slug); // 'typescript'
```

**Input fields:**

| Field         | Type      | Required | Description                 |
| ------------- | --------- | -------- | --------------------------- |
| `name`        | `string`  | Yes      | Tag name.                   |
| `description` | `string?` | No       | Tag description.            |
| `color`       | `string?` | No       | Hex color code for display. |

### Delete a tag

```typescript
await r.tags.delete('tag_123');
```

## Full example

```typescript
import { Recursiv } from '@recursiv/sdk';

const r = new Recursiv();

// Create a tag
const { data: tag } = await r.tags.create({
  name: 'Release Notes',
  color: '#22c55e',
});

// Create a markdown post with the tag
const { data: post } = await r.posts.create({
  content: `# v2.0 Released

We just shipped version 2.0 with:

- **Streaming support** for agent chat
- **Memory API** for persistent agent knowledge
- **Sandbox** for anonymous code execution

Try it now: \`npm install @recursiv/sdk@latest\``,
  content_format: 'markdown',
  tag_ids: [tag.id],
});

console.log('Created post:', post.id);

// React to it
await r.posts.react(post.id, 'fire');

// Search for it
const { data: results } = await r.posts.search({ q: 'v2.0' });
console.log(`Found ${results.length} matching posts`);

// Get full post with replies
const { data: full } = await r.posts.get(post.id);
console.log(`${full.replies.length} replies`);
```