GET /api/v1/ws?token={JWT_ACCESS_TOKEN}
Upgrade to WebSocket connection. The JWT access token is passed as a query parameter and validated during the HTTP upgrade handshake.
- Token: JWT access token (same as used for REST API
Authorization: Bearerheader) - Token lifetime: 15 minutes
- On token expiry: Client should read fresh token from storage before reconnecting (tokens are refreshed by the REST client's 401 interceptor)
- Strategy: Exponential backoff starting at 1s, doubling each attempt, capped at 30s
- On reconnect: Client reads fresh access token from localStorage before rebuilding the WebSocket URL
- After reconnect: Client refetches active channel messages, member list, DMs, and unread states to recover events missed during the disconnect window
| Direction | Message | Interval | Purpose |
|---|---|---|---|
| Server -> Client | WebSocket ping frame | Every 30s | Detect dead connections |
| Client -> Server | { "type": "HEARTBEAT" } |
Every 60s | Refresh presence TTL |
- Presence TTL in Redis: 90 seconds
- If no HEARTBEAT received within 90s, user's presence key expires and they appear offline
- On connect: Server automatically subscribes the client to all channels the user has access to (server channels via
server_members+ DM channels viadm_members) - On server join: Server subscribes the user's WebSocket clients to all channels in the joined server
- On server leave: Server unsubscribes the user's WebSocket clients from all channels in the left server
- On disconnect: Server unsubscribes the client from all channels and removes from user mapping
{ "type": "HEARTBEAT" }Refreshes the user's presence TTL in Redis.
{ "type": "PRESENCE_UPDATE", "data": { "status": "online" | "idle" | "dnd" | "offline" } }Sets the user's presence status. Broadcast to all subscribed channels.
{ "type": "TYPING_START", "data": { "channelId": "uuid" } }Indicates the user is typing in a channel. Debounced server-side (8s Redis key per user/channel pair).
All events follow this format:
{ "type": "EVENT_TYPE", "data": { ... } }{ message: Message }
// or flat Message objectA new message was sent in a channel.
{ message: Message }
// or flat Message objectA message was edited.
{ channelId: string; messageId: string }{ channel: Channel }
// or flat Channel object{ channel: Channel }
// or flat Channel object{ channelId: string; serverId: string }Server // { id, name, iconUrl?, description?, ownerId, createdAt }Server name, description, or icon was changed.
{ serverId: string }{ serverId: string; userId: string }{ serverId: string; userId: string }{ serverId: string; userId: string }{ serverId: string; userId: string }{ serverId: string; userId: string; timedOutUntil: string }{ serverId: string; userId: string; nickname: string }Role // { id, serverId, name, color?, position, permissions, isDefault, createdAt }Role{ roleId: string; serverId: string }{ serverId: string; roleId: string; userId: string }{ serverId: string; roleId: string; userId: string }ChannelCategory // { id, serverId, name, position, createdAt }ChannelCategory{ categoryId: string; serverId: string }{ userId: string; status: string }{ channelId: string; userId: string }{ channelId: string; mentionCount?: number; senderName?: string; channelName?: string; content?: string }{ channelId: string; messageId: string; userId: string; emoji: string }{ channelId: string; messageId: string; userId: string; emoji: string }DMChannel // { id, name, type, position, createdAt, recipients, lastMessage? }{ channelId: string; messageId: string }{ channelId: string; messageId: string }{ isReconnect: boolean }Emitted locally by the WebSocket client when the connection opens. Not sent over the wire. isReconnect is true for all connections after the first successful one.
- Send buffer overflow: Server buffers up to 256 events per client. If the buffer fills, events are dropped with a warning log. After 10 consecutive drops, the server closes the connection with status
4013 (Try Again Later). The client will reconnect and get fresh state. - Malformed messages: Silently ignored (logged client-side).
- Connection errors: Trigger automatic reconnection with exponential backoff.