Realtime WebSocket Protocol
Chatto exposes realtime updates at GET /api/realtime using binary protobuf frames from chatto.realtime.v1.
Realtime frames are documented separately from ConnectRPC services because they are exchanged over a long-lived WebSocket session rather than /api/connect RPC methods.
Protocol Types
Section titled “Protocol Types”RealtimeAssetDeletedEvent
Section titled “RealtimeAssetDeletedEvent”Asset-deleted signal.
Remove local attachment state by asset_id. When the owning message is known,
clients can refetch that message through MessageService.GetMessage or
refetch the timeline window.
| Field | Type | Description |
|---|---|---|
room_id | optional string | Room containing the message that owned the asset, when known. |
asset_id | string | Asset ID. |
RealtimeAssetProcessingEvent
Section titled “RealtimeAssetProcessingEvent”Asset-processing signal.
Message attachments are message-owned subresources. When
message_event_id is known, refetch message state with
MessageService.GetMessage or MessageService.BatchGetMessages; otherwise
refetch the affected attachment list or timeline window.
| Field | Type | Description |
|---|---|---|
room_id | optional string | Room containing the message that owns the asset, when known. |
asset_id | string | Asset ID. |
message_event_id | optional string | Event ID of the message that owns the asset, when known. |
RealtimeCallEvent
Section titled “RealtimeCallEvent”Voice-call state signal.
Hydrate current room-scoped call state with VoiceCallService.GetActiveCall
or VoiceCallService.BatchGetActiveCalls. Compare call_id to ignore stale
transitions from a previous call in the same room.
| Field | Type | Description |
|---|---|---|
room_id | string | Room containing the call. |
call_id | string | Call ID. |
source | RealtimeCallEventSource | Source that produced the call transition. |
RealtimeClientFrame
Section titled “RealtimeClientFrame”Client-to-server frame for Chatto’s protobuf WebSocket realtime protocol.
Clients send binary protobuf frames to /api/realtime. The first frame must
be hello; after the server replies with hello, clients send
subscribe_events to start the authenticated live event stream.
| Field | Type | Description |
|---|---|---|
frame.hello | RealtimeClientHello | Opens the protocol session and optionally carries a bearer token. |
frame.subscribe_events | RealtimeSubscribeEvents | Starts the caller’s authorized live-event stream. |
frame.ping | RealtimePing | Application-level ping. The server replies with pong. |
RealtimeClientHello
Section titled “RealtimeClientHello”Initial client hello.
| Field | Type | Description |
|---|---|---|
protocol_version | uint32 | Protocol version requested by the client. The current version is 1. |
bearer_token | optional string | Optional bearer token. When present, it takes precedence over cookie auth. |
RealtimeClose
Section titled “RealtimeClose”Close instruction sent before the socket is closed when possible.
| Field | Type | Description |
|---|---|---|
code | string | Stable machine-readable close code. |
message | string | Human-readable diagnostic message. |
reconnect | bool | True when the client should reconnect after a delay. |
retry_after_ms | uint32 | Suggested reconnect delay in milliseconds. |
RealtimeError
Section titled “RealtimeError”Protocol error.
| Field | Type | Description |
|---|---|---|
code | string | Stable machine-readable error code. |
message | string | Human-readable diagnostic message. |
fatal | bool | True when the server will close the socket after sending this error. |
RealtimeEventEnvelope
Section titled “RealtimeEventEnvelope”One authorized live event delivered over the realtime WebSocket.
Realtime events are invalidation signals first: payloads carry stable IDs and small inline hints, while durable resource state is hydrated through chatto.api.v1 ConnectRPC services. Event-specific messages document the intended hydration path when a referenced resource may need refreshing.
| Field | Type | Description |
|---|---|---|
id | string | Stable event ID. |
created_at | google.protobuf.Timestamp | Time the event was created. |
actor_id | optional string | User or system actor that caused the event, when known. |
event.message_posted | RealtimeMessagePostedEvent | A message was posted in a room or thread. |
event.message_edited | RealtimeMessageEditedEvent | A message body or renderable metadata changed. |
event.message_retracted | RealtimeMessageRetractedEvent | A message was retracted. |
event.reaction_added | RealtimeReactionEvent | A user added a reaction to a message. |
event.reaction_removed | RealtimeReactionEvent | A user removed a reaction from a message. |
event.user_typing | RealtimeTypingEvent | A user is typing in a room or thread. |
event.presence_changed | RealtimePresenceChangedEvent | A user’s live presence changed. |
event.room_created | RealtimeRoomEvent | A room was created. |
event.room_updated | RealtimeRoomEvent | A room’s profile or settings changed. |
event.room_deleted | RealtimeRoomEvent | A room was deleted. |
event.room_archived | RealtimeRoomEvent | A room was archived. |
event.room_unarchived | RealtimeRoomEvent | A room was restored from the archive. |
event.user_joined_room | RealtimeRoomEvent | A user joined a room visible to the current user. |
event.user_left_room | RealtimeRoomEvent | A user left a room visible to the current user. |
event.room_universal_changed | RealtimeRoomUniversalChangedEvent | A room’s universal visibility flag changed. |
event.notification_created | RealtimeNotificationCreatedEvent | A notification was created for the current user. |
event.notification_dismissed | RealtimeNotificationDismissedEvent | A notification was dismissed for the current user. |
event.notification_level_changed | RealtimeNotificationLevelChangedEvent | The current user’s notification level changed. |
event.thread_follow_changed | RealtimeThreadFollowChangedEvent | The current user’s thread follow state changed. |
event.room_marked_as_read | RealtimeRoomMarkedAsReadEvent | The current user’s room read state changed. |
event.thread_created | RealtimeThreadCreatedEvent | A thread was created in a room visible to the current user. |
event.server_updated | RealtimeServerUpdatedEvent | The server profile/config changed. |
event.user_profile_updated | RealtimeUserProfileUpdatedEvent | A user profile changed. |
event.user_custom_status_set | RealtimeUserCustomStatusSetEvent | A user’s custom status was set or changed. |
event.user_custom_status_cleared | RealtimeUserCustomStatusClearedEvent | A user’s custom status was cleared. |
event.server_user_preferences_updated | RealtimeServerUserPreferencesUpdatedEvent | The current user’s display preferences changed. |
event.room_groups_updated | RealtimeRoomGroupsUpdatedEvent | Room-group layout changed. |
event.server_member_deleted | RealtimeServerMemberDeletedEvent | A server member was deleted. |
event.asset_processing_started | RealtimeAssetProcessingEvent | Asset processing started for a message attachment. |
event.asset_processing_succeeded | RealtimeAssetProcessingEvent | Asset processing completed successfully for a message attachment. |
event.asset_processing_failed | RealtimeAssetProcessingEvent | Asset processing failed for a message attachment. |
event.asset_deleted | RealtimeAssetDeletedEvent | An asset attached to a message was deleted. |
event.call_started | RealtimeCallEvent | A voice call started in a room. |
event.call_participant_joined | RealtimeCallEvent | A participant joined a voice call. |
event.call_participant_left | RealtimeCallEvent | A participant left a voice call. |
event.call_ended | RealtimeCallEvent | A voice call ended. |
event.mention_notification | RealtimeMentionNotificationEvent | The current user was mentioned in a room. |
event.new_direct_message_notification | RealtimeNewDirectMessageNotificationEvent | The current user received a new direct message. |
event.session_terminated | RealtimeSessionTerminatedEvent | The current user’s session was terminated. |
RealtimeHeartbeat
Section titled “RealtimeHeartbeat”Application-level heartbeat.
| Field | Type | Description |
|---|---|---|
id | string | Stable event ID for this heartbeat. |
created_at | google.protobuf.Timestamp | Time the heartbeat was emitted. |
RealtimeMentionNotificationEvent
Section titled “RealtimeMentionNotificationEvent”Mention attention signal for the connected user.
Inline names are display hints. Hydrate referenced rooms through
RoomDirectoryService.BatchGetRooms and users through
UserService.BatchGetUsers when local caches are missing or stale.
| Field | Type | Description |
|---|---|---|
room_id | string | Room where the mention occurred. |
actor_user_id | string | Author user ID. |
room_name | optional string | Display name of the room where the mention occurred, when hydrated for the caller. |
actor_display_name | optional string | Display name of the author who mentioned the connected user, when hydrated. |
RealtimeMessageEditedEvent
Section titled “RealtimeMessageEditedEvent”Message-edited signal.
Refresh the affected timeline window with
RoomService.GetRoomEventsAround for room messages, or
ThreadService.GetThreadEventsAround when the local message belongs to
a thread.
| Field | Type | Description |
|---|---|---|
room_id | string | Room containing the message. |
message_event_id | string | Event ID of the edited message. |
RealtimeMessagePostedEvent
Section titled “RealtimeMessagePostedEvent”Message-posted signal.
Hydrate the affected timeline with RoomService.GetRoomEventsAround
for room messages, or ThreadService.GetThreadEventsAround when
thread_root_event_id is set.
| Field | Type | Description |
|---|---|---|
room_id | string | Room containing the message. |
message_event_id | string | Event ID of the posted message. |
thread_root_event_id | optional string | Root message event ID when the message was posted in a thread. |
RealtimeMessageRetractedEvent
Section titled “RealtimeMessageRetractedEvent”Message-retracted signal.
Refresh or patch the affected timeline window with
RoomService.GetRoomEventsAround for room messages, or
ThreadService.GetThreadEventsAround when the local message belongs to
a thread.
| Field | Type | Description |
|---|---|---|
room_id | string | Room containing the message. |
message_event_id | string | Event ID of the retracted message. |
reason | optional string | Retraction reason, when one was supplied. |
RealtimeNewDirectMessageNotificationEvent
Section titled “RealtimeNewDirectMessageNotificationEvent”New-DM attention signal for the connected user.
Inline names and avatar URLs are display hints. Hydrate the DM room through
RoomDirectoryService.GetRoom or RoomDirectoryService.BatchGetRooms, and
the sender through UserService.GetUser or UserService.BatchGetUsers when
local caches are missing or stale.
| Field | Type | Description |
|---|---|---|
room_id | string | DM room ID. |
sender_id | string | Sender user ID. |
sender_display_name | optional string | Display name of the sender, when hydrated. |
sender_avatar_url | optional string | Avatar URL of the sender, when one is available. |
conversation_name | optional string | Display name for the DM conversation from the connected user’s perspective, when hydrated. |
RealtimeNotificationCreatedEvent
Section titled “RealtimeNotificationCreatedEvent”Notification-created signal for the connected user.
Hydrate full notification rows with NotificationService.GetNotification or
NotificationService.BatchGetNotifications. Use RoomService or
ThreadService anchor reads when event_id or in_reply_to_id must be
opened in context.
| Field | Type | Description |
|---|---|---|
notification_id | string | Notification ID. |
room_id | optional string | Room associated with the notification, when the notification navigates to a room. |
event_id | optional string | Event ID associated with the notification, when the notification navigates to an event. |
in_reply_to_id | optional string | Parent event ID for reply notifications, when known. |
silent | bool | True when the notification should not alert. |
RealtimeNotificationDismissedEvent
Section titled “RealtimeNotificationDismissedEvent”Notification-dismissed signal for the connected user.
Remove the local notification row by ID. A later singular notification read
returns NOT_FOUND; batch reads omit dismissed or missing notifications.
| Field | Type | Description |
|---|---|---|
notification_id | string | Notification ID. |
RealtimeNotificationLevelChangedEvent
Section titled “RealtimeNotificationLevelChangedEvent”Notification-level signal for the connected user.
The updated level is inline. Use
NotificationPreferencesService.GetRoomNotificationPreference when clients
need the complete preference resource.
| Field | Type | Description |
|---|---|---|
room_id | string | Room whose notification level changed. |
level | chatto.api.v1.NotificationLevel | Explicit room notification level. |
effective_level | chatto.api.v1.NotificationLevel | Effective notification level after defaults are applied. |
RealtimePing
Section titled “RealtimePing”Application-level ping.
| Field | Type | Description |
|---|---|---|
nonce | string | Client-chosen opaque value echoed in the pong. |
RealtimePong
Section titled “RealtimePong”Application-level pong.
| Field | Type | Description |
|---|---|---|
nonce | string | Echo of the ping nonce. |
RealtimePresenceChangedEvent
Section titled “RealtimePresenceChangedEvent”Presence-changed signal.
The latest presence status is inline. Use UserService.GetUser when
the surrounding user profile or custom status also needs refreshing.
| Field | Type | Description |
|---|---|---|
user_id | string | User whose presence changed. |
status | chatto.api.v1.PresenceStatus | Latest presence status. |
RealtimeReactionEvent
Section titled “RealtimeReactionEvent”Reaction signal.
Refresh or patch the affected message in its timeline window. Use
RoomService.GetRoomEventsAround for room messages, or
ThreadService.GetThreadEventsAround when the local message belongs to
a thread. message_event_id is canonical: reactions made through a channel
echo of a thread reply report the original reply event ID. Clients that show
channel echoes should also refresh or patch visible echo rows whose
echo_of_event_id matches this ID.
| Field | Type | Description |
|---|---|---|
room_id | string | Room containing the reacted-to message. |
message_event_id | string | Canonical event ID of the reacted-to message. |
emoji | string | Reaction emoji. |
RealtimeRoomEvent
Section titled “RealtimeRoomEvent”Room lifecycle or membership signal.
Hydrate visible room state with RoomDirectoryService.GetRoom or
RoomDirectoryService.BatchGetRooms. Unknown/deleted rooms return
NOT_FOUND from singular reads, hidden rooms return PERMISSION_DENIED, and
batch reads omit unknown, deleted, hidden, and inaccessible rooms.
| Field | Type | Description |
|---|---|---|
room_id | string | Room affected by the event. |
RealtimeRoomGroupsUpdatedEvent
Section titled “RealtimeRoomGroupsUpdatedEvent”Room-group layout signal.
Refetch room-group layout with RoomDirectoryService.ListRoomGroups. If a
client already knows specific group IDs, it can use
RoomDirectoryService.GetRoomGroup or
RoomDirectoryService.BatchGetRoomGroups.
| Field | Type | Description |
|---|---|---|
changed | bool | Always true; present for event-bus compatibility. |
RealtimeRoomMarkedAsReadEvent
Section titled “RealtimeRoomMarkedAsReadEvent”Room-read signal for the connected user.
This is a current-user read-state invalidation signal. Clients can patch local unread state for the room; no separate hydration is normally needed.
| Field | Type | Description |
|---|---|---|
room_id | string | Room marked read. |
RealtimeRoomUniversalChangedEvent
Section titled “RealtimeRoomUniversalChangedEvent”Room universal-visibility signal.
The latest universal flag is inline. Hydrate the rest of the visible room with
RoomDirectoryService.GetRoom or RoomDirectoryService.BatchGetRooms.
| Field | Type | Description |
|---|---|---|
room_id | string | Room whose universal setting changed. |
universal | bool | Latest universal setting. |
RealtimeServerFrame
Section titled “RealtimeServerFrame”Server-to-client frame for Chatto’s protobuf WebSocket realtime protocol.
| Field | Type | Description |
|---|---|---|
frame.hello | RealtimeServerHello | Confirms protocol version, server version, and capabilities. |
frame.subscribed | RealtimeSubscribed | Confirms the live-event stream has started. |
frame.event | RealtimeEventEnvelope | A live event authorized for the connected user. |
frame.heartbeat | RealtimeHeartbeat | Application-level heartbeat used for liveness checks. |
frame.error | RealtimeError | Protocol or authorization error. |
frame.close | RealtimeClose | Server-requested close with reconnect guidance. |
frame.pong | RealtimePong | Reply to a client ping. |
RealtimeServerHello
Section titled “RealtimeServerHello”Initial server hello.
| Field | Type | Description |
|---|---|---|
protocol_version | uint32 | Protocol version accepted by the server. |
server_version | string | Chatto server software version. |
heartbeat_interval_seconds | uint32 | Approximate heartbeat interval clients should expect. |
capabilities | repeated string | Stable protocol capability keys supported by this server. Current keys: chatto.realtime.events.live.v1, chatto.realtime.heartbeat.v1, and chatto.realtime.ping.v1. |
RealtimeServerMemberDeletedEvent
Section titled “RealtimeServerMemberDeletedEvent”Server-member deleted signal.
Remove or invalidate local member/user rows for user_id. Admin clients can
hydrate remaining member rows through AdminUserService.GetMember or
AdminUserService.BatchGetMembers.
| Field | Type | Description |
|---|---|---|
user_id | string | Deleted user ID. |
RealtimeServerUpdatedEvent
Section titled “RealtimeServerUpdatedEvent”Server-profile signal.
Public server profile fields are inline. Use ServerDiscoveryService.GetServer
for public metadata and ServerService.GetMotd for the authenticated MOTD.
| Field | Type | Description |
|---|---|---|
name | string | Server display name. |
description | string | Server description. |
logo_url | optional string | Server logo URL, when configured. |
banner_url | optional string | Server banner URL, when configured. |
RealtimeServerUserPreferencesUpdatedEvent
Section titled “RealtimeServerUserPreferencesUpdatedEvent”User display-preferences signal for the connected user.
The updated display preferences are inline for the connected user.
| Field | Type | Description |
|---|---|---|
timezone | optional string | IANA timezone name, when the user has selected a fixed timezone. |
time_format | chatto.api.v1.TimeFormat | Preferred time display format. |
RealtimeSessionTerminatedEvent
Section titled “RealtimeSessionTerminatedEvent”Session-terminated signal for the connected user’s current session.
| Field | Type | Description |
|---|---|---|
reason | string | Termination reason. |
RealtimeSubscribeEvents
Section titled “RealtimeSubscribeEvents”Request to start the caller’s authorized live event stream.
RealtimeSubscribed
Section titled “RealtimeSubscribed”Confirms event streaming has started.
RealtimeThreadCreatedEvent
Section titled “RealtimeThreadCreatedEvent”Thread-created signal.
Hydrate the new thread with ThreadService.GetThreadEventsAround using
thread_root_event_id as the anchor.
| Field | Type | Description |
|---|---|---|
room_id | string | Room containing the thread. |
thread_root_event_id | string | Root message event ID that identifies the thread. |
RealtimeThreadFollowChangedEvent
Section titled “RealtimeThreadFollowChangedEvent”Thread-follow signal for the connected user.
The updated follow state is inline. Use ThreadService.GetThreadEvents
or ThreadService.GetThreadEventsAround when the thread itself needs
refreshing.
| Field | Type | Description |
|---|---|---|
room_id | string | Room containing the thread. |
thread_root_event_id | string | Root message event ID of the thread. |
following | bool | True when the user now follows the thread. |
RealtimeTypingEvent
Section titled “RealtimeTypingEvent”Typing signal.
This is an ephemeral signal. room_id and thread_root_event_id identify
where to display typing state; clients normally do not hydrate it.
| Field | Type | Description |
|---|---|---|
room_id | string | Room where the actor is typing. |
thread_root_event_id | optional string | Thread root event ID when the actor is typing in a thread. |
RealtimeUserCustomStatusClearedEvent
Section titled “RealtimeUserCustomStatusClearedEvent”User-custom-status cleared signal.
Clear local custom status for user_id. Use UserService.GetUser
when clients need to refresh the complete user profile.
| Field | Type | Description |
|---|---|---|
user_id | string | User whose status was cleared. |
RealtimeUserCustomStatusSetEvent
Section titled “RealtimeUserCustomStatusSetEvent”User-custom-status set signal.
The latest custom status is inline. Use UserService.GetUser when
clients need to refresh the complete user profile.
| Field | Type | Description |
|---|---|---|
user_id | string | User whose status changed. |
emoji | string | Emoji part of the status. |
text | string | Text part of the status. |
expires_at | google.protobuf.Timestamp | Expiration time, when the status expires automatically. |
RealtimeUserProfileUpdatedEvent
Section titled “RealtimeUserProfileUpdatedEvent”User-profile signal.
Basic profile fields are inline. Use UserService.GetUser or
UserService.BatchGetUsers for complete user-profile hydration.
| Field | Type | Description |
|---|---|---|
user_id | string | User whose profile changed. |
login | string | Login name. |
display_name | string | Display name. |
avatar_url | optional string | Avatar URL, when the user has an avatar. |
Protocol Enums
Section titled “Protocol Enums”RealtimeCallEventSource
Section titled “RealtimeCallEventSource”Source that produced a realtime voice-call transition.
| Name | Number | Description |
|---|---|---|
REALTIME_CALL_EVENT_SOURCE_UNSPECIFIED | 0 | No call event source was specified. |
REALTIME_CALL_EVENT_SOURCE_USER | 1 | The user explicitly requested the transition through Chatto. |
REALTIME_CALL_EVENT_SOURCE_LIVEKIT | 2 | LiveKit reported the transition through a webhook. |
REALTIME_CALL_EVENT_SOURCE_RECONCILIATION | 3 | Chatto reconciled its local state against LiveKit’s current room state. |