Skip to main content

WebSocket Protocol

The OpenClaw Gateway WebSocket API provides real-time bidirectional communication for agent interactions, event streaming, and control plane operations.

Connection

Endpoint

ws://localhost:18789/
For secure connections:
wss://your-domain:18789/

Connection Flow

  1. Open WebSocket connection
  2. Send connect method with authentication
  3. Receive connect.challenge event (optional, for device pairing)
  4. Receive connection response
  5. Subscribe to events (optional)
  6. Invoke RPC methods

Example Connection

const ws = new WebSocket('ws://localhost:18789');

ws.onopen = () => {
  // Authenticate
  ws.send(JSON.stringify({
    jsonrpc: "2.0",
    id: 1,
    method: "connect",
    params: {
      role: "control",
      auth: {
        token: "your-token-here"
      },
      client: {
        name: "My Client",
        version: "1.0.0",
        platform: "web"
      }
    }
  }));
};

ws.onmessage = (event) => {
  const frame = JSON.parse(event.data);
  console.log('Received:', frame);
};

Message Format

All messages follow JSON-RPC 2.0 format.

Request Frame

{
  "jsonrpc": "2.0",
  "id": "unique-request-id",
  "method": "method.name",
  "params": {
    "key": "value"
  }
}
jsonrpc
string
required
Must be "2.0"
id
string | number
required
Unique request identifier for matching responses
method
string
required
RPC method name (e.g., "agent", "send", "sessions.list")
params
object
Method-specific parameters

Response Frame

{
  "id": "unique-request-id",
  "ok": true,
  "payload": {
    "result": "data"
  },
  "error": null
}
id
string | number
Matches request id
ok
boolean
true for success, false for errors
payload
object
Method-specific response data (when ok: true)
error
object
Error details (when ok: false)

Error Response

{
  "id": "unique-request-id",
  "ok": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable error message"
  }
}

Authentication

Authenticate during the initial connect call:
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "connect",
  "params": {
    "role": "control",
    "auth": {
      "token": "your-token-here"
    },
    "client": {
      "name": "My Client",
      "version": "1.0.0",
      "mode": "control",
      "platform": "web"
    }
  }
}
role
string
required
Client role: control, node, or webchat
auth
object
required
Authentication credentials
auth.token
string
Bearer token or device token
auth.password
string
Password (alternative to token)
client
object
required
Client metadata
See Authentication for details.

Event Streaming

The Gateway broadcasts events to subscribed clients.

Event Frame

{
  "event": "event.name",
  "payload": {
    "data": "value"
  }
}
event
string
Event name
payload
object
Event-specific data

Available Events

EventDescription
agentAgent response chunks (streaming)
chatChat session updates
presenceSystem presence changes
healthHealth status updates
tickGateway heartbeat (periodic)
heartbeatClient heartbeat (periodic)
talk.modeVoice mode changes
shutdownGateway shutdown notification
cronCron job status
node.pair.requestedDevice pairing request
node.pair.resolvedDevice pairing resolved
device.pair.requestedDevice pairing request
device.pair.resolvedDevice pairing resolved
voicewake.changedVoice wake triggers changed
exec.approval.requestedExecution approval requested
exec.approval.resolvedExecution approval resolved
update.availableSoftware update available
See Events for detailed event schemas.

Subscribing to Events

Events are automatically sent to all connected clients. No explicit subscription is required for most events. For session-specific events, use session subscriptions:
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "node.subscribe",
  "params": {
    "sessionKey": "session-key"
  }
}

Common Methods

Invoke Agent

{
  "jsonrpc": "2.0",
  "id": 10,
  "method": "agent",
  "params": {
    "message": "Hello, agent!",
    "sessionKey": "user:123",
    "runId": "unique-run-id"
  }
}
See Agent Protocol for details.

Send Message

{
  "jsonrpc": "2.0",
  "id": 11,
  "method": "send",
  "params": {
    "to": "+1234567890",
    "message": "Hello!",
    "channel": "signal",
    "idempotencyKey": "unique-key"
  }
}
See Messages for details.

List Sessions

{
  "jsonrpc": "2.0",
  "id": 12,
  "method": "sessions.list",
  "params": {}
}
See Sessions for details.

Heartbeat

The Gateway sends periodic tick events to keep connections alive:
{
  "event": "tick",
  "payload": {
    "timestamp": 1234567890000
  }
}
Clients should respond to heartbeats or implement connection keep-alive logic.

Connection Management

Reconnection

If the WebSocket connection drops:
  1. Wait a short delay (exponential backoff recommended)
  2. Re-establish WebSocket connection
  3. Re-send connect method
  4. Resume operations

Graceful Shutdown

The Gateway sends a shutdown event before closing:
{
  "event": "shutdown",
  "payload": {
    "reason": "Gateway is shutting down"
  }
}
Clients should close the connection and attempt reconnection after a delay.

Error Handling

Common Error Codes

CodeDescription
INVALID_REQUESTRequest validation failed
UNAUTHORIZEDAuthentication failed
METHOD_NOT_FOUNDUnknown RPC method
INTERNAL_ERRORServer error
RATE_LIMITEDRate limit exceeded

Example Error Response

{
  "id": 10,
  "ok": false,
  "error": {
    "code": "INVALID_REQUEST",
    "message": "Missing required parameter: message"
  }
}

Example: Complete Flow

const WebSocket = require('ws');

const ws = new WebSocket('ws://localhost:18789');
let requestId = 0;

function sendRequest(method, params) {
  const id = ++requestId;
  ws.send(JSON.stringify({
    jsonrpc: "2.0",
    id,
    method,
    params
  }));
  return id;
}

ws.on('open', () => {
  console.log('Connected');
  
  // Authenticate
  sendRequest('connect', {
    role: 'control',
    auth: { token: 'your-token' },
    client: { name: 'Example Client', version: '1.0.0' }
  });
});

ws.on('message', (data) => {
  const frame = JSON.parse(data);
  
  if (frame.event) {
    // Handle event
    console.log('Event:', frame.event, frame.payload);
  } else if (frame.id === 1) {
    // Connected
    console.log('Authenticated:', frame.ok);
    
    // Invoke agent
    sendRequest('agent', {
      message: 'Hello, world!',
      sessionKey: 'example:session',
      runId: 'run-123'
    });
  } else if (frame.id === 2) {
    // Agent response
    console.log('Agent result:', frame.payload);
  }
});

ws.on('close', () => {
  console.log('Disconnected');
});

Next Steps

Agent Protocol

Invoke agents via WebSocket

Events

Subscribe to Gateway events

Sessions

Manage agent sessions

Messages

Send messages via channels