Documentation Index
Fetch the complete documentation index at: https://docs.burki.dev/llms.txt
Use this file to discover all available pages before exploring further.
WebSocket Endpoint
Endpoint: wss://api.burki.dev/ws/campaigns/{campaign_id}/progress?token={api_key}
This WebSocket endpoint provides real-time streaming of campaign progress updates. Use it to build live monitoring dashboards without polling.
Path Parameters
campaign_id (integer, required): The ID of the campaign to monitor
Authentication
Required: All connections must be authenticated using a valid API key.
Pass your API key as a query parameter:
wss://api.burki.dev/ws/campaigns/42/progress?token=your_api_key
Connection Process
- Connect: Establish WebSocket connection with campaign ID and token
- Receive Acknowledgment: Server confirms connection
- Get Initial Data: Receive current progress state
- Stream Updates: Receive real-time progress updates
- Handle Events: Process contact completions, failures, etc.
Server-to-Client Messages
Connection Acknowledgment
Sent immediately after connection is established:
{
"type": "connection_ack",
"campaign_id": 42,
"timestamp": 1707984000.123
}
Initial Progress
Current campaign state sent after connection:
{
"type": "initial_progress",
"data": {
"campaign_id": 42,
"status": "running",
"progress": {
"total_contacts": 500,
"completed": 325,
"failed": 25,
"processing": 5,
"pending": 145,
"progress_percentage": 70.0,
"success_rate": 92.86
},
"started_at": "2024-02-15T09:00:00Z",
"completed_at": null,
"recent_activity": []
}
}
Progress Update
Sent whenever progress changes (contact completed, failed, etc.):
{
"type": "progress_update",
"data": {
"campaign_id": 42,
"status": "running",
"progress": {
"total_contacts": 500,
"completed": 326,
"failed": 25,
"processing": 4,
"pending": 145,
"progress_percentage": 70.2,
"success_rate": 92.88
},
"started_at": "2024-02-15T09:00:00Z",
"completed_at": null
}
}
Metrics History
Response to request_history client message:
{
"type": "metrics_history",
"data": [
{
"timestamp": "2024-02-15T09:00:00Z",
"completed": 0,
"failed": 0,
"success_rate": 0
},
{
"timestamp": "2024-02-15T10:00:00Z",
"completed": 150,
"failed": 10,
"success_rate": 93.75
}
]
}
Pong Response
Response to ping health check:
Client-to-Server Messages
Ping (Health Check)
Send periodically to maintain connection:
Request Update
Request current progress on demand:
{
"type": "request_update"
}
Request History
Request historical metrics (last N hours):
{
"type": "request_history",
"hours": 24
}
Progress Fields
| Field | Type | Description |
|---|
total_contacts | integer | Total contacts in campaign |
completed | integer | Successfully contacted |
failed | integer | Failed contact attempts |
processing | integer | Currently being contacted |
pending | integer | Waiting to be contacted |
progress_percentage | float | Completion percentage |
success_rate | float | Success rate percentage |
Campaign Status Values
| Status | Description |
|---|
draft | Not yet started |
scheduled | Waiting for scheduled time |
running | Actively executing |
paused | Temporarily paused |
completed | All contacts processed |
cancelled | Stopped before completion |
failed | Campaign error |
Example Usage
JavaScript
const campaignId = 42;
const apiKey = "your_api_key_here";
const ws = new WebSocket(`wss://api.burki.dev/ws/campaigns/${campaignId}/progress?token=${apiKey}`);
ws.onopen = function() {
console.log("Connected to campaign progress stream");
// Set up ping interval to keep connection alive
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: "ping" }));
}
}, 30000);
};
ws.onmessage = function(event) {
const message = JSON.parse(event.data);
switch (message.type) {
case "connection_ack":
console.log("Connection confirmed for campaign", message.campaign_id);
break;
case "initial_progress":
case "progress_update":
const progress = message.data.progress;
console.log(`Progress: ${progress.completed}/${progress.total_contacts} ` +
`(${progress.progress_percentage.toFixed(1)}%)`);
console.log(`Status: ${message.data.status}`);
// Update your UI here
updateProgressBar(progress.progress_percentage);
updateStats(progress);
// Check if campaign completed
if (message.data.status === "completed") {
console.log("Campaign completed!");
}
break;
case "pong":
// Connection is healthy
break;
default:
console.log("Unknown message type:", message.type);
}
};
ws.onerror = function(error) {
console.error("WebSocket error:", error);
};
ws.onclose = function(event) {
console.log("Connection closed:", event.code, event.reason);
// Implement reconnection logic here
};
// Request historical metrics
function requestHistory() {
ws.send(JSON.stringify({ type: "request_history", hours: 24 }));
}
import asyncio
import websockets
import json
async def monitor_campaign(campaign_id: int, api_key: str):
"""Monitor campaign progress via WebSocket."""
uri = f"wss://api.burki.dev/ws/campaigns/{campaign_id}/progress?token={api_key}"
async with websockets.connect(uri) as websocket:
print(f"Connected to campaign {campaign_id}")
# Start ping task to keep connection alive
async def ping_task():
while True:
await asyncio.sleep(30)
await websocket.send(json.dumps({"type": "ping"}))
ping = asyncio.create_task(ping_task())
try:
async for message in websocket:
data = json.loads(message)
if data["type"] == "connection_ack":
print(f"Connection confirmed for campaign {data['campaign_id']}")
elif data["type"] in ["initial_progress", "progress_update"]:
progress = data["data"]["progress"]
status = data["data"]["status"]
print(f"Status: {status}")
print(f"Progress: {progress['completed']}/{progress['total_contacts']} "
f"({progress['progress_percentage']:.1f}%)")
print(f"Success rate: {progress['success_rate']:.1f}%")
print("---")
if status == "completed":
print("Campaign completed!")
break
elif data["type"] == "pong":
pass # Connection healthy
finally:
ping.cancel()
# Run the monitor
asyncio.run(monitor_campaign(42, "your_api_key_here"))
React Hook Example
import { useEffect, useState, useCallback, useRef } from 'react';
function useCampaignProgress(campaignId, apiKey) {
const [progress, setProgress] = useState(null);
const [status, setStatus] = useState('disconnected');
const [error, setError] = useState(null);
const wsRef = useRef(null);
const connect = useCallback(() => {
const ws = new WebSocket(
`wss://api.burki.dev/ws/campaigns/${campaignId}/progress?token=${apiKey}`
);
ws.onopen = () => {
setStatus('connected');
setError(null);
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.type === 'initial_progress' || message.type === 'progress_update') {
setProgress(message.data);
}
};
ws.onerror = () => {
setError('Connection error');
setStatus('error');
};
ws.onclose = () => {
setStatus('disconnected');
// Reconnect after 5 seconds
setTimeout(connect, 5000);
};
wsRef.current = ws;
// Ping every 30 seconds
const pingInterval = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'ping' }));
}
}, 30000);
return () => {
clearInterval(pingInterval);
ws.close();
};
}, [campaignId, apiKey]);
useEffect(() => {
const cleanup = connect();
return cleanup;
}, [connect]);
return { progress, status, error };
}
// Usage in component
function CampaignDashboard({ campaignId, apiKey }) {
const { progress, status, error } = useCampaignProgress(campaignId, apiKey);
if (status === 'disconnected') return <div>Connecting...</div>;
if (error) return <div>Error: {error}</div>;
if (!progress) return <div>Loading...</div>;
return (
<div>
<h2>Campaign Progress</h2>
<p>Status: {progress.status}</p>
<p>Progress: {progress.progress.completed}/{progress.progress.total_contacts}</p>
<progress
value={progress.progress.progress_percentage}
max="100"
/>
<p>Success Rate: {progress.progress.success_rate.toFixed(1)}%</p>
</div>
);
}
Connection Management
Reconnection Strategy
Implement exponential backoff for reconnection:
let reconnectAttempts = 0;
const maxReconnectAttempts = 10;
function reconnect() {
if (reconnectAttempts >= maxReconnectAttempts) {
console.error("Max reconnection attempts reached");
return;
}
const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), 30000);
reconnectAttempts++;
console.log(`Reconnecting in ${delay}ms (attempt ${reconnectAttempts})`);
setTimeout(connect, delay);
}
Keep-Alive
Send ping messages every 30 seconds to maintain the connection:
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: "ping" }));
}
}, 30000);
Error Handling
Close Codes
| Code | Description |
|---|
| 1000 | Normal closure |
| 1001 | Going away |
| 1008 | Authentication failed |
| 4000 | Internal server error |
| 4004 | Campaign not found |
Authentication Errors
If authentication fails, the connection is immediately closed with code 1008:
ws.onclose = function(event) {
if (event.code === 1008) {
console.error("Authentication failed - check your API key");
}
};
Best Practices
- Always authenticate: Include valid API key in query parameter
- Implement ping/pong: Send pings every 30 seconds to keep connection alive
- Handle reconnection: Implement exponential backoff for reconnection
- Process all message types: Handle connection_ack, initial_progress, progress_update
- Monitor connection state: Track open/close events for UI feedback
- Clean up on unmount: Close WebSocket when component unmounts
Multi-Campaign Monitoring
To monitor multiple campaigns, use the campaigns monitor endpoint:
Endpoint: wss://api.burki.dev/ws/campaigns/monitor?token={api_key}
This provides an overview of all campaigns in your organization with the ability to subscribe to specific campaigns.