Threads
Endpoints for listing and retrieving forum threads with filtering, sorting, and pagination.
List Threads
Get threads with filtering and pagination.
GET /api/threadsQuery Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
serverId | string | - | Filter by server (recommended) |
channelId | string | - | Filter by channel |
tag | string | - | Filter by tag name |
status | enum | all | open, resolved, locked, all |
sort | enum | latest | See sorting options below |
limit | number | 20 | Results per page (1-100) |
cursor | string | - | Pagination cursor |
Sorting Options
| Value | Description |
|---|---|
latest | Newest threads first (by creation date) |
oldest | Oldest threads first |
popular | Most messages first |
recently_active | Most recent activity first |
unanswered | Zero replies, oldest first |
Response
{ "threads": [ { "id": "thread001", "title": "How do I implement OAuth?", "slug": "how-do-i-implement-oauth", "preview": "I'm trying to add Discord OAuth to my app but keep getting redirect errors...", "status": "resolved", "messageCount": 12, "author": { "id": "user123", "username": "curious_dev", "avatar": "abc123" }, "tags": ["oauth", "solved"], "channelId": "456789012", "channelName": "help-forum", "createdAt": "2024-01-15T10:30:00.000Z", "lastActivityAt": "2024-01-15T14:20:00.000Z" } ], "nextCursor": "eyJpZCI6InRocmVhZDAwMiJ9", "hasMore": true}Response Fields
| Field | Type | Description |
|---|---|---|
id | string | Thread ID |
title | string | Thread title |
slug | string | URL-friendly slug |
preview | string | First 200 chars of first message |
status | string | open, resolved, locked, archived |
messageCount | number | Total messages in thread |
author | object | Thread creator |
tags | string[] | Applied tag names |
channelId | string | Forum channel ID |
channelName | string | Forum channel name |
createdAt | string | Creation timestamp |
lastActivityAt | string | Last message timestamp |
Examples
curl "http://localhost:3000/api/threads?serverId=123456789"curl "http://localhost:3000/api/threads?serverId=123456789&status=resolved"curl "http://localhost:3000/api/threads?serverId=123456789&tag=bug"curl "http://localhost:3000/api/threads?serverId=123456789&sort=unanswered"Pagination
Use cursor-based pagination for efficient traversal:
async function getAllThreads(serverId) { const threads = []; let cursor = null;
do { const params = new URLSearchParams({ serverId, limit: '100' }); if (cursor) params.set('cursor', cursor);
const response = await fetch(`/api/threads?${params}`); const data = await response.json();
threads.push(...data.threads); cursor = data.hasMore ? data.nextCursor : null; } while (cursor);
return threads;}Get Thread
Get full thread details including all messages.
GET /api/threads/:threadIdParameters
| Parameter | Type | Description |
|---|---|---|
threadId | string | Thread ID |
Response
{ "id": "thread001", "title": "How do I implement OAuth?", "slug": "how-do-i-implement-oauth", "status": "resolved", "author": { "id": "user123", "username": "curious_dev", "avatar": "abc123", "isBot": false }, "tags": ["oauth", "solved"], "channelId": "456789012", "channelName": "help-forum", "messages": [ { "id": "msg001", "content": "I'm trying to add Discord OAuth to my app but keep getting redirect errors. Here's my code:\n\n```javascript\nconst authUrl = 'https://discord.com/oauth2/authorize';\n```\n\nAny ideas?", "contentHtml": "<p>I'm trying to add Discord OAuth to my app but keep getting redirect errors. Here's my code:</p>\n<pre><code class=\"language-javascript\">const authUrl = 'https://discord.com/oauth2/authorize';</code></pre>\n<p>Any ideas?</p>", "author": { "id": "user123", "username": "curious_dev", "avatar": "abc123", "isBot": false }, "attachments": [], "reactions": [ { "emoji": "eyes", "count": 2 } ], "embeds": [], "createdAt": "2024-01-15T10:30:00.000Z", "editedAt": null }, { "id": "msg002", "content": "Make sure your redirect URL matches exactly what you set in the Developer Portal, including the trailing slash!", "contentHtml": "<p>Make sure your redirect URL matches exactly what you set in the Developer Portal, including the trailing slash!</p>", "author": { "id": "user456", "username": "helpful_mod", "avatar": "def456", "isBot": false }, "attachments": [], "reactions": [ { "emoji": "white_check_mark", "count": 5 } ], "embeds": [], "createdAt": "2024-01-15T10:45:00.000Z", "editedAt": null } ], "messageCount": 12, "createdAt": "2024-01-15T10:30:00.000Z", "archivedAt": null, "lockedAt": null}Message Fields
| Field | Type | Description |
|---|---|---|
id | string | Message ID |
content | string | Raw message content (Discord markdown) |
contentHtml | string | Processed HTML content |
author | object | Message author |
attachments | array | File attachments |
reactions | array | Reactions on message |
embeds | array | Embedded content |
createdAt | string | Message timestamp |
editedAt | string | null | Edit timestamp |
Attachment Format
{ "id": "att001", "filename": "screenshot.png", "url": "https://cdn.discordapp.com/attachments/...", "contentType": "image/png", "size": 125000, "width": 1920, "height": 1080}Embed Format
{ "type": "rich", "title": "GitHub Issue #123", "description": "Fix authentication bug", "url": "https://github.com/...", "color": 5814783, "thumbnail": { "url": "..." }, "author": { "name": "...", "url": "..." }}Get Thread Participants
Get all users who participated in a thread.
GET /api/threads/:threadId/participantsParameters
| Parameter | Type | Description |
|---|---|---|
threadId | string | Thread ID |
Response
{ "total": 8, "humans": 7, "bots": 1, "participants": [ { "userId": "user123", "username": "curious_dev", "avatar": "abc123", "isBot": false, "messageCount": 5, "firstMessageAt": "2024-01-15T10:30:00.000Z", "lastMessageAt": "2024-01-15T14:20:00.000Z" }, { "userId": "user456", "username": "helpful_mod", "avatar": "def456", "isBot": false, "messageCount": 3, "firstMessageAt": "2024-01-15T10:45:00.000Z", "lastMessageAt": "2024-01-15T12:00:00.000Z" } ]}Common Patterns
Thread List with Status Badges
function ThreadList({ threads }) { return ( <ul className="thread-list"> {threads.map(thread => ( <li key={thread.id}> <a href={`/threads/${thread.slug}`}> <StatusBadge status={thread.status} /> <h3>{thread.title}</h3> <p>{thread.preview}</p> <div className="meta"> <span>{thread.messageCount} replies</span> <span>{formatDate(thread.lastActivityAt)}</span> </div> </a> </li> ))} </ul> );}
function StatusBadge({ status }) { const colors = { open: 'blue', resolved: 'green', locked: 'gray', archived: 'gray' }; return <span className={`badge ${colors[status]}`}>{status}</span>;}Thread Detail Page
function ThreadPage({ thread }) { return ( <article> <header> <h1>{thread.title}</h1> <div className="tags"> {thread.tags.map(tag => ( <span key={tag} className="tag">{tag}</span> ))} </div> </header>
<div className="messages"> {thread.messages.map(message => ( <Message key={message.id} message={message} /> ))} </div> </article> );}
function Message({ message }) { return ( <div className="message"> <Avatar user={message.author} /> <div className="content"> <div className="header"> <span className="author">{message.author.username}</span> <time>{formatDate(message.createdAt)}</time> </div> <div className="body" dangerouslySetInnerHTML={{ __html: message.contentHtml }} /> <Reactions reactions={message.reactions} /> </div> </div> );}Filter UI
function ThreadFilters({ currentFilters, onFilterChange }) { return ( <div className="filters"> <select value={currentFilters.status} onChange={e => onFilterChange({ status: e.target.value })} > <option value="all">All Threads</option> <option value="open">Open</option> <option value="resolved">Resolved</option> <option value="locked">Locked</option> </select>
<select value={currentFilters.sort} onChange={e => onFilterChange({ sort: e.target.value })} > <option value="latest">Newest</option> <option value="recently_active">Recently Active</option> <option value="popular">Most Popular</option> <option value="unanswered">Unanswered</option> </select> </div> );}