WebSocket Protocol
Real-time communication protocol for CAD adapters.
Overview
The Conjure WebSocket protocol enables real-time bidirectional communication between CAD adapters (FreeCAD, Fusion 360) and the Conjure server. Adapters connect to receive commands from AI assistants and send results back instantly.
wss://conjure.lautrek.com/api/v1/adapter/ws
Connection Flow
Adapters follow a three-step connection process:
Connect with API Key
Establish WebSocket connection with API key in Authorization header or query parameter.
Register Adapter
Send registration message with adapter type, capabilities, and unique ID.
Message Loop
Receive commands, execute operations, and send results. Maintain connection with heartbeats.
Authentication
Provide your API key in one of two ways:
Option 1: Authorization Header
const ws = new WebSocket(
"wss://conjure.lautrek.com/api/v1/adapter/ws",
{
headers: {
"Authorization": "Bearer conjure_live_your_api_key"
}
}
);
Option 2: Query Parameter
const ws = new WebSocket(
"wss://conjure.lautrek.com/api/v1/adapter/ws?api_key=conjure_live_your_api_key"
);
Message Types
1. Adapter Registration
First message after connection. Identifies the adapter and its capabilities.
Client → Server
{
"type": "adapter_registration",
"adapter_type": "freecad", // or "fusion360"
"adapter_id": "adapter_unique_id_123",
"capabilities": [
"primitives",
"booleans",
"transforms",
"modifiers",
"queries"
]
}
Server → Client
{
"type": "registration_confirmed",
"adapter_id": "adapter_unique_id_123"
}
2. Command Execution
Server sends commands to adapter for execution in CAD software.
Server → Client
{
"type": "execute_command",
"command_id": "cmd_abc123",
"command": "create_box",
"parameters": {
"length": 50.0,
"width": 30.0,
"height": 20.0,
"position": [0, 0, 0]
},
"timeout_ms": 30000
}
Client → Server (Success)
{
"type": "command_result",
"command_id": "cmd_abc123",
"success": true,
"result": {
"object_id": "Box001",
"type": "Part::Box",
"bounding_box": {
"min": [0, 0, 0],
"max": [50, 30, 20]
}
},
"execution_time_ms": 45.2
}
Client → Server (Error)
{
"type": "command_result",
"command_id": "cmd_abc123",
"success": false,
"error": "Invalid parameter: length must be positive",
"error_code": "INVALID_PARAMETER",
"execution_time_ms": 12.1
}
3. Heartbeat
Adapters send periodic heartbeats to maintain connection health.
Client → Server
{
"type": "heartbeat",
"timestamp": "2025-01-15T10:35:00Z"
}
Server → Client
{
"type": "heartbeat_ack",
"timestamp": "2025-01-15T10:35:00Z"
}
Connection States
Connecting
Initial WebSocket connection established, awaiting registration.
Active
Registered and ready to receive commands. Heartbeats are current.
Idle
No commands being executed. Connection maintained with heartbeats.
Busy
Currently executing a command. Additional commands are queued.
Disconnected
Connection lost. Adapter should attempt reconnection with exponential backoff.
Error Codes
4001
Authentication Failed
Missing or invalid API key. Connection closed immediately.
4002
Registration Required
First message must be adapter_registration. Connection closed.
1000
Normal Closure
Adapter or server initiated graceful shutdown.
Example Implementation
Python example using websockets library:
import asyncio
import json
import websockets
API_KEY = "conjure_live_your_api_key"
WS_URL = "wss://conjure.lautrek.com/api/v1/adapter/ws"
async def connect_adapter():
# Connect with API key
uri = f"{WS_URL}?api_key={API_KEY}"
async with websockets.connect(uri) as websocket:
# Register adapter
registration = {
"type": "adapter_registration",
"adapter_type": "freecad",
"adapter_id": "my_adapter_123",
"capabilities": ["primitives", "booleans"]
}
await websocket.send(json.dumps(registration))
# Wait for confirmation
response = await websocket.recv()
data = json.loads(response)
print(f"Registered: {data}")
# Message loop
while True:
try:
message = await websocket.recv()
data = json.loads(message)
if data["type"] == "execute_command":
# Execute command in CAD software
result = execute_command(
data["command"],
data["parameters"]
)
# Send result back
response = {
"type": "command_result",
"command_id": data["command_id"],
"success": True,
"result": result
}
await websocket.send(json.dumps(response))
except websockets.exceptions.ConnectionClosed:
print("Connection closed")
break
asyncio.run(connect_adapter())
Best Practices
Implement Reconnection Logic
Use exponential backoff (1s, 2s, 4s, 8s, max 60s) when reconnecting after connection loss to avoid overwhelming the server.
Handle Timeouts Gracefully
Commands have a default 30-second timeout. If execution takes longer, send intermediate progress updates or request timeout extension.
Send Regular Heartbeats
Heartbeat every 30 seconds to maintain connection health. Connections without heartbeat for 90+ seconds are automatically disconnected.
Queue Commands Locally
If adapter is busy, queue incoming commands locally rather than rejecting them. Process queue when current command completes.
Include Detailed Error Information
When command execution fails, include error_code, error message, and any relevant context to help AI assistants understand what went wrong.