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.

WebSocket URL: wss://conjure.lautrek.com/api/v1/adapter/ws

Connection Flow

Adapters follow a three-step connection process:

1

Connect with API Key

Establish WebSocket connection with API key in Authorization header or query parameter.

2

Register Adapter

Send registration message with adapter type, capabilities, and unique ID.

3

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"
);
Security Note: If authentication fails, the connection will be closed with code 4001.

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"
}
Recommendation: Send heartbeats every 30 seconds. Connections without heartbeat for 90 seconds are considered stale.

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.

Next Steps