Skip to main content

server-service

Servers, membership, roles, invites, custom emoji, and channels. Owns the effective-permission resolver every other service calls into.

/api/v1/servers​

MethodPathWhat it does
GET/List servers the caller is in
POST/Create a server
POST/joinJoin by invite code
GET/invites/:codePreview an invite (name, icon, member count) before joining
GET/:serverId/invitesList a server's invites
POST/:serverId/invitesCreate an invite
DELETE/:serverId/invites/:codeRevoke an invite
PATCH/:serverIdUpdate server settings
DELETE/:serverIdDelete a server
POST/:serverId/leaveLeave a server
GET/:serverId/membersList members
POST/:serverId/membersAdd a member by username
PATCH/:serverId/members/:userIdChange role / permission overrides
DELETE/:serverId/members/:userIdRemove / kick a member
GET/:serverId/rolesList custom roles
POST/:serverId/rolesCreate a custom role
PATCH/:serverId/roles/:roleIdUpdate a custom role
DELETE/:serverId/roles/:roleIdDelete a custom role
GET/:serverId/emojiList custom emoji
POST/:serverId/emojiUpload a custom emoji
DELETE/:serverId/emoji/:emojiIdDelete a custom emoji
GET/:serverId/channelsList a server's channels, in sidebar order (position, then age); each carries categoryId and position
GET/:serverId/categoriesList a server's channel categories (membership)
POST/:serverId/categoriesCreate a category { name }, appended at the bottom (MANAGE_CHANNEL)
PATCH/:serverId/categories/:categoryIdRename a category (MANAGE_CHANNEL)
DELETE/:serverId/categories/:categoryIdDelete a category; its channels move to uncategorized, none is deleted (MANAGE_CHANNEL)
PUT/:serverId/channel-layoutReorder categories and move channels within/between them in one transaction (MANAGE_CHANNEL)
GET/:serverId/auditModeration audit trail: role/permission changes, removals, role create/update/delete, server settings (MANAGE_SERVER)

/api/v1/channels​

MethodPathWhat it does
GET/List a channel (query-scoped)
POST/Create a channel
PATCH/:channelIdUpdate a channel (name, topic, privacy)
DELETE/:channelIdDelete a channel
GET/:channelId/membersList a private channel's allowlist
PUT/:channelId/membersReplace a private channel's allowlist

PUT /:serverId/channel-layout takes { categoryIds?: string[], channels?: { id, categoryId | null }[] } and answers with { categories, channels } as the caller sees them. Both lists are "in the order they should be drawn"; a channel's position is its index among the entries sharing its category. Anything the request leaves out keeps its category and follows what it names, which is what lets a manager who cannot see a private channel still send a layout. Every id is checked against the server, and a channel must also be one the caller can see - MANAGE_CHANNEL does not open a private channel. Errors use the standard shape: CATEGORY_NOT_FOUND (404), CHANNEL_NOT_FOUND (404), DUPLICATE_LAYOUT_ENTRY (400), MISSING_PERMISSION (403). The layout is sent whole, so its bounds sit far past any real server: 2,000 categories and 10,000 channels per request, inside a 2 MB JSON body (server-service raises Express's 100 kB default through bootstrapService({ jsonBodyLimit })). There is no cap on how many channels or categories a server holds, and channel names may repeat. Every change is written to the server audit trail (category.created, category.updated, category.deleted, channels.reordered) and publishes channel.list.changed.

An invite is previewed (GET /invites/:code) before it's accepted — the preview is deliberately thin (name, icon, member count, online count from presence-service), because anyone holding a code can ask for it.