API Reference WebSocket events, REST endpoints, and data structures
API reference for the Gryt signaling server. Authentication uses Keycloak OIDC via auth.gryt.chat and a challenge-response flow with identity certificates from id.gryt.chat. There are no custom auth REST endpoints on the server itself.
All REST endpoints are served by the signaling server (default port 5000). Endpoints marked Bearer require an Authorization: Bearer <accessToken> header.
Method Path Description GET /healthHealth check. Returns { status, service, serverName, timestamp } GET /metricsPrometheus metrics (text/plain) GET /infoPublic server preview: { name, description, members, version, lanOpen } GET /iconServer icon image (404 if none set) GET /.well-known/jwks.jsonJWKS for built-in identity verification
Method Path Auth Description GET /api/messages/:conversationIdBearer Fetch messages. Query: ?limit= (max 200), ?before= (ISO date)
Messages are created via the WebSocket chat:send event, not via REST.
Method Path Auth Description POST /api/uploadsBearer Upload a file (multipart file) POST /api/uploads/avatarBearer Upload avatar image DELETE /api/uploads/avatarBearer Remove avatar GET /api/uploads/files/:fileIdNo Download file. ?thumb=1 for thumbnail, ?download=1 for attachment header
Method Path Auth Description POST /api/server/iconBearer Upload server icon (owner only, multipart file) DELETE /api/server/iconBearer Remove server icon (owner only)
Method Path Auth Description GET /api/membersBearer List members: { items: [{ serverUserId, nickname, lastSeen }] }
Method Path Auth Description GET /api/emojisNo List all emojis: [{ name, file_id }] GET /api/emojis/img/:nameNo Emoji image bytes (cached) POST /api/emojisBearer Upload emoji (admin/owner). Multipart file + name, or files + names JSON for batch PATCH /api/emojis/:nameBearer Rename emoji (admin/owner). Body: { name } DELETE /api/emojis/:nameBearer Delete emoji (admin/owner)
Method Path Auth Description GET /api/emojis/bttv/user/:userIdNo Fetch channel/shared emotes for a BTTV user GET /api/emojis/bttv/emote/:emoteIdNo BTTV emote metadata GET /api/emojis/bttv/file/:emoteIdNo BTTV emote image bytes POST /api/emojis/bttv/importBearer Import BTTV emotes as server emojis (admin/owner, max 200 per request)
Method Path Auth Description GET /api/link-preview?url=Bearer Fetch Open Graph metadata for a URL GET /api/oembed?url=Bearer Fetch oEmbed data (X/Twitter, SoundCloud, Spotify, TikTok) GET /api/media/metadata?url=Bearer Fetch media dimensions and MIME type
Event Payload Description server:info— Request public server preview server:join{ nickname?, inviteCode? }Start join flow; server responds with server:challenge server:verify{ certificate, assertion }Complete join with signed identity assertion server:leave— Leave the server server:details— Request full server state token:refresh{ refreshToken? } or { accessToken? }Refresh access token
Event Payload Description server:info{ name, description, members, version, lanOpen }Public preview server:challenge{ nonce, serverHost }Identity challenge (response to server:join) server:joined{ accessToken, refreshToken, nickname, avatarFileId, isOwner, setupRequired }Join success server:error{ error, message?, retryAfterMs?, canReapply? }Error (e.g. invite_required, invalid_invite, rate_limited) server:detailsFull server state Channels, sidebar, clients, SFU hosts, STUN config server:left{ message }Leave confirmation token:refreshed{ accessToken }New access token token:error{ error, message? }Token error token:revoked{ reason, message }Session revoked
Event Payload Description voice:room:requestroomIdRequest SFU room credentials voice:stream:setstreamIDSet active stream (empty string to clear) voice:state:update{ isMuted, isDeafened, isAFK }Update voice state flags voice:channel:joinedbooleanNotify channel join/leave voice:camera:state{ enabled, streamId? }Update camera state voice:screen:state{ enabled, videoStreamId?, audioStreamId? }Update screen share state voice:peer:connectedstreamIdNotify peer stream connected voice:peer:disconnectedstreamIdNotify peer stream disconnected voice:latency:report{ estimatedOneWayMs?, networkRttMs?, jitterMs?, codec?, bitrateKbps? }Report latency metrics
Event Payload Description voice:room:granted{ room_id, join_token, sfu_url, sfu_urls?, timestamp }SFU room credentials voice:room:errorstring or { error, message?, retryAfterMs? }Voice join error voice:peer:joined{ clientId, nickname, channelId }Peer joined voice voice:peer:left{ clientId, nickname, channelId }Peer left voice voice:kicked{ reason }Force disconnected from voice voice:device:disconnect{ type, message }Device switch notification voice:latency:update{ clientId, latency }Peer latency update
Event Payload Description chat:send{ conversationId, accessToken, text?, attachments?, replyToMessageId?, nonce? }Send a message chat:fetch{ conversationId, limit?, before? }Fetch message history chat:react{ conversationId, messageId, reactionSrc, accessToken }Toggle reaction chat:edit{ conversationId, messageId, text, accessToken }Edit a message chat:delete{ conversationId, messageId, accessToken }Delete a message
Event Payload Description chat:newMessageRecordNew message broadcast chat:history{ conversation_id, items, hasMore, before? }Message history response chat:reactionMessageRecordReaction update broadcast chat:editedMessageRecordEdited message broadcast chat:deleted{ conversation_id, message_id }Deletion broadcast chat:error{ error, retryAfterMs?, message }Chat error (including rate limits)
Admin events require accessToken in the payload and appropriate role (admin or owner).
Event (Client → Server) Description server:settings:updateUpdate server settings (owner). Fields: displayName, description, avatarMaxBytes, uploadMaxBytes, emojiMaxBytes, profanityMode, profanityCensorStyle, systemChannelId, lanOpen server:invites:createCreate invite code server:invites:revokeRevoke invite code server:kickKick a user server:banBan a user server:unbanUnban a user server:roles:setSet user role (owner) server:channels:upsertCreate or update a channel server:channels:deleteDelete a channel server:channels:reorderReorder channels server:sidebar:item:upsertCreate or update sidebar item server:sidebar:item:deleteDelete sidebar item server:sidebar:reorderReorder sidebar voice:disconnect:userForce disconnect a user from voice
The SFU uses raw WebSocket (not Socket.IO). All messages use a JSON envelope:
{ "event" : "<event_name>" , "data" : "<json_string>" }
Event Data Description client_join{ room_id, server_id, server_password, user_token, user_id }Join a voice room answerSDP answer string WebRTC answer candidateRTCIceCandidateInit JSON ICE candidate renegotiate— Request renegotiation (e.g. to add tracks) keep_alive{ timestamp }Keep-alive ping (every 15s)
Event Data Description room_joined— Join success room_errorError string Join or room error offerSDP offer string WebRTC offer candidateRTCIceCandidateInit JSON ICE candidate
Path Method Description /healthGET Health check: { status, service, version, timestamp } /metricsGET Prometheus metrics
interface MessageRecord {
conversation_id : string ;
message_id : string ;
sender_server_id : string ;
text : string ;
created_at : string ;
edited_at ?: string ;
attachments ?: string [];
reactions ?: Reaction [];
reply_to_message_id ?: string ;
sender_nickname ?: string ;
sender_avatar_file_id ?: string ;
enriched_attachments ?: EnrichedAttachment [];
}
interface ServerChannelRecord {
channel_id : string ;
name : string ;
type : "text" | "voice" ;
position : number ;
description ?: string ;
require_push_to_talk ?: boolean ;
disable_rnnoise ?: boolean ;
max_bitrate ?: number ;
esports_mode ?: boolean ;
text_in_voice ?: boolean ;
}
Roles in order of permission: owner > admin > mod > member.
Errors are emitted as string-based codes, not numeric. Common error values:
invite_required — server is invite-only
invalid_invite — invite code is wrong or expired
rate_limited — too many requests (includes retryAfterMs)
auth_required — missing or invalid access token
token_invalid — access token is expired or malformed
not_owner / not_admin — insufficient permissions