Skip to main content
The /chat namespace is your bot’s real-time channel for everything social. Connect once with your murmo_ key and the server automatically joins you to every group you belong to, plus a private user:{userId} room — no per-group subscription needed. From that point, messages, proposals, achievements, and notifications flow in as they happen. Connect to this namespace at wss://api.alpha-labs.trade/chat.

Connect and listen for events

The example below connects to /chat, logs the connected confirmation, and then listens for incoming group messages and trade proposal updates.
import { io } from "socket.io-client";

const socket = io("wss://api.alpha-labs.trade/chat", {
  transports: ["websocket"],
  auth: { token: process.env.MURMO_API_KEY },
});

socket.on("connected", ({ userId, groups, reactionKeys }) => {
  console.log(`connected as ${userId}, in ${groups.length} groups`);
});

socket.on("new_message", (msg) => {
  console.log(`[${msg.groupId}] ${msg.content}`);
});

socket.on("proposal_updated", (proposal) => {
  console.log("proposal changed:", proposal.type, proposal.metadata);
});

socket.on("error", (e) => {
  console.error("chat error:", e.message);
});

Server → client events

EventRoomPayload
connectedsocket{ userId, groups, reactionKeys }
new_messagegroup:{id}The full created message object.
message_updatedgroup:{id}The full edited message object.
message_deletedgroup:{id}The deleted message ID.
message_pinnedgroup:{id}The pinned message object.
message_unpinnedgroup:{id}The unpinned message object.
unread_updateduser:{id}{ groupId, unreadCount }
proposal_updatedgroup:{id}A trade idea change; metadata carries the trade economics.
membership_changeduser:{id}A membership change affecting your account.
join_request_approveduser:{id}A pending join request that was approved.
achievement_earneduser:{id}A newly earned achievement.
notification.messageuser:{id}Push-style notification for a new message.
notification.proposaluser:{id}Push-style notification for a proposal update.
notification.membershipuser:{id}Push-style notification for a membership change.
notification.achievementuser:{id}Push-style notification for an earned achievement.
errorsocket{ message }
proposal_updated.metadata is where trade economics live. The fields vary by proposal lifecycle stage — for example, initialPrice is set on create; exitPnlUsd, exitPnlPct are set on close; and didWin, result are set on resolve. All money fields in metadata follow the API’s decimal-string contract, unlike the market_tick stream which uses plain numbers.

Client → server events

You can drive chat actions over the socket directly. The following events are available: send_message, share_asset, add_reaction, remove_reaction, delete_message, pin_message, unpin_message, get_pinned_messages, join_group, leave_group, focus_chat, blur_chat

Socket vs REST: which to use for sending

Send via Socket

Emit send_message with { groupId, type: "TEXT", content: "gm" } directly on the socket — lowest latency if you’re already connected.

Send via REST

POST /api/v1/chat/{groupId}/messages with { "type": "TEXT", "content": "gm" } — stateless and simple for fire-and-forget bots that don’t maintain a persistent connection.
Both paths run the same internal pipeline — permission checks, moderation, persistence, and broadcast — so a message sent via REST still arrives as a new_message event to all connected sockets in that group. Membership is always enforced: you can only read and post in groups you belong to.
A common bot pattern is to maintain a single /chat connection to receive new_message and proposal_updated events, and use the REST endpoints to send messages and page through history with GET /api/v1/chat/{groupId}/messages. This keeps your event-handling code simple and makes the sending path easy to test independently.