Skip to content

Servers and Channels

Partly out of date

Rendered from ProjectInfo/client/servers-channels.md on docs/encryption-modes-spec. Its protocol content is still useful, but parts of it describe an older client or an earlier API. Where it disagrees with the code, the code wins — see the spec errata.

Servers

List joined servers

GET /api/servers
→ Server[]

Create a server

POST /api/servers
{ "name": "My Server", "description": "optional" }
→ 201 Server

The creating user becomes owner and first member.

Fetch one server

GET /api/servers/{serverID}
→ Server

Update server settings

PATCH /api/servers/{serverID}
{
  "name": "string",
  "description": "string",
  "icon_url": "/media/attachments/{id}",
  "banner_url": "/media/attachments/{id}",
  "verification_level": 0,
  "explicit_filter": 0,
  "afk_timeout_seconds": 300,
  "system_channel_id": "uuid",
  "rules_channel_id": "uuid",
  "afk_channel_id": "uuid"
}
→ 200 Server
Requires PermAdministrator.

Leave a server

DELETE /api/servers/{serverID}/members/@me
→ 204
Owner cannot leave — must delete the server or transfer ownership.

Delete a server

DELETE /api/servers/{serverID}
→ 204
Owner only. Permanent.


Channels

List channels in a server

GET /api/servers/{serverID}/channels
→ Channel[]

Sort by position ascending. Group by parent_id (category). Only show channels where PermViewChannel is satisfied.

Create a channel

POST /api/servers/{serverID}/channels
{
  "name": "general",
  "type": "text",            // text | voice | announcement | stage | category
  "topic": "optional",
  "position": 0,
  "nsfw": false,
  "parent_id": "uuid",       // optional category
  "slowmode_seconds": 0,
  "bitrate": 64000,          // voice channels
  "user_limit": 0,           // voice; 0 = unlimited
  "encryption_mode": "server_encrypted"   // see below
}
→ 201 Channel

Requires PermManageChannels. encryption_mode cannot be changed after creation.

Encryption Modes

Value Server can read History on join Requires key setup
server_encrypted Yes (disclosed) Full history No
rolling_key No None Yes — create initial epoch immediately
peer_provided No None Yes — deliver key P2P

UI disclosure requirement: When creating a rolling_key or peer_provided channel, require the user to read and acknowledge that reporting encrypted messages will share the decryption key.

Voice channels always use server_encrypted regardless of the value passed.

Fetch a single channel

GET /api/channels/{channelID}
→ Channel

Edit a channel

PATCH /api/channels/{channelID}
{
  "name": "new-name",
  "topic": "string",
  "position": 1,
  "nsfw": false,
  "slowmode_seconds": 10,
  "parent_id": "uuid"
}
→ 200 Channel
Requires PermManageChannels. encryption_mode cannot be changed.

Delete a channel

DELETE /api/channels/{channelID}
→ 204
Requires PermManageChannels.


Channel Read State

Track unread messages per channel.

Fetch read state

GET /api/channels/{channelID}/read-state
→ { channel_id, user_id, last_read_id, mention_count, updated_at }

Returns a zero-value struct if no row exists (not an error).

Acknowledge messages

PUT /api/channels/{channelID}/read-state
{ "last_read_id": "uuid" }
→ 200 ReadState

Call when the user views a channel and reads the most recent message. The server updates mention_count to reflect unread @mentions since last_read_id.


Permission Resolution

Permissions are a bitfield. The server always re-validates, but the client must resolve permissions for UI rendering (show/hide channels, enable/disable inputs).

Algorithm

1. Start with @everyone role permissions
   Note: @everyone role has id === server_id

2. OR in permissions from all roles the user holds

3. If Administrator bit is set → grant everything; stop

4. Apply channel permission overwrites in order:
   a. @everyone overwrite: deny bits (AND NOT), then allow bits (OR)
   b. Role overwrites (for each role user holds): deny then allow
   c. Member overwrite: deny then allow

5. Server owner shortcut: if user is server owner → Administrator granted, skip all above

Permission Bits

Constant Bit Description
PermViewChannel 1 << 0 See channel + read history
PermSendMessages 1 << 1 Post new messages
PermSendTTSMessages 1 << 2 Use /tts
PermManageMessages 1 << 3 Delete/pin others' messages
PermEmbedLinks 1 << 4 Auto-embed URLs
PermAttachFiles 1 << 5 Upload attachments
PermReadMessageHistory 1 << 6 Load old messages
PermMentionEveryone 1 << 7 @everyone / @here
PermAddReactions 1 << 8 React to messages
PermConnect 1 << 10 Join voice channel
PermSpeak 1 << 11 Speak in voice
PermMuteMembers 1 << 12 Server-mute others
PermDeafenMembers 1 << 13 Server-deafen others
PermMoveMembers 1 << 14 Move users between voice channels
PermUseVAD 1 << 15 Voice Activity Detection
PermStream 1 << 16 Screen share / stream
PermChangeNickname 1 << 17 Change own nickname
PermManageNicknames 1 << 18 Change others' nicknames
PermManageRoles 1 << 19 Create/edit/delete roles
PermManageChannels 1 << 20 Create/edit/delete channels
PermManageServer 1 << 21 Change server settings
PermKickMembers 1 << 22 Remove members
PermBanMembers 1 << 23 Ban members
PermManageWebhooks 1 << 24 Manage webhooks
PermManageEmoji 1 << 25 Custom emoji
PermViewAuditLog 1 << 26 View audit log
PermViewServerInsights 1 << 27 Analytics
PermModerateMembers 1 << 28 Timeout members
PermAdministrator 1 << 29 Bypass ALL checks

Default @everyone Permissions

PermViewChannel | PermSendMessages | PermReadMessageHistory |
PermAddReactions | PermConnect | PermSpeak

Checking a Permission

function hasPermission(userPermissions: number, perm: number): boolean {
  if (userPermissions & (1 << 29)) return true; // Administrator
  return (userPermissions & perm) !== 0;
}

Invites

Create an invite

POST /api/servers/{serverID}/invites
{
  "max_uses": 0,        // 0 = unlimited
  "expires_in": 86400   // seconds; 0 = never
}
→ 201 Invite

Preview an invite (no auth required)

GET /api/invites/{code}
→ Invite

Use this to show a server preview before the user decides to join.

Accept an invite

POST /api/invites/{code}
→ 200 Server

Invite link format: https://zipp.chat/join/{code}

Errors: 404 (expired/not found), 409 (already a member), 403 (banned)