Skip to Content
Boards, threads & posts

Boards, threads & posts

The forum is a four-level hierarchy: board → thread → post → reply. Articles sit alongside it as a separate content type (see Articles & paywalls).

Boards

A board is a topic category with a name, title, description, color, icon, and two point multipliers (fudPointsMultiplier, votePointsMultiplier).

EndpointMethodAuthNotes
/api/boardsGET—Active boards, pinned first. Supports ?search=.
/api/boards/name/:nameGET—Lookup by exact name (case-insensitive) or slug.
/api/boardsPOSTAdminCreates a board.

Board creation validates that name is 2–50 chars of letters, numbers, spaces, hyphens, and underscores, and rejects case-insensitive duplicates. Multipliers are clamped to 0.1–5.0 (default 1.0). Base64 images are uploaded to Cloudinary when configured, with a base64 fallback.

POST /api/boards uses verifyAdmin, which — beyond the x-wallet-address header — requires a valid x-signature and x-message (see API reference).

Threads

A thread is a discussion inside a board.

GET /api/threads # list, approved only GET /api/threads/board/:boardId # threads for a board GET /api/threads/:threadId # single thread POST /api/threads # create DELETE /api/threads/:threadId # owner or admin

Listing parameters (GET /api/threads): page (default 1), limit (default 1000), sort (hot | new | top), search. Only status = 'approved' threads are public. Responses include a pagination object (page, limit, total, pages, hasMore).

Create body: { title, content, userId, boardId, image? }. userId may be a uuid, a legacy ObjectId, or a wallet address and is resolved via the identity helper.

Moderation: pending → approved

Threads created by regular users are stored with status = 'pending' and are hidden from every public listing and from search until an admin approves them. Admin-created threads are auto-approved.

Other create-time guards:

  • Duplicate detection — the same (title, userId, boardId) returns 409.
  • Timeouts — if user.isTimedOut and timeoutRestrictions.canCreateThreads is false, returns 403 with the reason and expiry. Expired timeouts are auto-cleared on the next attempt.
  • Length — thread content is sanitized; board name characters are validated.

Posts

A post is a message inside a thread. It carries the FUD scoring fields (fudScore, fudPoints, pointsStatus, …).

GET /api/posts # global feed GET /api/posts/board/:boardId # board feed GET /api/posts/thread/:threadId # thread feed GET /api/posts/top # trending GET /api/posts/:postId # single post POST /api/posts # create post or reply

Feed parameters: page, limit (default 50), boardId, userId (injects the viewer’s userVote), cursor (created-at cursor; disables page offset), sort (random supported on the global feed). Responses include pagination.nextCursor for keyset pagination.

GET /api/posts/top accepts period (day | week | month | all) and sort (fud | votes | hot | new | random).

POST /api/posts is polymorphic. The body decides what gets created:

BodyCreates
{ content, userId, threadId }A top-level post in the thread.
{ content, userId, threadId, parentPostId }A reply to parentPostId.
{ content, userId, articleId }A comment on an article.

Posts are capped at 5000 characters. Exact-duplicate content by the same user in the same thread/post returns 409. Counters (thread.postCount, board.postCount, post.replyCount, thread.replyCount) are updated atomically; the thread update cascades to the board exactly once to avoid double counting.

Replies

GET /api/replies?threadId=...|postId=...|articleId=... GET /api/replies/post/:postId DELETE /api/replies/:replyId

GET /api/replies requires at least one filter (threadId, postId, or articleId) unless countsOnly=true. Pass x-wallet-address to get each reply’s userVote. countsOnly=true returns a { targetId: count } map.

Deletion & moderation

FudFi uses soft deletes: deleting a post, reply, or thread sets isDeleted = true (posts/replies) rather than removing rows, and vote rows are cleaned up as needed. DELETE handlers are guarded by verifyOwnershipOrAdmin — the author or an admin only.

Admins can additionally pin boards and posts globally:

POST /api/admin/pin-board POST /api/admin/unpin-board POST /api/admin/pin-post POST /api/admin/unpin-post