Skip to content

Posts, comments, and feed

GET /api/v1/community/posts?limit=20&scope=latest

Supported query parameters:

ParameterValuesNotes
limit1–50Defaults to 20.
cursoropaqueSend back exactly as returned.
scopeall, latest, recommended, following, mine, bookmarkedThe last three require a session.
stateactive, archived, allArchived state is only available with scope=mine.
q1–100 charactersSearches title and body.
tagnormalized tagFilters visible posts by tag.
seed0–2147483647Recommendation seed; required when continuing a recommended cursor.
refresh1Starts a fresh recommended feed; cannot be combined with cursor.

Response:

{ "posts": [], "nextCursor": null }

scope=recommended can also return seed. Keep that seed with the returned cursor. A cursor is opaque and may change format between releases.

GET /api/v1/community/posts/{postId}
GET /api/v1/community/posts/{postId}?commentsOnly=true
GET /api/v1/community/posts/{postId}?includeComments=false

By default, the response contains post, comments, commentsNextCursor, commentsSort, and viewer. Comments are sorted by hot by default or latest when commentsSort=latest; use commentsCursor to continue. commentId can focus a visible comment into the response.

Create a post:

POST /api/v1/community/posts
Content-Type: application/json
{
"title": "Optional title",
"body": "Post text",
"visibility": "public",
"tags": ["release-notes"],
"attachmentIds": ["attachment-id-returned-by-upload"]
}

title defaults to a derived first line. body is required and is limited to 20,000 characters. visibility is public, protected, or private; tags are normalized and capped at 10; a post can include at most 16 unique attachment IDs, and each attachment must already be ready and allowed. New posts are rate-limited to 30 per hour and 200 per day per author.

Edit a post with optimistic concurrency:

PATCH /api/v1/community/posts/{postId}
Content-Type: application/json
{
"title": "Updated title",
"body": "Updated body",
"visibility": "protected",
"tags": [],
"version": 1,
"editReason": "Clarify the release reference"
}

Send body and the current version on every post edit. You can also send title, visibility, tags, and optional editReason. Post edits do not change attachments; use the attachment link route for that. A stale version returns 409 version_conflict.

Delete:

DELETE /api/v1/community/posts/{postId}
Content-Type: application/json
{ "version": 2 }

Successful deletion is 204.

POST /api/v1/community/posts/{postId}/comments
Content-Type: application/json
{ "body": "A reply", "parentId": "parent-comment-id-from-the-post" }

parentId is optional and must identify a comment on the same post. Comment bodies are limited to 5,000 characters; comment quotas are 300 per hour and 2,000 per day per author.

Toggle post or comment reactions:

PUT /api/v1/community/posts/{postId}/reaction
{ "active": true }
PUT /api/v1/community/comments/{commentId}/reaction
{ "active": false }

Post reaction responses include { "active": true, "likeCount": 3 }; bookmark responses include { "active": true }; comment reaction responses include the updated likeCount.

Pin, archive, and restore require the current post version:

PUT /api/v1/community/posts/{postId}/pin
{ "active": true, "version": 2 }
POST /api/v1/community/posts/{postId}/archive
{ "version": 3 }

These return { "post": ... } with the updated post.

PUT /api/v1/community/posts/{postId}/feedback
{ "feedback": "not_interested", "reasonCode": "too_repetitive" }
DELETE /api/v1/community/me/post-feedback

Feedback is not_interested, hide, or null; reasonCode is optional when setting a value and must be absent/null when clearing it.

Create a report:

POST /api/v1/community/reports
{
"targetKind": "post",
"targetId": "post-id-from-the-service",
"reasonCode": "spam",
"details": "Optional detail"
}

targetKind is post, comment, or user. Report reasons are spam, harassment, hate, sexual, violence, privacy, copyright, misinformation, or other; details is required for other.