Skip to content

Threads

Endpoints for listing and retrieving forum threads with filtering, sorting, and pagination.

List Threads

Get threads with filtering and pagination.

GET /api/threads

Query Parameters

ParameterTypeDefaultDescription
serverIdstring-Filter by server (recommended)
channelIdstring-Filter by channel
tagstring-Filter by tag name
statusenumallopen, resolved, locked, all
sortenumlatestSee sorting options below
limitnumber20Results per page (1-100)
cursorstring-Pagination cursor

Sorting Options

ValueDescription
latestNewest threads first (by creation date)
oldestOldest threads first
popularMost messages first
recently_activeMost recent activity first
unansweredZero 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

FieldTypeDescription
idstringThread ID
titlestringThread title
slugstringURL-friendly slug
previewstringFirst 200 chars of first message
statusstringopen, resolved, locked, archived
messageCountnumberTotal messages in thread
authorobjectThread creator
tagsstring[]Applied tag names
channelIdstringForum channel ID
channelNamestringForum channel name
createdAtstringCreation timestamp
lastActivityAtstringLast message timestamp

Examples

Terminal window
curl "http://localhost:3000/api/threads?serverId=123456789"

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/:threadId

Parameters

ParameterTypeDescription
threadIdstringThread 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

FieldTypeDescription
idstringMessage ID
contentstringRaw message content (Discord markdown)
contentHtmlstringProcessed HTML content
authorobjectMessage author
attachmentsarrayFile attachments
reactionsarrayReactions on message
embedsarrayEmbedded content
createdAtstringMessage timestamp
editedAtstring | nullEdit 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/participants

Parameters

ParameterTypeDescription
threadIdstringThread 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>
);
}