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:
+-----------------------------------------------------------------------+
| event | Real-time event notification with resource data |
| ack | Acknowledgment of subscription changes |
| error | Error message for failed operations |
+-----------------------------------------------------------------------+
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>. Use*for the resource_id to match all resources of a type.
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.resource_type(enum string): Type of resource:call,message,activeflow,conference,queue,agent,recording,transcription.resource_id(UUID or*): UUID of a specific resource, or*to match all resources of the given type.
Valid Scopes
Scope |
Permission Required |
|---|---|
customer_id |
Admin or Manager permission for the customer |
agent_id |
Must be the owner of the agent |
Valid Resource Types
Resource Type |
Events |
|---|---|
call |
call.created, call.status, call.ended |
message |
message.received, message.sent, message.delivery |
activeflow |
activeflow.updated, activeflow.completed |
conference |
conference.joined, conference.left, conference.ended |
queue |
queue.joined, queue.connected, queue.left |
agent |
agent.status |
recording |
recording.started, recording.completed |
transcription |
transcription.completed |
Wildcard Usage
+-----------------------------------------------------------------------+
| Wildcard Patterns |
+-----------------------------------------------------------------------+
Specific resource:
customer_id:abc123:call:xyz789
-> Only events for call xyz789
All resources of type:
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.
Structure
{
"event_type": "<event-type>",
"timestamp": "<ISO-8601-timestamp>",
"topic": "<topic-that-matched>",
"data": {
// Resource-specific payload
}
}
Fields
event_type(enum string): Type of event (e.g.,"call.status","message.received"). See WebSocket Overview for the full list.timestamp(string, ISO 8601): When the event occurred, in UTC format.topic(String): The topic pattern that triggered this event. Matches one of your subscribed topic patterns.data(Object): Resource-specific data payload. Structure varies byevent_typeand corresponds to the relevant resource struct (e.g., Call for call events).
Note
AI Implementation Hint
Use the event_type field (not type) to determine the event kind for server-pushed events. The type field is used only for client-to-server messages (subscribe/unsubscribe) and server acknowledgments (ack/error). Always check for both type and event_type in your message handler to handle all message categories.
Event Type Reference
Complete list of event types and their data structures.
Call Events
// call.created
{
"event_type": "call.created",
"timestamp": "2024-01-15T10:30:00.000000Z",
"topic": "customer_id:abc123:call:xyz789",
"data": {
"id": "xyz789",
"customer_id": "abc123",
"direction": "inbound",
"source": {
"type": "tel",
"target": "+15551234567"
},
"destination": {
"type": "tel",
"target": "+15559876543"
},
"status": "ringing",
"tm_create": "2024-01-15T10:30:00.000000Z"
}
}
// call.status
{
"event_type": "call.status",
"timestamp": "2024-01-15T10:30:05.000000Z",
"topic": "customer_id:abc123:call:xyz789",
"data": {
"id": "xyz789",
"status": "answered",
"previous_status": "ringing",
"tm_update": "2024-01-15T10:30:05.000000Z"
}
}
Message Events
// message.received
{
"event_type": "message.received",
"timestamp": "2024-01-15T10:30:00.000000Z",
"topic": "customer_id:abc123:message:msg789",
"data": {
"id": "msg789",
"customer_id": "abc123",
"direction": "inbound",
"source": {
"type": "tel",
"target": "+15551234567"
},
"destination": {
"type": "tel",
"target": "+15559876543"
},
"text": "Hello, I need help with my order",
"tm_create": "2024-01-15T10:30:00.000000Z"
}
}
// message.delivery
{
"event_type": "message.delivery",
"timestamp": "2024-01-15T10:30:02.000000Z",
"topic": "customer_id:abc123:message:msg789",
"data": {
"id": "msg789",
"status": "delivered",
"tm_update": "2024-01-15T10:30:02.000000Z"
}
}
Activeflow Events
// activeflow.updated
{
"event_type": "activeflow.updated",
"timestamp": "2024-01-15T10:30:00.000000Z",
"topic": "customer_id:abc123:activeflow:flow789",
"data": {
"id": "flow789",
"flow_id": "template123",
"status": "executing",
"current_action": {
"id": "action456",
"type": "play",
"name": "welcome_message"
},
"tm_update": "2024-01-15T10:30:00.000000Z"
}
}
// activeflow.completed
{
"event_type": "activeflow.completed",
"timestamp": "2024-01-15T10:35:00.000000Z",
"topic": "customer_id:abc123:activeflow:flow789",
"data": {
"id": "flow789",
"flow_id": "template123",
"status": "completed",
"result": "success",
"tm_end": "2024-01-15T10:35:00.000000Z"
}
}
Queue Events
// queue.joined
{
"event_type": "queue.joined",
"timestamp": "2024-01-15T10:30:00.000000Z",
"topic": "customer_id:abc123:queue:queue789",
"data": {
"queue_id": "queue789",
"call_id": "call456",
"position": 3,
"estimated_wait": 120,
"tm_join": "2024-01-15T10:30:00.000000Z"
}
}
// queue.connected
{
"event_type": "queue.connected",
"timestamp": "2024-01-15T10:32:00.000000Z",
"topic": "customer_id:abc123:queue:queue789",
"data": {
"queue_id": "queue789",
"call_id": "call456",
"agent_id": "agent123",
"wait_time": 120,
"tm_connect": "2024-01-15T10:32:00.000000Z"
}
}
Conference Events
// conference.joined
{
"event_type": "conference.joined",
"timestamp": "2024-01-15T10:30:00.000000Z",
"topic": "customer_id:abc123:conference:conf789",
"data": {
"conference_id": "conf789",
"call_id": "call456",
"participant_count": 3,
"tm_join": "2024-01-15T10:30:00.000000Z"
}
}
// conference.left
{
"event_type": "conference.left",
"timestamp": "2024-01-15T10:45:00.000000Z",
"topic": "customer_id:abc123:conference:conf789",
"data": {
"conference_id": "conf789",
"call_id": "call456",
"participant_count": 2,
"reason": "hangup",
"tm_leave": "2024-01-15T10:45:00.000000Z"
}
}
Agent Events
// agent.status
{
"event_type": "agent.status",
"timestamp": "2024-01-15T10:30:00.000000Z",
"topic": "agent_id:agent123:agent:agent123",
"data": {
"agent_id": "agent123",
"status": "available",
"previous_status": "busy",
"tm_update": "2024-01-15T10:30:00.000000Z"
}
}
Recording Events
// recording.completed
{
"event_type": "recording.completed",
"timestamp": "2024-01-15T10:45:00.000000Z",
"topic": "customer_id:abc123:recording:rec789",
"data": {
"id": "rec789",
"call_id": "call456",
"duration": 300,
"format": "wav",
"size": 2400000,
"reference_url": "https://storage.voipbin.net/recordings/rec789.wav",
"tm_complete": "2024-01-15T10:45:00.000000Z"
}
}
Acknowledgment Messages
Server may send acknowledgments for subscription operations.
Success Acknowledgment
{
"type": "ack",
"action": "subscribe",
"topics": [
"customer_id:abc123:call:*"
],
"status": "success"
}
Error Response
{
"type": "error",
"action": "subscribe",
"topics": [
"customer_id:abc123:call:*"
],
"code": "PERMISSION_DENIED",
"message": "You do not have permission to subscribe to this topic"
}
Error Codes
Code |
Description |
|---|---|
PERMISSION_DENIED |
User lacks permission for the topic |
INVALID_TOPIC |
Topic format is invalid |
INVALID_MESSAGE |
Message structure is invalid |
RATE_LIMITED |
Too many subscription requests |
Message Handling Examples
Code examples for processing WebSocket messages.
JavaScript
ws.onmessage = function(event) {
const message = JSON.parse(event.data);
switch(message.type || message.event_type) {
case 'ack':
console.log('Subscription confirmed:', message.topics);
break;
case 'error':
console.error('Error:', message.code, message.message);
break;
case 'call.status':
handleCallStatus(message.data);
break;
case 'message.received':
handleMessage(message.data);
break;
default:
console.log('Received:', message.event_type, message.data);
}
};
Python
def on_message(ws, raw_message):
message = json.loads(raw_message)
msg_type = message.get('type') or message.get('event_type')
if msg_type == 'ack':
print(f"Subscription confirmed: {message['topics']}")
elif msg_type == 'error':
print(f"Error: {message['code']} - {message['message']}")
elif msg_type == 'call.status':
handle_call_status(message['data'])
elif msg_type == 'message.received':
handle_message(message['data'])
else:
print(f"Received: {msg_type}")