Skip to main content

Overview

Socket.IO enables real-time bidirectional communication between your application and the Helpdesk system. This allows instant messaging, voice calls, live notifications, and seamless customer support experiences.

Connection

Authentication

Connect to the Socket.IO server using your API key:
const API_BASE_URL = '{api_base_url}';
const socket = io(`${API_BASE_URL}/helpdesk?token_source=api&token=YOUR_API_KEY`);
Never expose your API key in client-side code. Fetch it from your backend and pass it to the socket connection.

Connection Response

Upon successful connection, you’ll receive:
{
  "status_code": 200,
  "sid": "socket_session_id",
  "message": "Connected",
  "data": {}
}
If authentication fails, the connection is refused with status_code 400 or 401.

Response Envelope

All event responses follow this envelope:
{
  "status_code": 200,
  "sid": "socket_session_id",
  "message": "Human-readable status",
  "data": {}
}

Event Flow

1

Connect

Authenticate and establish a connection using your API key.
2

Enter Customer Room

Call enter_customer_support with the customer’s session ID immediately after connecting. This subscribes the customer to their personal room so they can receive messages from agents even before a conversation starts.
3

Send & Receive Messages

Use support to send customer messages. Listen on support for SATE (AI) replies and on customer_support for agent replies.
4

Join Conversation Room

After the first message returns a ticket_chat_id, call enter_support to subscribe to that conversation room for all subsequent broadcasts.
5

Mark Read

Call mark_conversation_read whenever the customer views messages.
6

Voice Calls (optional)

Use call_support to start a voice call with SATE and close_call_support to end it.
7

Close & Review

When resolved, call close_support to end the conversation and review_support to collect feedback.

Events Reference

Emitting Events (Client → Server)


Enter Customer Room

Subscribe the customer to their personal room. Call this immediately after connecting so the customer can receive agent messages (customer_support) and auto-close notifications (customer_support_closed) regardless of which conversation is active. Event Name: enter_customer_support
Request Schema
session_id
string
required
Unique identifier for the customer — use the same value you send in the support event
Example Request
socket.on('connect', () => {
  socket.emit('enter_customer_support', {
    session_id: 'customer_123'
  });
});
Response Schema
{
  "status_code": 200,
  "sid": "socket_session_id",
  "message": "Joined customer support room",
  "data": {}
}

Enter Conversation Room

Join a specific conversation room to receive real-time updates for that conversation. Event Name: enter_support
Call this after the first support response returns a ticket_chat_id. You only need to call it once per conversation.
Request Schema
ticket_chat_id
string
required
The conversation ID returned from the first support response
Example Request
socket.emit('enter_support', {
  ticket_chat_id: 'conv_abc123'
});
Response Schema
{
  "status_code": 200,
  "sid": "socket_session_id",
  "message": "Joined support room",
  "data": {}
}

Leave Conversation Room

Unsubscribe from a conversation room. Event Name: leave_support
Request Schema
ticket_chat_id
string
required
The conversation ID to leave
Example Request
socket.emit('leave_support', {
  ticket_chat_id: 'conv_abc123'
});
Response Schema
{
  "status_code": 200,
  "sid": "socket_session_id",
  "message": "Left support room",
  "data": {}
}

Send Message

The primary event for sending customer messages. SATE (AI) replies come back on the support event; agent replies come on customer_support. Event Name: support
Request Schema
session_id
string
required
Unique identifier for the customer
message
string
required
The message content to send
attachment
boolean
required
Whether this message includes an attachment
ticket_chat_id
string
Conversation ID. Set to null for the first message in a new conversation; use the returned ID for all follow-ups.
attachment_metadata
object | array
Attachment details. Required when attachment is true.
Example Request
// Start a new conversation
socket.emit('support', {
  session_id: 'customer_123',
  message: 'Hello, I need help with my order',
  attachment: false,
  ticket_chat_id: null
});

// Continue an existing conversation
socket.emit('support', {
  session_id: 'customer_123',
  message: 'My order number is #4567',
  attachment: false,
  ticket_chat_id: 'conv_abc123'
});
Response Schema
After sending, the server emits two separate events: 1. support_echo — Acknowledges that your message was received and queued:
{
  "status_code": 200,
  "sid": "socket_session_id",
  "message": "support_echo",
  "data": {}
}
2. support — The AI or agent reply, broadcast to the conversation room:
status_code
integer
200 for success
sid
string
Session identifier
message
string
Status message
data
object
Response data
data object:
message
string
The reply message from SATE or an agent
ticket_chat_id
string
Conversation ID — store this from the first response to continue the conversation
sender
string
Who sent the reply: "sate" or "agent"
attachment
boolean
Whether the reply includes an attachment
attachment_metadata
object | null
Attachment details if present
close_support
boolean
true when SATE signals that the issue appears resolved and the conversation can be closed
closed_support
boolean
true when the conversation has been confirmed closed
date_created
string
ISO 8601 timestamp
date_updated
string
ISO 8601 timestamp
Example Response
socket.on('support', (response) => {
  // {
  //   "status_code": 200,
  //   "sid": "socket_session_id",
  //   "message": "Message sent successfully",
  //   "data": {
  //     "message": "Happy to help! Could you share your order number?",
  //     "ticket_chat_id": "conv_abc123",
  //     "sender": "sate",
  //     "attachment": false,
  //     "attachment_metadata": null,
  //     "close_support": false,
  //     "closed_support": false,
  //     "date_created": "2024-01-15T10:31:00Z",
  //     "date_updated": "2024-01-15T10:31:00Z"
  //   }
  // }
});
When close_support is true but closed_support is false, SATE is suggesting the issue looks resolved. Ask the customer to confirm, then call close_support if they agree.
Important Notes
  • New conversations: Set ticket_chat_id to null. Save the ticket_chat_id from the response — you need it for every follow-up message.
  • Conversation limit: Each customer session can have a maximum of 3 open conversations at a time.
  • Attachments: Only available when the responder is "agent". SATE does not send attachments.

Mark Conversation Read

Mark all messages in a conversation as read by the customer. Event Name: mark_conversation_read
Request Schema
ticket_chat_id
string
required
The conversation ID to mark as read
entity
string
required
Who is marking as read. Always use "customer" for customer-side integrations.
Example Request
socket.emit('mark_conversation_read', {
  ticket_chat_id: 'conv_abc123',
  entity: 'customer'
});
Response Schema
{
  "status_code": 200,
  "sid": "socket_session_id",
  "message": "Marked as read",
  "data": {}
}

Start Voice Call

Initiate a voice call with SATE. The server responds with a LiveKit token you use to join the audio room. Event Name: call_support
Request Schema
session_id
string
required
Unique identifier for the customer
ticket_chat_id
string
Existing conversation ID. Set to null to start a new conversation via call.
Example Request
// Start a call on a new conversation
socket.emit('call_support', {
  session_id: 'customer_123',
  ticket_chat_id: null
});

// Start a call on an existing conversation
socket.emit('call_support', {
  session_id: 'customer_123',
  ticket_chat_id: 'conv_abc123'
});
Response Schema
The server broadcasts a call_support event to the conversation room with the call details:
token
string
LiveKit access token — use this with the LiveKit SDK to join the voice room
room
string
LiveKit room name (matches the ticket_chat_id)
ticket_chat_id
string
Conversation ID
ticket_call_id
string
Unique ID for this call
call_start_time
string
ISO 8601 timestamp of when the call started
Example Response
socket.on('call_support', (response) => {
  if (response.status_code === 200) {
    const { token, room } = response.data;
    // Connect to the voice room using the LiveKit client SDK
    connectToLiveKitRoom(token, room);
  }
});
Important Notes
  • Availability: Voice calls are only available while SATE is the active responder and no other call is in progress.
  • Voice room: Pass the token to the LiveKit client SDK to connect to the audio room.
  • Room name: The LiveKit room name is the same as ticket_chat_id.

End Voice Call

End an active voice call. Event Name: close_call_support
Request Schema
ticket_chat_id
string
required
The conversation ID for the active call
Example Request
socket.emit('close_call_support', {
  ticket_chat_id: 'conv_abc123'
});
Response Schema
The server broadcasts a close_call_support event to the conversation room:
room
string
LiveKit room name
ticket_chat_id
string
Conversation ID
ticket_call_id
string | null
Call ID — null if no active call was found
call_start_time
string
ISO 8601 call start timestamp
call_end_time
string
ISO 8601 call end timestamp
Example Response
socket.on('close_call_support', (response) => {
  if (response.status_code === 200) {
    const { call_start_time, call_end_time } = response.data;
    disconnectFromLiveKit();
    showCallSummary(call_start_time, call_end_time);
  }
});

Close Conversation

End a conversation when the issue is resolved. Event Name: close_support
Call this only after SATE sets close_support: true in a message and the customer confirms they want to end the conversation.
Request Schema
ticket_chat_id
string
required
The conversation ID to close
Example Request
socket.emit('close_support', {
  ticket_chat_id: 'conv_abc123'
});
Response Schema
{
  "status_code": 200,
  "sid": "socket_session_id",
  "message": "Support conversation closed",
  "data": {
    "message": "Thank you for using our support. Your conversation has been closed.",
    "ticket_chat_id": "conv_abc123",
    "sender": "sate",
    "attachment": false,
    "attachment_metadata": null,
    "close_support": true,
    "closed_support": true,
    "date_created": "2024-01-15T12:00:00Z",
    "date_updated": "2024-01-15T12:00:00Z"
  }
}

Review Support

Collect customer feedback after a conversation ends. Event Name: review_support
Request Schema
ticket_chat_id
string
required
The conversation ID to review
rating
string
required
Customer rating: "1", "2", "3", "4", or "5"
review
string
Optional customer feedback text
Example Request
socket.emit('review_support', {
  ticket_chat_id: 'conv_abc123',
  rating: '5',
  review: 'Great service, very helpful!'
});
Response Schema
{
  "status_code": 200,
  "sid": "socket_session_id",
  "message": "Review noted",
  "data": {
    "rating": "5",
    "review": "Great service, very helpful!",
    "ticket_chat_id": "conv_abc123"
  }
}

Server Broadcasts (Server → Client)

These are events the server pushes to your client. Set up listeners for them to handle real-time updates.

Agent Message Received

Fired when a human agent sends a reply. Agent messages arrive on this event — not on support — so you must listen on both. Listen On: customer_support
This event is delivered to the customer’s personal room (set up via enter_customer_support), so it works even before the customer has joined a specific conversation room.
Response Schema
message
string
Agent’s reply message
ticket_chat_id
string
Conversation ID
sender
string
Always "agent" for this event
attachment
boolean
Whether the message includes an attachment
attachment_metadata
object | null
Attachment details if present
close_support
boolean
Whether the agent is suggesting closure
closed_support
boolean
Whether the conversation is closed
date_created
string
ISO 8601 timestamp
date_updated
string
ISO 8601 timestamp
Example
socket.on('customer_support', (response) => {
  if (response.status_code === 200) {
    const { message, sender, ticket_chat_id } = response.data;
    displayMessage(message, sender);
    socket.emit('mark_conversation_read', {
      ticket_chat_id,
      entity: 'customer'
    });
  }
});

Conversation Auto-Closed

Fired when SATE closes a conversation automatically — without waiting for the customer to confirm. Update your UI to reflect the closed state. Listen On: customer_support_closed
Response Schema
ticket_id
string
The parent ticket ID
ticket_chat_id
string
The conversation ID that was closed
status
string
Updated ticket status
Example
socket.on('customer_support_closed', (response) => {
  if (response.status_code === 200) {
    const { ticket_chat_id } = response.data;
    showConversationClosed(ticket_chat_id);
  }
});

Handler Changed

Fired when a human agent takes over a conversation from SATE. Use this to show a notification like “You are now chatting with a human agent.” Listen On: support_handler_changed
Response Schema
ticket_chat_id
string
The conversation ID
handler
string
The new handler type: "agent"
agent
object
Details of the agent who took over
agent object:
id
string
Agent’s unique ID
username
string
Agent’s display name
Example
socket.on('support_handler_changed', (response) => {
  if (response.status_code === 200) {
    const { handler, agent } = response.data;
    if (handler === 'agent') {
      showNotification(`You're now chatting with ${agent.username}`);
    }
  }
});

SATE Left Call

Fired when SATE disconnects from an active voice call. Handle this to update your call UI and give the customer the option to end or wait. Listen On: sate_left_call
Response Schema
ticket_chat_id
string
The conversation ID
ticket_call_id
string
The call ID
Example
socket.on('sate_left_call', (response) => {
  if (response.status_code === 200) {
    disconnectFromLiveKit();
    showNotification('The call has ended');
  }
});

Implementation Example

A complete example showing the full conversation and call flow:
const API_BASE_URL = '{api_base_url}';
const socket = io(`${API_BASE_URL}/helpdesk?token_source=api&token=${API_KEY}`);

let currentConversationId = null;

// 1. On connect — immediately subscribe to the customer's personal room
socket.on('connect', () => {
  socket.emit('enter_customer_support', {
    session_id: CUSTOMER_SESSION_ID
  });
});

// 2. Send a message
function sendMessage(message, attachmentData = null) {
  socket.emit('support', {
    session_id: CUSTOMER_SESSION_ID,
    message: message,
    attachment: !!attachmentData,
    attachment_metadata: attachmentData,
    ticket_chat_id: currentConversationId
  });
}

// 3. Receive SATE replies
socket.on('support', (response) => {
  if (response.status_code === 200) {
    const { data } = response;

    if (!currentConversationId) {
      currentConversationId = data.ticket_chat_id;
      // Join the conversation room for all future broadcasts
      socket.emit('enter_support', { ticket_chat_id: currentConversationId });
    }

    displayMessage(data.message, data.sender);

    socket.emit('mark_conversation_read', {
      ticket_chat_id: currentConversationId,
      entity: 'customer'
    });

    // SATE suggests the issue is resolved — ask the customer to confirm
    if (data.close_support && !data.closed_support) {
      askCustomerToClose(currentConversationId);
    }
  }
});

// 4. Receive agent replies (separate event from SATE replies)
socket.on('customer_support', (response) => {
  if (response.status_code === 200) {
    displayMessage(response.data.message, response.data.sender);
    socket.emit('mark_conversation_read', {
      ticket_chat_id: response.data.ticket_chat_id,
      entity: 'customer'
    });
  }
});

// 5. Notify customer when a human agent takes over
socket.on('support_handler_changed', (response) => {
  if (response.status_code === 200 && response.data.handler === 'agent') {
    showBanner(`You're now chatting with ${response.data.agent.username}`);
  }
});

// 6. Handle auto-close by SATE
socket.on('customer_support_closed', (response) => {
  if (response.status_code === 200) {
    showConversationClosed(response.data.ticket_chat_id);
  }
});

// 7. Voice call
function startVoiceCall() {
  socket.emit('call_support', {
    session_id: CUSTOMER_SESSION_ID,
    ticket_chat_id: currentConversationId
  });
}

socket.on('call_support', (response) => {
  if (response.status_code === 200) {
    // Use the LiveKit SDK to connect to the voice room
    connectToLiveKitRoom(response.data.token, response.data.room);
  }
});

socket.on('sate_left_call', () => {
  disconnectFromLiveKit();
  showNotification('The call has ended');
});

function endVoiceCall() {
  socket.emit('close_call_support', {
    ticket_chat_id: currentConversationId
  });
}

socket.on('close_call_support', (response) => {
  if (response.status_code === 200) {
    disconnectFromLiveKit();
    showCallSummary(response.data.call_start_time, response.data.call_end_time);
  }
});

// 8. Customer confirms close
function closeConversation() {
  socket.emit('close_support', { ticket_chat_id: currentConversationId });
}

// 9. Collect review
function submitReview(rating, feedback) {
  socket.emit('review_support', {
    ticket_chat_id: currentConversationId,
    rating: rating,
    review: feedback
  });
}

Next Steps