PingBack’s real-time layer is built on Socket.IO with a Redis backplane. All live features — new messages, typing indicators, delivery receipts, and agent status — flow through this connection.
Socket.IO supports both WebSocket and long-polling transports. PingBack uses WebSocket by default and falls back to polling automatically when WebSocket is unavailable.
Connect
Connect to https://api.pingback.live and pass your business_id in the auth handshake object. PingBack rejects connections that do not include a business_id.
import { io } from "socket.io-client";
const socket = io("https://api.pingback.live", {
auth: {
business_id: "YOUR_BUSINESS_ID",
},
transports: ["websocket", "polling"],
});
socket.on("connect", () => {
console.log("Connected:", socket.id);
});
Room system
PingBack uses rooms to scope events to the right audience. Join a room to start receiving its events.
| Room | Purpose |
|---|
business_{business_id} | Org-wide events for your entire workspace. |
conversation_{conversation_id} | Events scoped to a single conversation. |
To join a conversation room, emit join_conversation:
socket.emit("join_conversation", { conversation_id: 42 });
Events to listen for (server → client)
These events are emitted by the server. Listen for them in your client.
new_message
Fires when any participant in the conversation sends a message.
Payload:
| Field | Type | Description |
|---|
id | number | Message ID. |
content | string | Message text (or file URL for attachments). |
sender_type | string | "customer" or "agent". |
conversation_id | number | The conversation this message belongs to. |
created_at | string | ISO 8601 timestamp. |
typing
Fires when a participant starts or stops typing.
Payload: { conversation_id, user_id, is_typing }
delivered
Fires when a message is marked as delivered.
Payload: { message_id, delivered_at }
read
Fires when a message is marked as read.
Payload: { message_id, read_at }
agent_status_changed
Fires when an agent’s online status changes.
Payload: { agent_id, status }
conversation_assigned
Fires when a conversation is assigned to an agent.
Payload: { conversation_id, assigned_by }
Events to emit (client → server)
These events are sent from your client to PingBack.
join_conversation
Join a conversation room to receive its events.
Payload: { conversation_id: number }
client_message
Send a message from the customer widget. PingBack creates a new conversation automatically if conversation_id is omitted.
Payload:
| Field | Type | Required | Description |
|---|
content | string | Yes | The message text. |
conversation_id | number | No | Include to continue an existing conversation. |
customer_data | object | Yes | { name: string, email: string } — identifies the customer. |
typing
Broadcast a typing indicator to all participants in the conversation.
Payload: { conversation_id: number, user_id: number, is_typing: boolean }
Complete integration example
The example below shows how to connect, join a conversation, send messages, and handle incoming events in a customer-facing widget.
import { io } from "socket.io-client";
const socket = io("https://api.pingback.live", {
auth: { business_id: "YOUR_BUSINESS_ID" },
transports: ["websocket", "polling"],
});
// Track the active conversation
let conversationId = null;
// Join an existing conversation (if resuming a session)
function joinConversation(id) {
conversationId = id;
socket.emit("join_conversation", { conversation_id: id });
}
// Send a message from the customer
function sendMessage(text) {
socket.emit("client_message", {
content: text,
conversation_id: conversationId, // omit to start a new conversation
customer_data: {
name: "Jane Smith",
email: "jane@example.com",
},
});
}
// Send a typing indicator
function setTyping(isTyping) {
socket.emit("typing", {
conversation_id: conversationId,
user_id: "customer",
is_typing: isTyping,
});
}
// Listen for new messages
socket.on("new_message", (message) => {
console.log(`[${message.sender_type}] ${message.content}`);
// Update conversationId from the first reply
if (!conversationId) {
conversationId = message.conversation_id;
}
});
// Listen for typing indicators
socket.on("typing", ({ user_id, is_typing }) => {
console.log(`User ${user_id} is ${is_typing ? "typing..." : "done typing"}`);
});
// Listen for delivery and read receipts
socket.on("delivered", ({ message_id, delivered_at }) => {
console.log(`Message ${message_id} delivered at ${delivered_at}`);
});
socket.on("read", ({ message_id, read_at }) => {
console.log(`Message ${message_id} read at ${read_at}`);
});