OpenClaw Multi-Channel AI Assistant
Skill by ara.so — Hermes Skills collection.
OpenClaw is a self-hosted multi-channel AI assistant platform that connects Web control UI, chat channels (WhatsApp, Telegram, Discord, Slack, Signal, Feishu, etc.), nodes, tools, and AI models through a unified Gateway architecture. This skill covers installation, configuration, channel setup, AI provider integration, plugin development, and troubleshooting.
What OpenClaw Does
- Unified Gateway: Central control plane that routes messages between channels, nodes, and AI agents
- Multi-Channel Support: 20+ chat platforms including WhatsApp, Telegram, Discord, Slack, Signal, iMessage, Feishu, Teams, Matrix
- Multi-Model Support: Anthropic Claude, OpenAI GPT, DeepSeek, Qwen, Kimi, GLM, Ollama, and more
- Tool System: Browser automation, code execution, skills, sub-agents
- Plugin Architecture: Hooks, adapters, and extensible agent framework
- Mobile Nodes: Remote execution nodes for distributed AI workflows
- Web UI: Browser-based control panel for configuration and monitoring
Installation
Quick Start (Docker)
# Clone the repository
git clone https://github.com/openclaw/openclaw.git
cd openclaw
# Copy environment template
cp .env.example .env
# Edit .env with your configuration
# Required: OPENAI_API_KEY or ANTHROPIC_API_KEY
nano .env
# Start with Docker Compose
docker-compose up -d
# Access Web UI at http://localhost:3000
Node.js Installation
# Prerequisites: Node.js 18+ and npm
git clone https://github.com/openclaw/openclaw.git
cd openclaw
# Install dependencies
npm install
# Configure environment
cp .env.example .env
# Edit .env with API keys and settings
# Start Gateway
npm run gateway:start
# Start Web UI (separate terminal)
npm run web:start
# Start a channel adapter (example: Telegram)
npm run channel:telegram
Cloud Deployment
# Deploy to VPS (Ubuntu/Debian)
curl -fsSL https://get.openclaw.ai | bash
# Or use the official installer
wget https://github.com/openclaw/openclaw/releases/latest/download/install.sh
chmod +x install.sh
./install.sh
# Follow prompts to configure Gateway, channels, and models
Core Architecture
Gateway Configuration
The Gateway is the central hub. Configure in config/gateway.yaml:
gateway:
host: 0.0.0.0
port: 3000
secret: ${GATEWAY_SECRET}
channels:
- type: telegram
enabled: true
token: ${TELEGRAM_BOT_TOKEN}
- type: whatsapp
enabled: true
provider: baileys # or twilio
- type: discord
enabled: true
token: ${DISCORD_BOT_TOKEN}
models:
- provider: anthropic
model: claude-3-5-sonnet-20241022
apiKey: ${ANTHROPIC_API_KEY}
- provider: openai
model: gpt-4-turbo
apiKey: ${OPENAI_API_KEY}
- provider: ollama
model: llama3
baseURL: http://localhost:11434
agent:
defaultModel: claude-3-5-sonnet-20241022
temperature: 0.7
maxTokens: 4096
contextWindow: 200000
memory:
provider: sqlite # or postgres, redis
retentionDays: 30
tools:
browser: true
codeExecution: false # requires sandboxed environment
webSearch: true
Environment Variables
Create .env file:
# Gateway
GATEWAY_SECRET=your-secure-random-secret
GATEWAY_PORT=3000
# AI Providers
ANTHROPIC_API_KEY=sk-ant-xxxxx
OPENAI_API_KEY=sk-xxxxx
DEEPSEEK_API_KEY=sk-xxxxx
# Channels - Telegram
TELEGRAM_BOT_TOKEN=123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
# Channels - Discord
DISCORD_BOT_TOKEN=MTk4NjIy...
# Channels - WhatsApp (Baileys)
WHATSAPP_SESSION_PATH=./sessions/whatsapp
# Database
DATABASE_URL=postgresql://user:pass@localhost:5432/openclaw
# or
DATABASE_URL=sqlite:./data/openclaw.db
# Memory
MEMORY_PROVIDER=sqlite
MEMORY_RETENTION_DAYS=30
# Tools
ENABLE_BROWSER_TOOL=true
ENABLE_CODE_EXECUTION=false
SEARXNG_URL=http://localhost:8080
CLI Commands
Gateway Management
# Start Gateway server
npm run gateway:start
# or
./openclaw gateway start
# Check Gateway status
./openclaw gateway status
# Reload configuration without restart
./openclaw gateway reload
# View Gateway logs
./openclaw gateway logs --tail 100
Channel Management
# List available channels
./openclaw channels list
# Start a specific channel adapter
./openclaw channel start telegram
./openclaw channel start whatsapp
./openclaw channel start discord
# Stop a channel
./openclaw channel stop telegram
# Channel authentication (WhatsApp)
./openclaw channel auth whatsapp
# Scan QR code with WhatsApp mobile app
Node Management
# Register a new node
./openclaw node register --name "mobile-node-1" --type mobile
# List active nodes
./openclaw node list
# Start a node
./openclaw node start mobile-node-1
# Node health check
./openclaw node health mobile-node-1
Plugin Management
# List installed plugins
./openclaw plugin list
# Install a plugin
./openclaw plugin install @openclaw/plugin-calendar
# Create new plugin scaffold
./openclaw plugin create my-custom-plugin
# Enable/disable plugin
./openclaw plugin enable my-custom-plugin
./openclaw plugin disable my-custom-plugin
Channel Adapter Implementation
Creating a Custom Channel Adapter
// plugins/channels/my-channel/index.js
import { ChannelAdapter } from '@openclaw/core';
export default class MyChannelAdapter extends ChannelAdapter {
constructor(config) {
super(config);
this.client = null;
}
async initialize() {
// Initialize your channel client
this.client = new MyChannelClient({
token: this.config.token,
apiUrl: this.config.apiUrl
});
// Set up incoming message handler
this.client.on('message', this.handleIncomingMessage.bind(this));
await this.client.connect();
this.logger.info('MyChannel adapter initialized');
}
async handleIncomingMessage(rawMessage) {
// Normalize to OpenClaw message format
const message = {
id: rawMessage.id,
channelType: 'my-channel',
channelUserId: rawMessage.sender.id,
text: rawMessage.content,
timestamp: rawMessage.timestamp,
metadata: {
chatId: rawMessage.chat.id,
messageType: rawMessage.type
}
};
// Route to Gateway for Agent processing
await this.routeToAgent(message);
}
async sendMessage(sessionKey, response) {
// Extract channel-specific routing info
const { chatId } = this.extractRoutingInfo(sessionKey);
// Send through channel API
await this.client.sendMessage({
chatId,
text: response.text,
attachments: response.attachments
});
}
async cleanup() {
if (this.client) {
await this.client.disconnect();
}
}
}
// Plugin manifest
export const manifest = {
name: 'my-channel',
version: '1.0.0',
type: 'channel-adapter',
requiredConfig: ['token'],
optionalConfig: ['apiUrl']
};
Register Channel in Gateway
// config/channels.js
import MyChannelAdapter from './plugins/channels/my-channel/index.js';
export const channelRegistry = {
telegram: TelegramAdapter,
whatsapp: WhatsAppAdapter,
discord: DiscordAdapter,
'my-channel': MyChannelAdapter
};
Agent Configuration & Tools
Custom Agent Prompt
// config/agent.js
export const agentConfig = {
systemPrompt: `You are a helpful AI assistant with access to various tools.
Available tools:
- browser: Navigate websites and extract information
- webSearch: Search the internet using SearXNG
- executeCode: Run Python code (sandboxed)
Guidelines:
- Be concise but helpful
- Always cite sources when using web search
- Ask for confirmation before executing code
- Respect user privacy`,
model: 'claude-3-5-sonnet-20241022',
temperature: 0.7,
maxTokens: 4096,
// Tool configuration
tools: {
browser: {
enabled: true,
timeout: 30000,
userAgent: 'OpenClaw/1.0'
},
webSearch: {
enabled: true,
searxngUrl: process.env.SEARXNG_URL,
maxResults: 5
},
codeExecution: {
enabled: false, // Requires secure sandbox
languages: ['python', 'javascript'],
timeout: 10000
}
},
// Memory configuration
memory: {
enabled: true,
contextWindow: 50, // Last 50 messages
summarization: true,
summaryThreshold: 30
}
};
Implementing a Custom Tool
// plugins/tools/weather-tool.js
import { Tool } from '@openclaw/core';
export default class WeatherTool extends Tool {
constructor() {
super({
name: 'getWeather',
description: 'Get current weather for a location',
parameters: {
type: 'object',
properties: {
location: {
type: 'string',
description: 'City name or coordinates'
},
units: {
type: 'string',
enum: ['metric', 'imperial'],
default: 'metric'
}
},
required: ['location']
}
});
}
async execute({ location, units = 'metric' }) {
const apiKey = process.env.WEATHER_API_KEY;
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${location}&units=${units}&appid=${apiKey}`
);
if (!response.ok) {
throw new Error(`Weather API error: ${response.statusText}`);
}
const data = await response.json();
return {
location: data.name,
temperature: data.main.temp,
condition: data.weather[0].description,
humidity: data.main.humidity,
units
};
}
}
// Register tool
import { registerTool } from '@openclaw/core';
registerTool(new WeatherTool());
Agent with Tool Usage
// Example: Agent processing with tool invocation
import { Agent } from '@openclaw/core';
const agent = new Agent({
model: 'claude-3-5-sonnet-20241022',
apiKey: process.env.ANTHROPIC_API_KEY,
tools: ['getWeather', 'webSearch', 'browser']
});
async function processUserMessage(message, sessionKey) {
// Load conversation context
const context = await agent.getContext(sessionKey);
// Process with tool support
const response = await agent.process({
messages: [...context, { role: 'user', content: message }],
sessionKey,
toolChoice: 'auto' // Let agent decide when to use tools
});
// Response includes tool calls if any were made
if (response.toolCalls) {
console.log('Tools used:', response.toolCalls.map(t => t.name));
}
// Save to memory
await agent.saveContext(sessionKey, response);
return response.content;
}
Session & Context Management
Session Key Format
OpenClaw uses session keys to route conversations:
// Format: channel:channelUserId:chatId
const sessionKey = 'telegram:123456789:987654321';
const sessionKey = 'whatsapp:+1234567890:group123';
const sessionKey = 'discord:user123:channel456';
// Parse session key
function parseSessionKey(sessionKey) {
const [channel, userId, chatId] = sessionKey.split(':');
return { channel, userId, chatId };
}
// Create session key
function createSessionKey(channel, userId, chatId) {
return `${channel}:${userId}:${chatId || userId}`;
}
Context Window Management
// services/context-manager.js
import { ContextManager } from '@openclaw/core';
const contextManager = new ContextManager({
provider: 'sqlite',
dbPath: './data/context.db',
maxMessages: 50,
summarizationThreshold: 30
});
// Store message
await contextManager.addMessage(sessionKey, {
role: 'user',
content: 'What is the weather in Tokyo?',
timestamp: Date.now()
});
// Retrieve context with automatic summarization
const context = await contextManager.getContext(sessionKey, {
maxTokens: 4000, // Token budget for context
includeSummary: true
});
// Manual context pruning
await contextManager.pruneContext(sessionKey, {
keepLast: 20,
summarizeRest: true
});
// Clear context for session
await contextManager.clearContext(sessionKey);
Plugin Development
Plugin Hook System
// plugins/my-plugin/index.js
export default class MyPlugin {
constructor(core) {
this.core = core;
}
// Hook: Before message is sent to agent
async onBeforeAgentProcess(message, context) {
console.log('Processing message:', message.text);
// Modify message or context
if (message.text.includes('urgent')) {
context.priority = 'high';
}
return { message, context };
}
// Hook: After agent response, before sending
async onBeforeMessageSend(response, sessionKey) {
// Add custom footer
response.text += '\n\n_Powered by OpenClaw_';
// Log analytics
await this.logAnalytics(sessionKey, response);
return response;
}
// Hook: On tool execution
async onToolExecute(toolName, params, result) {
console.log(`Tool ${toolName} executed with:`, params);
return result;
}
async logAnalytics(sessionKey, response) {
// Custom analytics logic
}
}
export const manifest = {
name: 'my-plugin',
version: '1.0.0',
hooks: [
'onBeforeAgentProcess',
'onBeforeMessageSend',
'onToolExecute'
]
};
Plugin Configuration
# config/plugins.yaml
plugins:
- name: my-plugin
enabled: true
config:
customSetting: value
- name: analytics-plugin
enabled: true
config:
endpoint: https://analytics.example.com
apiKey: ${ANALYTICS_API_KEY}
- name: calendar-integration
enabled: false
Advanced Configuration
Multi-Model Fallback
// config/models.js
export const modelConfig = {
providers: [
{
name: 'primary',
provider: 'anthropic',
model: 'claude-3-5-sonnet-20241022',
apiKey: process.env.ANTHROPIC_API_KEY,
priority: 1
},
{
name: 'fallback-1',
provider: 'openai',
model: 'gpt-4-turbo',
apiKey: process.env.OPENAI_API_KEY,
priority: 2
},
{
name: 'fallback-2',
provider: 'ollama',
model: 'llama3',
baseURL: 'http://localhost:11434',
priority: 3
}
],
fallbackStrategy: 'priority', // or 'round-robin', 'random'
retryAttempts: 3,
timeoutMs: 30000
};
// Agent will automatically fallback on errors
import { Agent } from '@openclaw/core';
const agent = new Agent({ models: modelConfig });
Memory Backends
// SQLite (default)
export const memoryConfig = {
provider: 'sqlite',
path: './data/memory.db'
};
// PostgreSQL
export const memoryConfig = {
provider: 'postgres',
connectionString: process.env.DATABASE_URL,
schema: 'openclaw_memory'
};
// Redis
export const memoryConfig = {
provider: 'redis',
host: process.env.REDIS_HOST || 'localhost',
port: process.env.REDIS_PORT || 6379,
password: process.env.REDIS_PASSWORD,
db: 0,
ttl: 2592000 // 30 days in seconds
};
Common Patterns
Multi-Channel Broadcasting
// Broadcast message to multiple channels
import { Gateway } from '@openclaw/core';
const gateway = new Gateway();
async function broadcast(message, channels) {
const promises = channels.map(async ({ channel, userId }) => {
const sessionKey = `${channel}:${userId}:${userId}`;
await gateway.sendMessage(sessionKey, {
text: message,
metadata: { broadcast: true }
});
});
await Promise.all(promises);
}
// Usage
await broadcast('System maintenance in 10 minutes', [
{ channel: 'telegram', userId: '123456' },
{ channel: 'discord', userId: '789012' },
{ channel: 'whatsapp', userId: '+1234567890' }
]);
Scheduled Tasks with Cron
// plugins/scheduler/index.js
import cron from 'node-cron';
import { Gateway } from '@openclaw/core';
export default class SchedulerPlugin {
constructor(core) {
this.core = core;
this.gateway = new Gateway();
this.jobs = [];
}
async initialize() {
// Daily summary at 9 AM
const dailySummary = cron.schedule('0 9 * * *', async () => {
await this.sendDailySummary();
});
this.jobs.push(dailySummary);
// Hourly reminder check
const reminderCheck = cron.schedule('0 * * * *', async () => {
await this.checkReminders();
});
this.jobs.push(reminderCheck);
}
async sendDailySummary() {
const users = await this.core.getActiveUsers();
for (const user of users) {
const summary = await this.generateSummary(user);
await this.gateway.sendMessage(user.sessionKey, {
text: `📊 Daily Summary\n\n${summary}`
});
}
}
async checkReminders() {
// Implementation
}
async cleanup() {
this.jobs.forEach(job => job.stop());
}
}
Webhook Integration
// services/webhook-handler.js
import express from 'express';
import { Gateway } from '@openclaw/core';
const app = express();
const gateway = new Gateway();
app.use(express.json());
// GitHub webhook
app.post('/webhooks/github', async (req, res) => {
const event = req.headers['x-github-event'];
const payload = req.body;
if (event === 'push') {
const message = `🔔 New push to ${payload.repository.full_name}\n` +
`Branch: ${payload.ref}\n` +
`Commits: ${payload.commits.length}\n` +
`Author: ${payload.pusher.name}`;
// Send to configured channel
await gateway.sendMessage(
process.env.GITHUB_NOTIFICATION_SESSION,
{ text: message }
);
}
res.status(200).send('OK');
});
app.listen(3001, () => {
console.log('Webhook server running on port 3001');
});
Troubleshooting
Gateway Connection Issues
# Check Gateway is running
curl http://localhost:3000/health
# Check Gateway logs
tail -f logs/gateway.log
# Verify environment variables
./openclaw config verify
# Test channel connectivity
./openclaw channel test telegram
Channel Adapter Not Receiving Messages
// Enable debug logging
// config/gateway.yaml
logging:
level: debug
channels: true
// Or via environment
DEBUG=openclaw:channel:* npm run gateway:start
// Common issues:
// 1. Webhook not configured (Telegram, Discord)
// 2. Wrong bot token
// 3. Firewall blocking incoming connections
// 4. WhatsApp session expired (re-auth needed)
Memory/Context Issues
// Check context size
import { ContextManager } from '@openclaw/core';
const cm = new ContextManager();
const stats = await cm.getContextStats(sessionKey);
console.log('Messages:', stats.messageCount);
console.log('Estimated tokens:', stats.tokenCount);
// If context too large:
await cm.summarizeContext(sessionKey);
// Or reset completely:
await cm.clearContext(sessionKey);
Model API Errors
# Test API connectivity
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model":"claude-3-5-sonnet-20241022","max_tokens":1024,"messages":[{"role":"user","content":"test"}]}'
# Check API key validity
./openclaw models test anthropic
# Enable model fallback
# config/agent.js
export const agentConfig = {
enableFallback: true,
fallbackModels: ['openai/gpt-4-turbo', 'ollama/llama3']
};
WhatsApp QR Code Not Appearing
# Clear WhatsApp session
rm -rf sessions/whatsapp/*
# Restart WhatsApp adapter with fresh auth
./openclaw channel stop whatsapp
./openclaw channel start whatsapp --fresh-auth
# Check logs for QR code
tail -f logs/whatsapp.log
# If using Docker, ensure terminal is attached
docker-compose logs -f whatsapp
Database Migration Issues
# Check current migration status
./openclaw db status
# Run pending migrations
./openclaw db migrate
# Rollback last migration
./openclaw db rollback
# Reset database (WARNING: deletes all data)
./openclaw db reset --confirm
Performance Optimization
// config/performance.js
export const performanceConfig = {
// Enable response streaming for faster UX
streaming: true,
// Parallel tool execution
parallelTools: true,
maxParallelTools: 3,
// Context caching (reduces API calls)
contextCache: {
enabled: true,
ttl: 300, // 5 minutes
maxSize: 100 // Cache 100 sessions
},
// Rate limiting per user
rateLimit: {
enabled: true,
maxRequests: 50,
windowMs: 60000 // 1 minute
}
};
Production Deployment Checklist
# 1. Set secure secrets
GATEWAY_SECRET=$(openssl rand -hex 32)
# 2. Use environment-specific configs
NODE_ENV=production
# 3. Configure process manager
# pm2 ecosystem.config.js
module.exports = {
apps: [{
name: 'openclaw-gateway',
script: './dist/gateway.js',
instances: 2,
exec_mode: 'cluster',
env: {
NODE_ENV: 'production'
}
}]
};
# 4. Set up monitoring
# - Health check endpoint: /health
# - Metrics endpoint: /metrics (Prometheus)
# - Logging: Winston + external aggregator
# 5. Enable HTTPS
# Use reverse proxy (nginx, Caddy) with SSL cert
# 6. Database backups
# PostgreSQL: pg_dump scheduled via cron
# SQLite: File-based backups
# 7. Update strategy
# Blue-green deployment or rolling updates
# Test in staging first
This skill covers OpenClaw installation, channel configuration, agent setup, plugin development, and production deployment. All code examples use environment variables for secrets and demonstrate real integration patterns.