Skip to main content

Overview

The VoicePact WebSocket API enables real-time, bidirectional communication for contract status updates, payment notifications, and system events. Each client connects with a unique identifier and can receive both personal and broadcast messages.

Connection Endpoint

WebSocket URL

ws://your-domain.com/api/v1/ws/{client_id}
Path Parameters:
  • client_id (string, required): Unique identifier for the client connection, typically a phone number

Connecting to WebSocket

JavaScript Example

const clientId = "+1234567890";
const ws = new WebSocket(`ws://localhost:8000/api/v1/ws/${clientId}`);

ws.onopen = () => {
  console.log("Connected to VoicePact WebSocket");
  
  // Send a ping message
  ws.send(JSON.stringify({
    type: "ping",
    timestamp: Date.now()
  }));
};

ws.onmessage = (event) => {
  const message = JSON.parse(event.data);
  console.log("Received:", message);
  
  switch(message.type) {
    case "pong":
      console.log("Ping response received");
      break;
    case "contract_update":
      console.log(`Contract ${message.contract_id} status: ${message.status}`);
      break;
    case "payment_update":
      console.log(`Payment update for contract ${message.contract_id}`);
      break;
  }
};

ws.onerror = (error) => {
  console.error("WebSocket error:", error);
};

ws.onclose = () => {
  console.log("WebSocket connection closed");
};

Python Example

import asyncio
import json
import websockets

async def connect_to_voicepact():
    client_id = "+1234567890"
    uri = f"ws://localhost:8000/api/v1/ws/{client_id}"
    
    async with websockets.connect(uri) as websocket:
        print("Connected to VoicePact WebSocket")
        
        # Send a ping message
        await websocket.send(json.dumps({
            "type": "ping",
            "timestamp": int(time.time() * 1000)
        }))
        
        # Listen for messages
        async for message in websocket:
            data = json.loads(message)
            print(f"Received: {data}")
            
            if data["type"] == "pong":
                print("Ping response received")
            elif data["type"] == "contract_update":
                print(f"Contract {data['contract_id']} status: {data['status']}")
            elif data["type"] == "payment_update":
                print(f"Payment update: ${data['amount']}")

asyncio.run(connect_to_voicepact())

Message Types

Client-to-Server Messages

Ping

Check connection health and latency. Request:
{
  "type": "ping",
  "timestamp": 1234567890
}
Response:
{
  "type": "pong",
  "timestamp": 1234567890
}

Contract Status Request

Query the status of a specific contract. Request:
{
  "type": "contract_status",
  "contract_id": "contract_123"
}
Response:
{
  "type": "contract_status_response",
  "contract_id": "contract_123",
  "status": "active"
}

Server-to-Client Messages

Contract Update

Sent when a contract status changes. This message is sent only to parties involved in the contract. Payload:
{
  "type": "contract_update",
  "contract_id": "contract_123",
  "status": "active",
  "timestamp": "1234567890"
}
Fields:
  • type: Always "contract_update"
  • contract_id: Unique identifier of the contract
  • status: New status of the contract (e.g., “active”, “completed”, “terminated”)
  • timestamp: Unix timestamp (seconds) when the update occurred

Payment Update

Broadcast to all connected clients when a payment event occurs. Payload:
{
  "type": "payment_update",
  "contract_id": "contract_123",
  "status": "completed",
  "amount": 150.00,
  "timestamp": "1234567890"
}
Fields:
  • type: Always "payment_update"
  • contract_id: Contract associated with the payment
  • status: Payment status (e.g., “pending”, “completed”, “failed”)
  • amount: Payment amount in USD
  • timestamp: Unix timestamp (seconds) when the payment was processed

Connection Lifecycle

1. Connection Establishment

When a client connects, the server:
  • Accepts the WebSocket connection
  • Registers the client with the provided client_id
  • Logs the connection event

2. Active Connection

During an active connection:
  • Client can send messages to the server
  • Server can send personal messages to the specific client
  • Server can broadcast messages to all connected clients
  • Connection is maintained until explicitly closed or network failure

3. Disconnection

When a client disconnects:
  • Server removes the client from active connections
  • Logs the disconnection event
  • No further messages will be sent to that client

Error Handling

Invalid JSON

If the server receives malformed JSON, it logs an error but maintains the connection. The client will not receive a response for invalid messages.

Unknown Message Types

Messages with unrecognized type fields are logged but ignored. The connection remains active.

Connection Drops

If the WebSocket connection is interrupted:
  • Client should implement reconnection logic with exponential backoff
  • Use the same client_id to maintain consistency
  • Re-subscribe to any necessary event streams after reconnection

Best Practices

Send periodic ping messages to detect connection failures early and maintain connection health.
setInterval(() => {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({ type: "ping", timestamp: Date.now() }));
  }
}, 30000); // Every 30 seconds
Implement automatic reconnection with exponential backoff to handle temporary network issues.
function connectWithRetry(clientId, maxRetries = 5) {
  let retries = 0;
  
  function connect() {
    const ws = new WebSocket(`ws://localhost:8000/api/v1/ws/${clientId}`);
    
    ws.onclose = () => {
      if (retries < maxRetries) {
        const delay = Math.min(1000 * Math.pow(2, retries), 30000);
        console.log(`Reconnecting in ${delay}ms...`);
        setTimeout(connect, delay);
        retries++;
      }
    };
    
    ws.onopen = () => {
      retries = 0; // Reset on successful connection
    };
    
    return ws;
  }
  
  return connect();
}
Always validate incoming messages before processing to prevent errors from unexpected payloads.
ws.onmessage = (event) => {
  try {
    const message = JSON.parse(event.data);
    
    if (!message.type) {
      console.warn("Message missing type field");
      return;
    }
    
    // Process message...
  } catch (error) {
    console.error("Failed to parse message:", error);
  }
};
Each connection should have a unique client_id. In VoicePact, phone numbers are typically used as client identifiers to ensure users receive updates specific to their contracts.

Rate Limits

The WebSocket API currently does not enforce rate limits, but clients should avoid sending excessive messages to prevent server overload.

Security Considerations

The current implementation does not include authentication. In production environments, implement:
  • Token-based authentication during WebSocket handshake
  • Message encryption for sensitive data
  • Client ID validation against authorized users
  • Rate limiting per connection

Next Steps

REST API

Explore the REST API for contract and payment operations

Webhooks

Set up webhooks for server-to-server notifications

Build docs developers (and LLMs) love