- Add capabilities negotiation to Features list - Document connection flow (5 steps) - Add capabilities_response message format - Add request_capabilities message format - Update JavaScript example with capabilities handling - Update Python example with capabilities handling
WebSocket Channel Plugin for OpenClaw
Custom WebSocket-based messaging channel plugin for OpenClaw. Enables custom messaging platforms to connect via WebSocket with configurable capabilities and API key authentication.
Features
- 🔐 API Key Authentication - Secure connections via
X-API-Keyheader or query parameter - 🏢 Multi-tenant - Support for multiple accounts with unique API keys
- ⚙️ Configurable Capabilities - Per-account configuration for:
- Text messages
- Images
- Files
- Voice messages
- Reactions
- Threads
- Typing indicators
- 🔄 Capabilities Negotiation - Clients report their capabilities on connect
- 🛡️ DM Security - Three policies:
allowlist,open,closed - 🔗 Threading - Full support for threaded conversations
- 🤝 Pairing Flow - Text-based pairing with code exchange
- 📊 CLI Commands - Management via
openclaw websocket statusandopenclaw websocket accounts
Installation
Local Development
# Plugin is already in extensions/websocket-channel
# OpenClaw will auto-load it
From Git
git clone https://git.azimkin.dev/Azimkin/openclaw-websocket-channel.git
cd openclaw-websocket-channel
openclaw plugins install ./openclaw-websocket-channel
Configuration
Add to your openclaw.json:
{
"channels": {
"websocket": {
"port": 8765,
"path": "/ws",
"accounts": [
{
"accountId": "default",
"apiKey": "sk_test_your_api_key_here",
"allowFrom": ["user-123", "user-456"],
"dmPolicy": "allowlist",
"capabilities": {
"text": true,
"images": true,
"files": false,
"voice": false,
"reactions": false,
"threads": true,
"typing": true
},
"metadata": {
"serverName": "My WebSocket Server",
"region": "eu-west"
}
}
]
}
}
}
WebSocket Protocol
Connection
ws://localhost:8765/ws?key=YOUR_API_KEY
Or with header:
const ws = new WebSocket('ws://localhost:8765/ws', {
headers: { 'X-API-Key': 'YOUR_API_KEY' }
});
Connection Flow
- Client connects → Server authenticates via API key
- Server sends handshake → Server capabilities (maximum allowed)
- Server requests capabilities → Asks client what it supports
- Client responds → Client capabilities + metadata
- Effective capabilities → Server uses intersection of both
- Messaging begins → Only supported message types allowed
Message Format
Client → Server
Capabilities Response (required after handshake):
{
"type": "capabilities_response",
"capabilities": {
"text": true,
"images": true,
"files": false,
"voice": false,
"reactions": false,
"threads": true,
"typing": true
},
"metadata": {
"clientName": "My App",
"version": "1.0.0",
"platform": "custom"
}
}
Text Message:
{
"type": "message",
"from": "user-123",
"content": {
"type": "text",
"text": "Hello, bot!"
}
}
Server → Client
Handshake:
{
"type": "handshake",
"accountId": "default",
"capabilities": {
"text": true,
"images": true,
"files": true,
"voice": true,
"reactions": false,
"threads": true,
"typing": true
},
"metadata": {
"serverName": "My WebSocket Server"
},
"timestamp": 1703275200000
}
Capabilities Request:
{
"type": "request_capabilities"
}
Message:
{
"type": "message",
"to": "user-123",
"content": {
"type": "text",
"text": "Hello! How can I help?"
},
"messageId": "msg_abc123",
"timestamp": 1703275200000
}
Client Examples
JavaScript
import WebSocket from 'ws';
const ws = new WebSocket('ws://localhost:8765/ws?key=YOUR_API_KEY');
ws.on('message', (data) => {
const msg = JSON.parse(data.toString());
switch (msg.type) {
case 'handshake':
console.log('Server capabilities:', msg.capabilities);
break;
case 'request_capabilities':
// Send client capabilities
ws.send(JSON.stringify({
type: 'capabilities_response',
capabilities: {
text: true,
images: true,
files: false,
voice: false,
reactions: false,
threads: true,
typing: true
},
metadata: {
clientName: 'My App',
version: '1.0.0'
}
}));
break;
case 'message':
console.log('Bot:', msg.content.text);
break;
}
});
// Send message (after capabilities_response)
ws.send(JSON.stringify({
type: 'message',
from: 'user-123',
content: { type: 'text', text: 'Hello!' }
}));
Python
import websocket
import json
def on_message(ws, message):
data = json.loads(message)
if data['type'] == 'handshake':
print(f"Server capabilities: {data['capabilities']}")
elif data['type'] == 'request_capabilities':
# Send client capabilities
ws.send(json.dumps({
"type": "capabilities_response",
"capabilities": {
"text": True,
"images": True,
"files": False,
"voice": False,
"reactions": False,
"threads": True,
"typing": True
},
"metadata": {
"clientName": "My Python App",
"version": "1.0.0"
}
}))
elif data['type'] == 'message':
print(f"Bot: {data['content']['text']}")
ws = websocket.WebSocketApp(
"ws://localhost:8765/ws?key=YOUR_API_KEY",
on_message=on_message
)
ws.run_forever()
CLI Commands
# Show WebSocket status
openclaw websocket status
# List configured accounts
openclaw websocket accounts
Testing
# Install wscat
npm install -g wscat
# Connect
wscat -c "ws://localhost:8765/ws?key=YOUR_API_KEY"
# Send message
> {"type":"message","from":"user-123","content":{"type":"text","text":"Hello!"}}
Documentation
- Client Specification - Full protocol documentation
- Plugin Specification - Development spec
- OpenClaw Docs - General OpenClaw documentation
Development
Project Structure
websocket-channel/
├── package.json # Package metadata
├── openclaw.plugin.json # Plugin manifest
├── index.ts # Main entry point
├── setup-entry.ts # Setup entry point
├── api.ts # Public API exports
└── src/
├── config.ts # Types and defaults
├── auth.ts # Authentication utilities
├── websocket-server.ts # WebSocket server
├── client.ts # Connection handler
├── channel.ts # Channel plugin config
└── channel.test.ts # Test suite
Running Tests
cd extensions/websocket-channel
pnpm test
License
MIT
Support
- Issues: https://git.azimkin.dev/Azimkin/openclaw-websocket-channel/issues
- OpenClaw Discord: https://discord.com/invite/clawd
Description
WebSocket channel plugin for OpenClaw - enables custom messaging platforms to connect via WebSocket
Languages
TypeScript
100%