OpenClaw Integration
OpenClaw is the AI that actually does things -clearing inboxes, sending emails, managing calendars, checking you in for flights. ClawXP is the real-time transport layer that powers OpenClaw's live updates.
This guide shows you how to subscribe to OpenClaw agent events, stream live reasoning steps, and build reactive UIs that update as OpenClaw works.
ClawXP + OpenClaw -ClawXP is the official pub/sub backend for OpenClaw's live agent streams. All real-time events from OpenClaw agents flow through a ClawXP server.
Prerequisites
@clawxp/sdkinstalled (npm install @clawxp/sdk)- A ClawXP server running (see Server docs) or access to an OpenClaw-hosted endpoint
- An OpenClaw API key from openclaw.ai
Channel conventions
OpenClaw uses the following channel naming conventions:
| Channel pattern | Mode | Description |
|---|---|---|
| openclaw:agent-stream | stream | Live agent reasoning steps |
| openclaw:tool-calls | stream | Tool execution events (browse, email, calendar) |
| openclaw:agent-done | stream | Task completion event with summary |
| openclaw:notifications | stream | Proactive agent check-in notifications |
| openclaw:status | datagram | Agent status heartbeat (high frequency) |
Basic setup
import { ClawXPClient } from '@clawxp/sdk';
const client = new ClawXPClient({
url: 'wss://your-clawxp-server.com',
token: process.env.CLAWXP_TOKEN,
});
// Subscribe to live agent reasoning
await client.subscribe('openclaw:agent-stream', {
mode: 'stream',
onMessage: (msg) => {
const { thought, step, agentId } = msg.data;
console.log(`[${agentId}] Step ${step}: ${thought}`);
},
});
Streaming agent thoughts to a React UI
import { useState, useEffect } from 'react';
import { ClawXPClient } from '@clawxp/sdk';
export function AgentThoughts() {
const [thoughts, setThoughts] = useState<string[]>([]);
useEffect(() => {
const client = new ClawXPClient({
url: process.env.NEXT_PUBLIC_CLAWXP_URL!,
token: process.env.NEXT_PUBLIC_CLAWXP_TOKEN!,
});
let unsub: (() => void) | undefined;
client.subscribe('openclaw:agent-stream', {
mode: 'stream',
onMessage: (msg) => {
setThoughts((prev) => [...prev, msg.data.thought]);
},
}).then((sub) => {
unsub = sub.unsubscribe;
});
return () => unsub?.();
}, []);
return (
<ul>
{thoughts.map((t, i) => <li key={i}>{t}</li>)}
</ul>
);
}
Listening for tool calls
When OpenClaw performs an action (browsing the web, sending an email), it publishes a tool-call event:
await client.subscribe('openclaw:tool-calls', {
mode: 'stream',
onMessage: (msg) => {
const { tool, input, status, output } = msg.data;
switch (tool) {
case 'browse':
console.log(`Browsing: ${input.url}`);
break;
case 'send_email':
console.log(`Email sent to: ${input.to}`);
break;
case 'create_event':
console.log(`Calendar event created: ${input.title}`);
break;
}
},
});
Task completion events
When an OpenClaw task finishes, subscribe to openclaw:agent-done for the final summary:
await client.subscribe('openclaw:agent-done', {
mode: 'stream',
onMessage: (msg) => {
const { taskId, summary, duration, toolsUsed } = msg.data;
console.log(`Task ${taskId} done in ${duration}ms`);
console.log('Summary:', summary);
},
});
Agent status heartbeat (datagram mode)
For a live "thinking / idle" indicator, use datagram mode on the status channel:
await client.subscribe('openclaw:status', {
mode: 'datagram', // Drop stale status packets -freshness matters
onMessage: (msg) => {
const { agentId, state } = msg.data;
updateStatusIndicator(agentId, state); // 'thinking' | 'idle' | 'waiting'
},
});
Publishing to OpenClaw
You can publish tasks or instructions directly to an OpenClaw agent via ClawXP:
// Trigger an OpenClaw task
await client.publish('openclaw:tasks', {
instruction: 'Check my inbox and summarize unread emails from today',
userId: 'u_42',
priority: 'normal',
});
Publishing to OpenClaw channels requires appropriate authorization claims in your JWT. Ensure your token includes "publish:openclaw" in its permissions scope.
Full example: OpenClaw live dashboard
import { ClawXPClient } from '@clawxp/sdk';
const client = new ClawXPClient({
url: process.env.CLAWXP_URL!,
token: process.env.CLAWXP_TOKEN!,
});
// Subscribe to all relevant OpenClaw channels
await Promise.all([
client.subscribe('openclaw:agent-stream', {
mode: 'stream',
onMessage: (msg) => ui.appendThought(msg.data),
}),
client.subscribe('openclaw:tool-calls', {
mode: 'stream',
onMessage: (msg) => ui.showToolCall(msg.data),
}),
client.subscribe('openclaw:agent-done', {
mode: 'stream',
onMessage: (msg) => ui.showSummary(msg.data),
}),
client.subscribe('openclaw:status', {
mode: 'datagram',
onMessage: (msg) => ui.updateStatus(msg.data.state),
onStale: (count) => console.log(`Dropped ${count} stale status ticks`),
}),
]);
Troubleshooting
I'm not receiving any messages
- Verify your JWT token has the correct claims for the OpenClaw channels
- Ensure your ClawXP server is configured with the OpenClaw adapter
- Check the Playground to confirm your ClawXP server is reachable
Messages arrive out of order
- For agent thoughts and tool calls, always use
mode: 'stream'-this guarantees ordered delivery - Only use
mode: 'datagram'for status heartbeats where freshness beats completeness
Agent stream stops mid-task
- The ClawXP SDK automatically reconnects and resubscribes on any network interruption
- Your
onMessagehandler will resume receiving events once connectivity is restored
For questions about OpenClaw itself, visit openclaw.ai or the OpenClaw Discord.