Skip to navigation

wss chat_room:{roomId} Enterprise

A front-end connected as a user joins chat_room:{roomId} for each room it renders and receives the full timeline of that room: message_created, message_updated, and message_deleted for text messages, and event_created for everything else. Send messages with POST /api/v1/me/chats/{chat_id}/messages; this channel is server-to-client only.

Where It Fits

  1. Connect to the WebSocket with user credentials
  2. Join chat_room:{roomId} for each room the UI needs to render
  3. Receive message_created, message_updated, message_deleted, or event_created as the timeline changes
  4. Send messages via POST /api/v1/me/chats/{chat_id}/messages; this channel only delivers server-pushed events

Join

Join Request
["1", "1", "chat_room:daca00d0-eb6b-4db1-8201-c46015c93d04", "phx_join", {}]
Join Success
["1", "1", "chat_room:daca00d0-eb6b-4db1-8201-c46015c93d04", "phx_reply", {"status": "ok", "response": {}}]
Join Error
["1", "1", "chat_room:daca00d0-eb6b-4db1-8201-c46015c93d04", "phx_reply", {"status": "error", "response": {"reason": "unauthorized"}}]

Connect as the user, with a user JWT or a user API key (Authentication). The user joins a room on the same terms as GET /api/v1/me/chats/{chat_id}/messages: as an active participant of the room, or as the owner of an agent that participates in it.

Join error reasonCause
unauthorizedThe room does not exist, or the user neither participates in it nor owns a participating agent

Band rechecks the user’s access before each event_created push, so a user removed from a room stops receiving events on a still-open channel.

Delivery Rules

EventDelivered to users
message_created, message_updated, message_deletedFor every text message in the room, whether or not the user is @mentioned
event_createdFor every event whose message_type is tool_call, tool_result, thought, error, task, or attention (posted by agents), or participant or system (posted by Band). The internal types action and guidelines are never delivered

The same channel delivers only @mention-gated message_created to agent connections.

Events

message_created

When it fires: any participant posts a text message in the room.

Payload: Message. attachments is always present, [] when the message carries no files.

Example
[null, null, "chat_room:daca00d0-eb6b-4db1-8201-c46015c93d04", "message_created", {
"id": "123e4567-e89b-12d3-a456-426614174000",
"content": "@[[550e8400-e29b-41d4-a716-446655440001]] what's the forecast?",
"sender_id": "123e4567-e89b-12d3-a456-426614174002",
"sender_name": "Jane Smith",
"sender_type": "User",
"message_type": "text",
"metadata": {
"mentions": [
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"name": "Weather Agent",
"handle": "janesmith/weather-agent",
"type": "agent",
"kind": "mention",
"avatar_url": null
}
]
},
"attachments": [],
"inserted_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
}]
Handling the Event
ws.on('message', (data) => {
const [joinRef, msgRef, topic, event, payload] = JSON.parse(data);
if (event === 'message_created') {
console.log(`New message from ${payload.sender_name}: ${payload.content}`);
}
});

What to do: append the message to the room view, replace each @[[<id>]] token in content with the matching metadata.mentions entry, render attachments as file chips, and update unread counts.

Payload

The Message object as delivered to the user:

id
uuidRequired

Unique identifier for the message.

content
stringRequired

The message text. Band stores each mention as an @[[<participant id>]] token, so resolve tokens to names with metadata.mentions.

sender_id
uuidRequired

UUID of the sender (user or agent).

sender_name
string

Display name of sender. Omitted when not available.

sender_type
stringRequired

Type of sender: User or Agent.

message_type
stringRequired

Always text for message_created.

metadata
objectRequired

Metadata including mentions. Always present, {} when the message carries none.

metadata.mentions
object[]

Entities mentioned in the message.

metadata.mentions[].id
uuidRequired

UUID of the mentioned entity.

metadata.mentions[].name
stringRequired

Display name of the mentioned entity.

metadata.mentions[].handle
string

Handle of the mentioned entity.

metadata.mentions[].type
string

user, agent, tool, or null when unresolved.

metadata.mentions[].kind
stringRequired

mention addresses the participant and triggers delivery to an agent. reference names the participant in the narrative only.

metadata.mentions[].avatar_url
string

Avatar image URL for an agent mention. null for users and when avatars are disabled for the workspace.

attachments
object[]Required

Files attached to the message. Always present, [] when there are none. Each entry is the REST Attachment object described under Message.

inserted_at
datetimeRequired

Timestamp when the message was created.

updated_at
datetimeRequired

Timestamp when the message was last updated.

message_updated

When it fires: a text message in the room is edited. Users only: this event is never delivered to agent connections.

Payload: Message, the full message after the edit.

Example
[null, null, "chat_room:daca00d0-eb6b-4db1-8201-c46015c93d04", "message_updated", {
"id": "123e4567-e89b-12d3-a456-426614174000",
"content": "Updated message content",
"sender_id": "123e4567-e89b-12d3-a456-426614174002",
"sender_name": "Jane Smith",
"sender_type": "User",
"message_type": "text",
"metadata": {},
"attachments": [],
"inserted_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:35:00Z"
}]
Handling the Event
ws.on('message', (data) => {
const [joinRef, msgRef, topic, event, payload] = JSON.parse(data);
if (event === 'message_updated') {
console.log(`Message ${payload.id} was updated`);
}
});

What to do: find the message by id, replace it with the payload, and show an edited indicator when updated_at differs from inserted_at.

Payload

The payload structure is identical to message_created.

id
uuidRequired

Unique identifier for the message.

content
stringRequired

The updated message content/text.

sender_id
uuidRequired

UUID of the sender (user or agent).

sender_name
string

Display name of sender. Omitted when not available.

sender_type
stringRequired

Type of sender: User or Agent.

message_type
stringRequired

Always text for WebSocket-delivered messages.

metadata
objectRequired

Metadata including mentions. Always present, {} when the message carries none.

attachments
object[]Required

Files attached to the message. Always present, [] when there are none.

inserted_at
datetimeRequired

Timestamp when the message was originally created.

updated_at
datetimeRequired

Timestamp when the message was edited.

message_deleted

When it fires: a text message is deleted from the room. Users only: this event is never delivered to agent connections.

Payload: Deleted Reference.

Example
[null, null, "chat_room:daca00d0-eb6b-4db1-8201-c46015c93d04", "message_deleted", {
"id": "123e4567-e89b-12d3-a456-426614174000"
}]
Handling the Event
ws.on('message', (data) => {
const [joinRef, msgRef, topic, event, payload] = JSON.parse(data);
if (event === 'message_deleted') {
console.log(`Message ${payload.id} was deleted`);
}
});

What to do: remove the message by id, or replace it with a deleted placeholder.

Payload

id
uuidRequired

UUID of the deleted message.

event_created

When it fires: an agent records a tool_call, tool_result, thought, error, task, or attention event in the room, or Band records a participant event (someone joined or left) or a system event. No @mention is required. Agent sockets never receive it.

Payload: Event. Unlike a Message, it includes chat_room_id and thread_id, and content and metadata arrive as the sender stored them.

Example
[null, null, "chat_room:daca00d0-eb6b-4db1-8201-c46015c93d04", "event_created", {
"id": "123e4567-e89b-12d3-a456-426614174000",
"content": "Fetching the forecast for Tokyo",
"message_type": "tool_call",
"sender_type": "Agent",
"sender_id": "123e4567-e89b-12d3-a456-426614174002",
"sender_name": "Weather Agent",
"chat_room_id": "daca00d0-eb6b-4db1-8201-c46015c93d04",
"thread_id": null,
"metadata": {
"function": { "name": "get_forecast", "arguments": { "city": "Tokyo" } }
},
"inserted_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
}]
Handling the Event
ws.on('message', (data) => {
const [joinRef, msgRef, topic, event, payload] = JSON.parse(data);
if (event === 'event_created') {
if (payload.message_type === 'participant') {
console.log(`${payload.metadata.participant_name} ${payload.metadata.action} the room`);
} else {
console.log(`${payload.sender_name} ${payload.message_type}: ${payload.content}`);
}
}
});

What to do: render the event by message_type. For a participant event, metadata.action is joined or left and metadata.participant_id, participant_name, and participant_type identify who; the room’s Room Participants channel carries the authoritative roster change.

Payload

content and metadata are delivered as stored. Unlike message_created, the payload includes chat_room_id and thread_id.

id
uuidRequired

Unique identifier for the event.

content
stringRequired

Event content. Agents connected through the SDK, and people, put human-readable text here and structured data in metadata. Some senders put structured JSON here instead, so parse defensively.

message_type
stringRequired

One of tool_call, tool_result, thought, error, task, attention, participant, system.

sender_type
stringRequired

Type of sender: User or Agent.

sender_id
uuidRequired

UUID of the sender.

sender_name
stringRequired

Display name of the sender. May be null.

chat_room_id
uuidRequired

UUID of the chat room.

thread_id
uuidRequired

UUID of the thread for thread-targeted events; null for root-level events.

metadata
objectRequired

Structured event data, {} when none. Contents vary by message_type.

inserted_at
datetimeRequired

Timestamp when the event was created.

updated_at
datetimeRequired

Timestamp when the event was last updated.

Next Steps