WebSocket Events¶
All communication with the server happens over a single WebSocket connection. Client-to-server messages are called events; server-to-client messages are called dispatches.
Connection¶
Connect to:
ws://<host>/messaging/
Authentication is handled by your ASGI middleware. Unauthenticated connections
are closed immediately with code 4001.
Payload Format¶
Every client event follows this structure:
{
"event_type": "<event_name>",
"data": { ... }
}
Every server dispatch follows this structure:
{
"eventType": "<dispatch_name>",
"data": { ... }
}
Note the difference in casing: event_type (snake_case) for client events,
eventType (camelCase) for server dispatches.
On Connect¶
chat.notifications¶
Dispatched automatically to the connecting user immediately after a successful
connection (when ENABLE_NOTIFICATION is True).
{
"eventType": "chat.notifications",
"data": {
"<room_uuid>": [
{
"id": "<notification_uuid>",
"notification_type": "NEW_MESSAGE",
"message": { ... }
}
]
}
}
Data is keyed by room UUID. Each value is a list of notification objects for that
room. An empty object ({}) means no pending notifications.
Message Events¶
message.send¶
Send a new message to a room. Requires room membership and send permission.
Client payload:
{
"event_type": "message.send",
"data": {
"room_id": "<room_uuid>",
"content": "Hello!",
"extra_fields": {
"parent_message_id": "<uuid>", // optional — creates a reply
"forwarded_from_id": "<uuid>", // optional — creates a forward
"media": [ // optional — attach files
{
"media_url": "https://cdn.example.com/file.jpg",
"media_type": "image",
"file_size": 204800,
"mime_type": "image/jpeg",
"metadata": {}
}
]
}
}
}
Note
parent_message_id and forwarded_from_id are mutually exclusive.
A message cannot be both a reply and a forward.
Server dispatch (broadcast to all room members):
{
"eventType": "message.dispatch",
"data": { ... } // full Message object
}
message.acknowledged¶
Inform the server that the client received one or more messages. Updates
delivered_to on each message and removes the user from notification
recipients.
Client payload:
{
"event_type": "message.acknowledged",
"data": {
"message_id": ["<uuid1>", "<uuid2>"]
}
}
Server dispatch (sent only to the original message sender(s)):
{
"eventType": "messagedelivered.dispatch",
"data": [ ... ] // list of updated message objects
}
message.read¶
Mark one or more messages as read.
Client payload:
{
"event_type": "message.read",
"data": {
"message_id": ["<uuid1>", "<uuid2>"]
}
}
Server dispatch (broadcast to all room members):
{
"eventType": "readreceipt.dispatch",
"data": { ... } // updated message(s) with read_receipts
}
message.react¶
Add or remove a reaction. One reaction per user per message — adding a new one replaces the old.
Client payload (add):
{
"event_type": "message.react",
"data": {
"type": "add",
"message_id": "<uuid>",
"reaction_content": "👍"
}
}
Client payload (remove):
{
"event_type": "message.react",
"data": {
"type": "remove",
"message_id": "<uuid>",
"reaction_content": "👍"
}
}
Server dispatch (broadcast to all room members):
{
"eventType": "reaction.dispatch",
"data": {
"status": "successful",
"type": "add",
"message": { ... }
}
}
message.typing¶
Signal that the current user is typing. Not persisted.
Client payload:
{
"event_type": "message.typing",
"data": {
"room_id": "<uuid>"
}
}
Server dispatch (broadcast to all room members):
{
"eventType": "messagetyping.dispatch",
"data": {
"username": "alice"
}
}
message.modify¶
Edit or delete messages. Only the original sender can modify their messages.
Update (single message only):
{
"event_type": "message.modify",
"data": {
"action": "update",
"message_id": "<uuid>",
"extra_fields": {
"content": "Updated content"
}
}
}
Delete (single or bulk):
{
"event_type": "message.modify",
"data": {
"action": "delete",
"message_id": ["<uuid1>", "<uuid2>"]
}
}
All messages in a bulk delete must come from the same room.
Server dispatch (broadcast to all room members):
{
"eventType": "messagemodification.dispatch",
"data": {
"status": "successful",
"action": "update", // or "delete"
"message": { ... } // for update; "message_ids": [...] for delete
}
}
Room Events¶
room.create¶
Create a new room. The creator is automatically added as a member.
OneToOneChat:
{
"event_type": "room.create",
"data": {
"type": "OneToOneChat",
"participants": [2]
}
}
Note
For OneToOneChat, participants should contain only the other user’s
ID. The creator is added automatically. The total count will be 2.
GroupChat:
{
"event_type": "room.create",
"data": {
"type": "GroupChat",
"name": "Project Team",
"description": "Discussion for Project X",
"participants": [2, 3, 4],
"extra_fields": {
"join_approval_required": false,
"group_locked": false,
"property": {
"preferences": {
"notifications": true
}
}
}
}
}
Channel:
{
"event_type": "room.create",
"data": {
"type": "Channel",
"name": "Announcements",
"description": "Company-wide updates",
"subscribers": [2, 3],
"extra_fields": {
"is_public": true,
"property": {
"preferences": {}
}
}
}
}
Server dispatch (broadcast to all initial members):
{
"eventType": "roomcreate.dispatch",
"data": { ... } // full room object with type field
}
room.list¶
Retrieve all rooms the current user belongs to.
Client payload:
{
"event_type": "room.list",
"data": {}
}
Server dispatch (sent only to requesting user):
{
"eventType": "roomlist.dispatch",
"data": [
{
"type": "OneToOneChat",
"id": "<uuid>",
"peer": { ... },
"last_message": { ... }
},
{
"type": "GroupChat",
"id": "<uuid>",
"name": "Project Team",
"creator": { ... },
"last_message": { ... }
}
]
}
The type field in each room object identifies the concrete room type.
room.info¶
Retrieve full details for a specific room.
Client payload:
{
"event_type": "room.info",
"data": {
"room_id": "<uuid>"
}
}
Server dispatch (sent only to requesting user):
{
"eventType": "roominfo.dispatch",
"data": {
"type": "GroupChat",
"id": "<uuid>",
"name": "...",
"participants": [...],
"admins": [...],
"creator": {...},
"property": {"preferences": {}},
...
}
}
room.messages¶
Retrieve messages for a room with optional pagination.
Client payload:
{
"event_type": "room.messages",
"data": {
"room_id": "<uuid>",
"paginate": {
"page": 1,
"size": 50
}
}
}
Omit paginate to retrieve all messages. Response is sent only to the
requesting user.
Server dispatch:
{
"eventType": "roommessages.dispatch",
"data": {
"has_next": true,
"has_previous": false,
"next_page_number": 2,
"prev_page_number": null,
"page": 1,
"size": 50,
"data": {
"room_id": "<uuid>",
"messages": [ ... ]
}
}
}
room.join¶
Join a room. For Channels: allowed if is_public is True. For GroupChats:
raises an error by default (admin must add you). See Custom Handlers to
implement custom approval flows.
Client payload:
{
"event_type": "room.join",
"data": {
"room_id": "<uuid>"
}
}
Server dispatch (broadcast to all room members):
{
"eventType": "roomaddmembers.dispatch",
"data": {
"room": { ... },
"new_members": ["alice"],
"added_by": "self"
}
}
room.leave¶
Leave a room. Not available for OneToOneChat.
Client payload:
{
"event_type": "room.leave",
"data": {
"room_id": "<uuid>"
}
}
Server dispatches:
Sent to the leaving user:
{
"eventType": "roomexit.dispatch",
"data": {
"room": { ... },
"message": "You left Project Team"
}
}
Broadcast to remaining members:
{
"eventType": "roomremovemembers.dispatch",
"data": {
"room": { ... },
"removed_members": ["alice"],
"removed_by": "self"
}
}
If the room is deleted because it becomes empty:
{
"eventType": "roomdelete.dispatch",
"data": {
"room_id": "<uuid>"
}
}
room.add_members¶
Add users to a room. Requires can_add_new_participants (GroupChat) or
can_add_new_subscribers (Channel) permission.
Client payload:
{
"event_type": "room.add_members",
"data": {
"room_id": "<uuid>",
"members": [5, 6, 7]
}
}
Server dispatch (broadcast to all room members):
{
"eventType": "roomaddmembers.dispatch",
"data": {
"room": { ... },
"new_members": ["carol", "dave", "eve"],
"added_by": "alice"
}
}
room.remove_members¶
Remove users from a room. Requires can_remove_participants (GroupChat) or
can_remove_subscribers (Channel) permission. Room creator cannot be removed
by another admin.
Client payload:
{
"event_type": "room.remove_members",
"data": {
"room_id": "<uuid>",
"members": [5, 6]
}
}
Server dispatches:
Sent to each removed user:
{
"eventType": "roomexit.dispatch",
"data": {
"room": { ... },
"message": "You have been removed by alice"
}
}
Broadcast to remaining members:
{
"eventType": "roomremovemembers.dispatch",
"data": {
"room": { ... },
"removed_members": ["carol", "dave"],
"removed_by": "alice"
}
}
room.modify¶
Admin/moderator-only room management. Requires admin status for most actions; only the room creator can delete.
Update room details:
{
"event_type": "room.modify",
"data": {
"room_id": "<uuid>",
"action": "update",
"data": {
"name": "New Room Name",
"description": "Updated description",
"avatar": "https://cdn.example.com/avatar.jpg",
"group_locked": true, // GroupChat only
"join_approval_required": true, // GroupChat only
"is_public": false, // Channel only
"property": {
"preferences": {"theme": "dark"}
}
}
}
}
Delete room (creator only for GroupChat/Channel):
{
"event_type": "room.modify",
"data": {
"room_id": "<uuid>",
"action": "delete"
}
}
Add/remove admin (GroupChat only):
{
"event_type": "room.modify",
"data": {
"room_id": "<uuid>",
"action": "add_admin",
"data": {"users": [5, 6]}
}
}
Add/remove moderator (Channel only):
{
"event_type": "room.modify",
"data": {
"room_id": "<uuid>",
"action": "add_moderator",
"data": {"users": [8]}
}
}
Grant/revoke permissions:
{
"event_type": "room.modify",
"data": {
"room_id": "<uuid>",
"action": "add_permission",
"data": {
"users": [3],
"permission": ["can_send_messages"]
}
}
}
Valid actions: update, delete, add_admin, remove_admin,
add_moderator, remove_moderator, add_permission,
remove_permission.
Server dispatch (broadcast to all room members):
{
"eventType": "roomupdate.dispatch",
"data": { ... } // full updated room object
}
For delete:
{
"eventType": "roomdelete.dispatch",
"data": {
"room_id": "<uuid>"
}
}
Session Events¶
session.heartbeat¶
Keep the session alive. Send every 15–30 seconds.
Client payload:
{
"event_type": "session.heartbeat",
"data": {}
}
Server response (to requesting user only):
{"status": "success"}
Display Logic Tips¶
Several dispatch events include context fields to help the frontend display the right message:
removed_by: "self"— the user left voluntarily → display “Alice left”removed_by: "<username>"— the user was removed → display “Bob removed Alice”added_by: "self"— the user joined voluntarily → display “Alice joined”added_by: "<username>"— the user was added by someone → display “Bob added Alice”