Client → Server Methods
Methods that clients invoke on the SignalR hub to send messages, join conversations, and signal typing.
Overview
These are the methods clients call on the server hub. Agents call them from the dashboard, and visitors call them from the widget. The server validates permissions, persists data, and broadcasts events to the appropriate groups.
JoinConversation
Subscribe to real-time updates for a specific conversation. Adds the caller to the conversation:{id} SignalR group.
await connection.invoke("JoinConversation", conversationId);{
"conversationId": "conv_abc123"
}Who can call: Agents (any conversation in their tenant), Visitors (only their own conversation)
Server response: None (fire-and-forget). The client will start receiving events for that conversation.
JoinConversation when opening a conversation in the dashboard UI. The tenant group already receives high-level events (new conversations, status changes), but joining a specific conversation enables message-level events.StartConversation
Create a new conversation (visitor only). The server creates the conversation record, adds the visitor to the conversation group, and broadcasts ConversationCreated to the tenant group.
const result = await connection.invoke("StartConversation", {
visitorName: "John Doe",
visitorEmail: "[email protected]",
initialMessage: "Hi, I need help with my order"
});
// result: { conversationId: "conv_new123" }{
"visitorName": "John Doe",
"visitorEmail": "[email protected]",
"initialMessage": "Hi, I need help with my order",
"pageUrl": "https://example.com/orders",
"metadata": {
"browser": "Chrome 120",
"os": "macOS"
}
}visitorName— requiredinitialMessage— required, the first message contentvisitorEmail— optional (required if workspace hasrequireEmail: true)pageUrl— optional, the page the visitor is onmetadata— optional, arbitrary key-value pairs
Returns: { conversationId: string }
SendMessage
Send a message in a conversation. The server persists the message and broadcasts MessageReceived to all members of the conversation group.
await connection.invoke("SendMessage", {
conversationId: "conv_abc123",
content: "Sure, I can help with that!",
isInternal: false
});{
"conversationId": "conv_abc123",
"content": "Sure, I can help with that!",
"isInternal": false
}conversationId— requiredcontent— required, message text (max 5000 characters)isInternal— optional, defaults tofalse. Whentrue, the message is an internal note only visible to agents
isInternal: true) are only broadcasted to the tenant:{tenantId} group, not to the visitor. Visitors can never see or receive internal notes through any channel.SendTyping
Signal that the user is typing. The server broadcasts TypingChanged to the conversation group. This is a lightweight event — it is not persisted.
await connection.invoke("SendTyping", {
conversationId: "conv_abc123",
isTyping: true
});{
"conversationId": "conv_abc123",
"isTyping": true
}conversationId— requiredisTyping— required,truewhen typing starts,falsewhen it stops
Clients should debounce typing signals — send isTyping: true on the first keystroke, then isTyping: false after 3 seconds of inactivity or when the message is sent.
Error Handling
If a hub method fails (validation error, permission denied), SignalR throws a HubException which the client receives as a rejected promise:
try {
await connection.invoke("SendMessage", payload);
} catch (error) {
// error.message: "You do not have access to this conversation"
console.error("Hub method failed:", error.message);
}