meta { name: Get Topic type: http seq: 1 } get { url: {{appview_url}}/api/topics/{{topic_id}} } params:path { topic_id: 1 } params:query { ~offset: 0 ~limit: 25 } assert { res.status: eq 200 res.body.topicId: isDefined res.body.post: isDefined res.body.replies: isArray res.body.locked: isDefined res.body.pinned: isDefined res.body.total: isDefined res.body.offset: isDefined res.body.limit: isDefined } docs { Get a topic (thread starter post) with paginated replies. Path params: - id: Topic post ID (bigint as string) Query params: - offset: Number of replies to skip (optional, default 0, clamped to >= 0) - limit: Max replies to return (optional, default 25, max 250) Returns: { "topicId": "1", "locked": false, "pinned": false, "total": 42, "offset": 0, "limit": 25, "post": { "id": "1", "did": "did:plc:...", "rkey": "...", "title": "Topic title", "text": "Topic text", "forumUri": "at://did:plc:.../space.atbb.forum.forum/self", "createdAt": "2024-01-01T00:00:00.000Z", "author": { "did": "did:plc:...", "handle": "user.bsky.social" } }, "replies": [ { "id": "2", "did": "did:plc:...", "rkey": "...", "title": null, "text": "Reply text", "parentPostId": "1", "createdAt": "2024-01-01T00:00:00.000Z", "author": { "did": "did:plc:...", "handle": "user.bsky.social" } } ] } Pagination notes: - total is the reply count after SQL-level moderation (bannedByMod=false), but before in-memory filters (active user bans, hidden posts). Treat as an upper bound. - To page through replies: increment offset by limit on each Load More click - For bookmark/deep-link support: request offset=0 and limit=(desired_offset + page_size) Moderation enforcement (fail-open: errors in mod lookups show all content): - Replies from banned users are excluded from the current page - Replies hidden by mod "delete" action are excluded (restored by "undelete") - locked/pinned reflect most recent lock/pin action per topic Error codes: - 400: Invalid topic ID format - 404: Topic not found - 500: Server error }