Skip to main content

WebSocket API

The Chucky service uses WebSocket for real-time bidirectional communication. This document describes the low-level protocol for developers who want to build custom clients.
Most users should use the JavaScript SDK or Python SDK instead of the raw WebSocket API.

Connection

Endpoint

wss://conjure.chucky.cloud/ws?token=<JWT>

Authentication

Include your JWT token as a query parameter:
const ws = new WebSocket(`wss://conjure.chucky.cloud/ws?token=${token}`);
The token must be a valid JWT signed with your project’s HMAC secret. See Authentication for token creation.

Message Format

All messages are JSON-encoded with an envelope structure:
interface Envelope {
  type: string;
  payload: unknown;
}

Example

{
  "type": "sdk_message",
  "payload": {
    "type": "user",
    "message": "Hello, Claude!"
  }
}

Connection Lifecycle

1

Connect

Client opens WebSocket connection to wss://conjure.chucky.cloud/ws?token=<JWT>
2

Initialize

Client sends init message with session configuration
3

Ready

Server responds with control:ready when session is initialized
4

Communicate

Client sends sdk_message, server streams response chunks back
5

Result

Server sends result message when response is complete
6

Close

Client sends control:close to end session gracefully

Initialization

After connecting, send an init message to configure the session:
{
  "type": "init",
  "payload": {
    "model": "claude-sonnet-4-5-20250929",
    "systemPrompt": "You are a helpful assistant.",
    "maxTurns": 10
  }
}

Init Payload Options

FieldTypeDescription
modelstringClaude model to use
systemPromptstringSystem prompt
maxTurnsnumberMaximum conversation turns
toolsarrayTool definitions
mcpServersarrayMCP server definitions
allowedToolsstring[]Whitelist of tools
disallowedToolsstring[]Blacklist of tools

Keep-Alive

Send periodic ping messages to keep the connection alive:
{
  "type": "ping",
  "payload": {
    "timestamp": 1704067200000
  }
}
Server responds with:
{
  "type": "pong",
  "payload": {
    "timestamp": 1704067200000
  }
}
Recommended interval: 30-60 seconds.

Closing

To gracefully close a session:
{
  "type": "control",
  "payload": {
    "action": "close"
  }
}

Error Handling

Errors are sent as:
{
  "type": "error",
  "payload": {
    "code": "BUDGET_EXCEEDED",
    "message": "AI budget exceeded",
    "details": {
      "used": 1000000,
      "limit": 1000000
    }
  }
}

Error Codes

CodeDescription
AUTHENTICATION_ERRORInvalid or expired token
BUDGET_EXCEEDEDAI or compute budget exceeded
CONCURRENCY_LIMITToo many concurrent sessions
RATE_LIMITRate limit exceeded
SESSION_ERRORSession operation failed
VALIDATION_ERRORInvalid message format

Example: Minimal Client

const token = 'your-jwt-token';
const ws = new WebSocket(`wss://conjure.chucky.cloud/ws?token=${token}`);

ws.onopen = () => {
  // Initialize session
  ws.send(JSON.stringify({
    type: 'init',
    payload: {
      model: 'claude-sonnet-4-5-20250929',
    }
  }));
};

ws.onmessage = (event) => {
  const message = JSON.parse(event.data);

  switch (message.type) {
    case 'control':
      if (message.payload.action === 'ready') {
        // Session ready, send a message
        ws.send(JSON.stringify({
          type: 'sdk_message',
          payload: {
            type: 'user',
            message: 'Hello!'
          }
        }));
      }
      break;

    case 'sdk_message':
      // Handle streaming response
      console.log('Stream:', message.payload);
      break;

    case 'result':
      // Final result
      console.log('Result:', message.payload.text);
      break;

    case 'error':
      console.error('Error:', message.payload);
      break;
  }
};

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

ws.onclose = (event) => {
  console.log('Connection closed:', event.code, event.reason);
};