Structures¶
This section documents the message structures used in VoIPBin’s WebSocket API for subscription management and event delivery.
Message Overview¶
WebSocket communication uses JSON messages for all operations.
Message Categories
+-----------------------------------------------------------------------+
| WebSocket Message Types |
+-----------------------------------------------------------------------+
Client -> Server:
+-----------------------------------------------------------------------+
| subscribe | Register for event notifications |
| unsubscribe | Stop receiving event notifications |
+-----------------------------------------------------------------------+
Server -> Client:
+-----------------------------------------------------------------------+
| (resource payload with | Real-time event notification. Pushed as |
| event_type key) | the resource payload with one added |
| | key, event_type -- see "Event Message |
| | Structure" below. |
+-----------------------------------------------------------------------+
Note
AI Implementation Hint
There is no server-to-client acknowledgment or error message for subscribe/unsubscribe requests. If a subscribed topic fails permission validation, the server closes the WebSocket connection instead of sending an error frame.
Subscribe Message¶
Send a subscribe message to receive events for specific topics.
Structure
{
"type": "subscribe",
"topics": [
"<topic-pattern-1>",
"<topic-pattern-2>",
...
]
}
Fields
type(enum string): Must be"subscribe".topics(Array of String): List of topic patterns to subscribe to. Each topic follows the format<scope>:<scope_id>:<resource_type>:<resource_id>. Omit theresource_idsegment (and its leading colon) to match all resources of a type – see Topic Pattern Structure below.
Example: Subscribe to all calls
{
"type": "subscribe",
"topics": [
"customer_id:12345678-1234-1234-1234-123456789012:call"
]
}
Example: Subscribe to multiple resource types
{
"type": "subscribe",
"topics": [
"customer_id:12345678-1234-1234-1234-123456789012:call",
"customer_id:12345678-1234-1234-1234-123456789012:message",
"customer_id:12345678-1234-1234-1234-123456789012:activeflow"
]
}
Example: Subscribe to specific resource
{
"type": "subscribe",
"topics": [
"customer_id:12345678-1234-1234-1234-123456789012:call:a1b2c3d4-e5f6-7890-abcd-ef1234567890"
]
}
Example: Agent-level subscription
{
"type": "subscribe",
"topics": [
"agent_id:98765432-4321-4321-4321-210987654321:queue",
"agent_id:98765432-4321-4321-4321-210987654321:call"
]
}
Unsubscribe Message¶
Send an unsubscribe message to stop receiving events for specific topics.
Structure
{
"type": "unsubscribe",
"topics": [
"<topic-pattern-1>",
"<topic-pattern-2>",
...
]
}
Fields
type(enum string): Must be"unsubscribe".topics(Array of String): List of topic patterns to unsubscribe from. Must match the exact topic patterns used during subscription.
Example: Unsubscribe from calls
{
"type": "unsubscribe",
"topics": [
"customer_id:12345678-1234-1234-1234-123456789012:call"
]
}
Example: Unsubscribe from specific resource
{
"type": "unsubscribe",
"topics": [
"customer_id:12345678-1234-1234-1234-123456789012:call:a1b2c3d4-e5f6-7890-abcd-ef1234567890"
]
}
Topic Pattern Structure¶
Topics follow a consistent format for event filtering.
Topic Format
<scope>:<scope_id>:<resource_type>:<resource_id>
Topic Components
scope(enum string): Access level. Either"customer_id"or"agent_id".scope_id(UUID): The UUID of the customer or agent. Obtained fromGET /customersorGET /agents. Foragent_idscope, this must match the authenticated agent’s own ID.resource_type(String): Type of resource, derived from the first underscore-delimited segment of the resource’s webhook event type (e.g.callfromcall_created,activeflowfromactiveflow_updated,queuecallfromqueuecall_created). See Webhook Struct for the full list of event types.resource_id(UUID, optional): UUID of a specific resource. Omit this segment (and its leading colon) to match every event of the given resource type – see “Matching All Resources of a Type” below.
Note
AI Implementation Hint
There is no * wildcard character. Matching is prefix-based: the server delivers an event to a subscription whenever the event’s topic string starts with the subscribed topic string. To subscribe to a single resource, use the full 4-part topic with a real UUID. To subscribe to every resource of a type, subscribe with just the 3-part prefix (no trailing resource ID).
Valid Scopes
Scope |
Permission Required |
|---|---|
customer_id |
Admin or Manager permission for the customer (non-Direct tokens) |
agent_id |
|
Common Resource Types
Resource Type |
Example Events (see Webhook Struct for the full list) |
|---|---|
call |
call_created, call_ringing, call_progressing, call_updated, call_hangup |
message |
message_created, message_updated |
activeflow |
activeflow_created, activeflow_updated, activeflow_deleted |
queue |
queue_created, queue_updated, queue_deleted |
queuecall |
queuecall_created, queuecall_connecting, queuecall_serviced, queuecall_done, queuecall_abandoned |
agent |
agent_created, agent_updated, agent_status_updated |
chat |
chat_created, chatmessage_created, chatparticipant_added |
conversation |
conversation_created, conversation_updated, conversation_deleted, conversation_message_created |
Matching All Resources of a Type
+-----------------------------------------------------------------------+
| Prefix-Based Topic Matching |
+-----------------------------------------------------------------------+
Specific resource:
customer_id:abc123:call:xyz789
-> Only events for call xyz789
All resources of type (omit the resource_id segment):
customer_id:abc123:call
-> All call events for customer abc123
Multiple types (separate subscriptions):
customer_id:abc123:call
customer_id:abc123:message
-> All calls AND all messages
Event Message Structure¶
Events are pushed from the server when subscribed resources change. The pushed text frame is the resource payload itself (the same fields as the corresponding webhook data) with one added top-level key, event_type, naming the event. There is no topic/timestamp envelope and no data wrapper. See Event Message Format.
Structure
{
"event_type": "<event-type>",
// Resource-specific payload fields at the top level, e.g. the Call struct fields for call_* events
}
Fields
event_type(enum string): The event type (e.g.,"call_created","chatparticipant_removed","conversation_message_created"). See Webhook Struct for the full list.All other keys: the resource’s own fields. A resource field named
typeis a resource field, not the event type.
Note
AI Implementation Hint
Branch on event_type, not on the shape of the payload. Frames do not carry a topic or timestamp field – use the resource’s own tm_create/tm_update fields for timing, and its own id/customer_id/owner_id fields to correlate the event. A client that subscribes with an overlapping prefix (for example both customer_id:<id>:chat and agent_id:<id>:chat) can receive the same event more than once, so make event handlers idempotent by resource id.
Event Type Reference¶
The WebSocket event payloads contain the same fields as the corresponding webhook data plus event_type. Below is one representative example per resource type; see Webhook Struct for the complete, authoritative list of event types and full field sets.
Call Events
// call_created
{
"event_type": "call_created",
"id": "5371e9db-d035-4db6-a8d6-0994d33e744e",
"customer_id": "5e4a0680-804e-11ec-8477-2fea5968d85b",
"status": "ringing",
"direction": "incoming",
"tm_create": "2022-04-11 00:23:53.636000",
"tm_update": "9999-01-01 00:00:00.000000"
}
// call_hangup
{
"event_type": "call_hangup",
"id": "5371e9db-d035-4db6-a8d6-0994d33e744e",
"status": "terminated",
"hangup_by": "remote",
"hangup_reason": "normal",
"tm_hangup": "2022-04-11 00:25:10.000000"
}
Activeflow Events
// activeflow_updated
{
"event_type": "activeflow_updated",
"id": "flow789",
"customer_id": "5e4a0680-804e-11ec-8477-2fea5968d85b",
"tm_update": "2024-01-15 10:30:00.000000"
}
Queue Events
// queuecall_created
{
"event_type": "queuecall_created",
"id": "queuecall789",
"customer_id": "5e4a0680-804e-11ec-8477-2fea5968d85b",
"queue_id": "queue789",
"reference_id": "call456",
"tm_create": "2024-01-15 10:30:00.000000"
}
Agent Events
// agent_status_updated
{
"event_type": "agent_status_updated",
"id": "agent123",
"customer_id": "5e4a0680-804e-11ec-8477-2fea5968d85b",
"status": "available",
"tm_update": "2024-01-15 10:30:00.000000"
}
Talk (Chat) Events
// chatmessage_created
{
"event_type": "chatmessage_created",
"id": "a3c5e8f2-4d3a-4b5c-9e7f-1a2b3c4d5e6f",
"customer_id": "5e4a0680-804e-11ec-8477-2fea5968d85b",
"chat_id": "e8b2e976-f043-44c8-bb89-e214e225e813",
"text": "Hello team!",
"tm_create": "2024-01-17 10:32:00.000000"
}
Acknowledgment Messages¶
The server does not send acknowledgment or error messages for subscribe/unsubscribe requests. There is no ack or error message type in the current implementation.
If a subscribed topic fails permission validation (e.g., a customer_id that does not match the caller’s own customer, or an agent_id that is not the authenticated agent), the server closes the WebSocket connection outright rather than returning an error frame. Design your client to detect an unexpected close shortly after sending a subscribe message as a signal of a permission or format problem, and to reconnect with corrected topics.
Message Handling Examples¶
Code examples for processing WebSocket messages. Server-pushed events are the resource payload plus an event_type key, so a switch on event_type distinguishes them. The server never echoes subscribe/unsubscribe messages.
JavaScript
ws.onmessage = function(event) {
const message = JSON.parse(event.data);
switch(message.event_type) {
case 'call_created':
case 'call_ringing':
case 'call_progressing':
case 'call_updated':
case 'call_hangup':
handleCallEvent(message.event_type, message);
break;
case 'message_created':
handleMessage(message);
break;
default:
console.log('Received:', message.event_type, message);
}
};
Python
def on_message(ws, raw_message):
message = json.loads(raw_message)
msg_type = message.get('event_type')
if msg_type and msg_type.startswith('call_'):
handle_call_event(msg_type, message)
elif msg_type == 'message_created':
handle_message(message)
else:
print(f"Received: {msg_type}")