WebSockets
WebSocket Connection
WebSockets provide real-time, bidirectional communication between your client and EnvoyX. Unlike webhooks (server-to-server), WebSockets are ideal for web and mobile applications that need instant updates.
When to Use WebSockets
Use WebSockets when:
- Building real-time dashboards
- Creating web/mobile apps with live updates
- Need instant feedback on invoice processing
- Want to avoid polling
Use Webhooks when:
- Building server-to-server integrations
- Need guaranteed delivery with retries
- Processing events asynchronously
- Don't need bidirectional communication
Connection
Endpoint
wss://staging-api.tryenvoyx.com/api/v1/wsAuthentication
Authenticate with your API key via query parameter:
wss://staging-api.tryenvoyx.com/api/v1/ws?api_key=YOUR_API_KEYClient Examples
// Browser or Node.js (with ws package)
const apiKey = 'YOUR_API_KEY'
const ws = new WebSocket(`wss://staging-api.tryenvoyx.com/api/v1/ws?api_key=${apiKey}`)
ws.onopen = () => {
console.log('Connected to EnvoyX WebSocket')
// Send ping every 30 seconds to keep connection alive
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send('ping')
}
}, 30000)
}
ws.onmessage = (event) => {
const data = JSON.parse(event.data)
console.log('Event received:', data)
// Handle different event types
switch (data.event) {
case 'invoice.processed':
handleInvoiceProcessed(data.data)
break
case 'invoice.flagged':
handleInvoiceFlagged(data.data)
break
case 'pong':
console.log('Pong received')
break
default:
console.log('Unknown event:', data.event)
}
}
ws.onerror = (error) => {
console.error('WebSocket error:', error)
}
ws.onclose = (event) => {
console.log('WebSocket closed:', event.code, event.reason)
// Implement reconnection logic
setTimeout(() => {
console.log('Reconnecting...')
connectWebSocket()
}, 5000)
}
// Graceful shutdown
window.addEventListener('beforeunload', () => {
ws.close(1000, 'Client disconnecting')
})import asyncio
import websockets
import json
async def connect_websocket(api_key):
uri = f"wss://staging-api.tryenvoyx.com/api/v1/ws?api_key={api_key}"
async with websockets.connect(uri) as websocket:
print("Connected to EnvoyX WebSocket")
# Send ping every 30 seconds
async def send_ping():
while True:
await asyncio.sleep(30)
await websocket.send("ping")
# Start ping task
ping_task = asyncio.create_task(send_ping())
try:
# Receive messages
async for message in websocket:
data = json.loads(message)
print(f"Event received: {data['event']}")
# Handle different event types
if data['event'] == 'invoice.processed':
handle_invoice_processed(data['data'])
elif data['event'] == 'invoice.flagged':
handle_invoice_flagged(data['data'])
except websockets.exceptions.ConnectionClosed:
print("WebSocket closed")
ping_task.cancel()
# Run
asyncio.run(connect_websocket("YOUR_API_KEY"))package main
import (
"encoding/json"
"log"
"time"
"github.com/gorilla/websocket"
)
type Event struct {
Event string `json:"event"`
Data map[string]interface{} `json:"data"`
}
func main() {
apiKey := "YOUR_API_KEY"
url := "wss://staging-api.tryenvoyx.com/api/v1/ws?api_key=" + apiKey
conn, _, err := websocket.DefaultDialer.Dial(url, nil)
if err != nil {
log.Fatal("WebSocket connection error:", err)
}
defer conn.Close()
log.Println("Connected to EnvoyX WebSocket")
// Send ping every 30 seconds
go func() {
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
for range ticker.C {
if err := conn.WriteMessage(websocket.TextMessage, []byte("ping")); err != nil {
log.Println("Ping error:", err)
return
}
}
}()
// Receive messages
for {
_, message, err := conn.ReadMessage()
if err != nil {
log.Println("Read error:", err)
break
}
var event Event
if err := json.Unmarshal(message, &event); err != nil {
log.Println("JSON parse error:", err)
continue
}
log.Printf("Event received: %s\n", event.Event)
switch event.Event {
case "invoice.processed":
handleInvoiceProcessed(event.Data)
case "invoice.flagged":
handleInvoiceFlagged(event.Data)
}
}
}Event Format
All WebSocket messages follow the same format as webhook payloads:
{
"event": "invoice.processed",
"timestamp": "2024-02-10T12:00:00Z",
"data": {
"id": "inv_clx1234567890",
"status": "PROCESSED",
"extracted_data": {
"claim_number": "CLM-2024-001",
"total_amount": 250.00
}
}
}See Webhook Events for full event documentation.
Connection Lifecycle
1. Connect
const ws = new WebSocket('wss://staging-api.tryenvoyx.com/api/v1/ws?api_key=YOUR_API_KEY')2. Keep Alive
Send ping every 30 seconds to prevent timeout:
setInterval(() => {
ws.send('ping')
}, 30000)Server responds with pong:
{
"event": "pong",
"timestamp": "2024-02-10T12:00:00Z"
}3. Receive Events
All invoice events are automatically sent to your WebSocket connection.
4. Disconnect
Close gracefully:
ws.close(1000, 'Client disconnecting')Reconnection Strategy
Implement exponential backoff for reconnections:
let reconnectAttempts = 0
const maxReconnectAttempts = 10
function connectWebSocket(apiKey) {
const ws = new WebSocket(`wss://staging-api.tryenvoyx.com/api/v1/ws?api_key=${apiKey}`)
ws.onopen = () => {
console.log('Connected')
reconnectAttempts = 0 // Reset on successful connection
}
ws.onclose = (event) => {
if (reconnectAttempts < maxReconnectAttempts) {
const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), 30000)
reconnectAttempts++
console.log(`Reconnecting in ${delay}ms (attempt ${reconnectAttempts})`)
setTimeout(() => {
connectWebSocket(apiKey)
}, delay)
} else {
console.error('Max reconnection attempts reached')
}
}
}Error Handling
Connection Errors
ws.onerror = (error) => {
console.error('WebSocket error:', error)
// Check common issues:
// - Invalid or expired API key
// - Network connectivity
// - Server unavailable
}Authentication Errors
If authentication fails, the server closes the connection with code 4401:
ws.onclose = (event) => {
if (event.code === 4401) {
console.error('Authentication failed - API key invalid or expired')
// Check your API key and try again
}
}Close Codes
| Code | Reason | Action |
|---|---|---|
1000 | Normal closure | No action needed |
1001 | Going away | Reconnect if needed |
1006 | Abnormal closure | Reconnect with backoff |
4401 | Unauthorized | Check API key |
4429 | Rate limited | Wait before reconnecting |
Rate Limiting
WebSocket connections have the following limits:
- Max Connections: 5 simultaneous connections per API key
- Message Rate: 100 messages per minute
- Connection Rate: 10 new connections per minute
Exceeding rate limits will result in connection closure with code 4429.
Best Practices
1. Implement Heartbeat
Always send ping messages to keep the connection alive:
const pingInterval = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send('ping')
}
}, 30000)
ws.onclose = () => {
clearInterval(pingInterval)
}2. Handle Reconnection
Network issues are common. Always implement reconnection logic with exponential backoff.
3. Graceful Shutdown
Always close connections properly:
window.addEventListener('beforeunload', () => {
ws.close(1000, 'Client disconnecting')
})4. Message Queuing
Handle offline scenarios by queuing actions:
const messageQueue = []
function sendMessage(message) {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(message))
} else {
messageQueue.push(message)
}
}
ws.onopen = () => {
// Send queued messages
while (messageQueue.length > 0) {
ws.send(JSON.stringify(messageQueue.shift()))
}
}React Hook Example
import { useEffect, useState } from 'react'
function useEnvoyXWebSocket(apiKey) {
const [events, setEvents] = useState([])
const [connected, setConnected] = useState(false)
useEffect(() => {
if (!apiKey) return
const ws = new WebSocket(
`wss://staging-api.tryenvoyx.com/api/v1/ws?api_key=${apiKey}`
)
ws.onopen = () => {
setConnected(true)
// Heartbeat
const pingInterval = setInterval(() => {
ws.send('ping')
}, 30000)
ws.pingInterval = pingInterval
}
ws.onmessage = (event) => {
const data = JSON.parse(event.data)
setEvents((prev) => [...prev, data])
}
ws.onclose = () => {
setConnected(false)
clearInterval(ws.pingInterval)
}
return () => {
ws.close(1000)
}
}, [apiKey])
return { events, connected }
}
// Usage
function Dashboard({ apiKey }) {
const { events, connected } = useEnvoyXWebSocket(apiKey)
return (
<div>
<p>Status: {connected ? 'Connected' : 'Disconnected'}</p>
<ul>
{events.map((event, i) => (
<li key={i}>{event.event}: {event.data.id}</li>
))}
</ul>
</div>
)
}Comparison: WebSocket vs Webhooks
| Feature | WebSocket | Webhooks |
|---|---|---|
| Connection | Persistent, bidirectional | Request per event |
| Latency | Instant (<100ms) | 1-2 seconds |
| Client Type | Web/mobile apps | Servers |
| Reliability | Requires reconnection logic | Built-in retries |
| Scalability | 5 connections/key | Unlimited |
| Best For | Real-time dashboards | Server integrations |
Next Steps
- Set up webhooks for server-side event handling
- View all event types
- Learn about authentication