
Hey fellow AI tinkerers — if you've been stress-testing self-hosted agent setups and want to wire OpenClaw into your own Node.js project rather than just run it from the CLI, this one's for you. I'm Hanks. I test automation tools inside real workflows — not demos. Here's what I found after digging into the openclaw npm package over the past few weeks.
I'll be upfront: when I first looked at this, my question wasn't "what can it do?" It was: "Can I actually call this thing programmatically from my own code without the whole gateway babysitting me?"
Spoiler — it's possible, but the architecture is different from what you might expect.
The package is published as openclaw on the public npm registry. As of February 25, 2026, the current stable version is 2026.2.25 (MIT license, ~796,000 weekly downloads). It's an ESM-only package ("type": "module"), which matters a lot when you're wiring it into an existing project.
The official description: "Multi-channel AI gateway with extensible messaging integrations." That's accurate but undersells the complexity. This isn't a small utility — the published tarball is 73 MB.
Here's the key thing most tutorials don't explain clearly. The package exposes three distinct surfaces:
If you want to extend or integrate OpenClaw without forking the core, the Plugin SDK (openclaw/plugin-sdk) is where you should be spending your time. It's explicitly exported and typed. The main dist/index.js is internal and can break between releases.

OpenClaw is designed as a global CLI tool first. The recommended install:
# npm
npm install -g openclaw@latest
# pnpm (preferred for builds from source)
pnpm add -g openclaw@latest
# bun
bun add -g openclaw@latest
If you're building a plugin or script that imports from openclaw rather than running it globally, add it as a dev/peer dependency in your project:
npm install openclaw --save-dev
Version note: Avoid
2026.2.21-2— a confirmed bug in that release caused corrupted gateway-cli dist files with aSyntaxError: Unexpected identifier 'timeoutSeconds'crash on startup. The fix landed in2026.2.23-beta.1and is included in the current stable2026.2.25.
The team recommends Anthropic Claude with Opus 4.6 specifically for long-context strength and better prompt-injection resistance.
For plugin development, your entry file should use the Plugin SDK export. Here's a minimal working plugin skeleton (TypeScript/ESM):
// my-openclaw-plugin/index.ts
import type { PluginContext } from 'openclaw/plugin-sdk';
export default function myPlugin(ctx: PluginContext) {
// Register a custom tool
ctx.registerTool({
name: 'my_custom_tool',
description: 'Does something useful',
inputSchema: {
type: 'object',
properties: {
message: { type: 'string' }
},
required: ['message']
},
execute: async ({ message }) => {
return { result: `Processed: ${message}` };
}
});
}
Your package.json for the plugin:
{
"name": "my-openclaw-plugin",
"version": "1.0.0",
"type": "module",
"openclaw": {
"extensions": ["./index.ts"]
},
"peerDependencies": {
"openclaw": "*"
}
}

OpenClaw doesn't use a single "API key for itself" — it routes your AI provider's key (Anthropic, OpenAI, etc.) through its gateway config. Two ways to handle this:
Option 1 — Onboarding wizard (recommended for first-time setup):
openclaw onboard --install-daemon
This walks you through model selection, workspace setup, and channel configuration interactively.
Option 2 — Direct config set (for automation/CI):
openclaw config set anthropic.apiKey "sk-ant-..."
openclaw config set model "claude-opus-4-6"
Or via environment variable:
ANTHROPIC_API_KEY=sk-ant-... openclaw gateway
Always run openclaw doctor after setup — it surfaces misconfigured DM policies, missing keys, and memory probe issues before they bite you at runtime.
The intended path for sending messages from code is the CLI interface, callable as a subprocess, or via the Gateway WebSocket control plane at ws://127.0.0.1:18789.
CLI subprocess approach (works well for automation scripts):
import { execSync } from 'node:child_process';
// Send a message to a connected channel
execSync(`openclaw message send --to +1234567890 --message "Automated report ready"`);
// Trigger the agent with a task
execSync(`openclaw agent --message "Summarize the last 24h of logs" --thinking high`);
For tighter integration, use the Gateway WebSocket — it exposes a JSON-RPC-style control plane where you connect, send a message event, and listen for the agent's response stream.
Skills live in the skills/ directory inside the openclaw package, and are also installable from ClawHub (5,700+ community skills as of February 2026). Trigger a skill from a Node.js script:
import { execSync } from 'node:child_process';
// Invoke a specific skill via the agent
execSync(`openclaw agent --message "Run the daily-report skill"`);
For plugin-registered tools, they're automatically available to the agent once your plugin is installed and the gateway restarts:
openclaw plugins install -l ./my-openclaw-plugin
openclaw gateway restart
The gateway emits structured events over its WebSocket control plane. Here's a minimal listener in Node.js:
import WebSocket from 'ws';
const ws = new WebSocket('ws://127.0.0.1:18789');
ws.on('open', () => {
console.log('Connected to OpenClaw gateway');
});
ws.on('message', (data) => {
const event = JSON.parse(data.toString());
if (event.type === 'agent.message') {
console.log('Agent said:', event.payload.content);
}
if (event.type === 'tool.call') {
console.log('Tool called:', event.payload.name, event.payload.input);
}
});
ws.on('close', () => {
console.log('Gateway disconnected — check openclaw doctor');
});
The ws package is not bundled — add it separately: npm install ws.
Here's an end-to-end example: a Node.js script that sends a task to the OpenClaw agent, waits for the response, and logs the output.
Scenario: You have a daily log file. You want the agent to summarize it and deliver the result via a connected channel (e.g. Telegram).
// daily-summary.mjs
import WebSocket from 'ws';
import { readFileSync } from 'node:fs';
const LOG_PATH = './logs/today.log';
const GATEWAY_URL = 'ws://127.0.0.1:18789';
async function runDailySummary() {
const logContent = readFileSync(LOG_PATH, 'utf-8');
const prompt = `Summarize this log in 3 bullet points and send it to me on Telegram:\n\n${logContent}`;
const ws = new WebSocket(GATEWAY_URL);
ws.on('open', () => {
ws.send(JSON.stringify({
type: 'agent.task',
payload: { message: prompt }
}));
});
ws.on('message', (data) => {
const event = JSON.parse(data.toString());
if (event.type === 'agent.done') {
console.log('Task complete:', event.payload.summary);
ws.close();
}
});
ws.on('error', (err) => {
console.error('WS error:', err.message);
console.error('Is the gateway running? Try: openclaw gateway --verbose');
});
}
runDailySummary();
Success criteria: Agent delivers the summary to Telegram within ~30 seconds, no hallucinated log entries. Failure signal: WS connection refused means the gateway isn't running, or it's on a different port.
Run it:
node daily-summary.mjs
Automate it with OpenClaw's built-in cron scheduler:
openclaw agent --message "Every day at 8am, run the daily-summary task"
At Macaron, we see this pattern constantly: automation scripts work in isolation, but the task-to-result handoff loses context the moment it spans more than one tool. If you want to see how a one-sentence instruction stays structured and executable across steps without losing thread, try running a real task in Macaron — it's free to start.

Security note (February 2026): A compromised cline@2.3.0 npm package briefly contained a postinstall script that silently installed OpenClaw on developer machines. If you didn't intentionally install it, run npm uninstall -g openclaw and audit your global packages. OpenClaw itself wasn't the attacker — but it has broad system permissions, which makes unintended installs a real risk.
Q: Is openclaw/plugin-sdk a stable public API?
It's the most stable programmatic interface OpenClaw exposes — explicitly exported and typed in package.json. That said, OpenClaw ships daily releases and breaking changes happen. Pin your plugin's peer dependency to a specific range and watch the GitHub releases changelog.
Q: Can I use OpenClaw in a Docker container?
Yes — mount a persistent volume for ~/.openclaw/ so config and memory survive restarts. The container image needs Node ≥ 22. The secure-openclaw community project has a solid Docker Compose reference setup.
Q: What AI models does OpenClaw support?
Any model with an OpenAI-compatible API, plus native support for Anthropic, OpenAI, Gemini, Moonshot/Kimi, Qwen, and others. The team specifically recommends claude-opus-4-6 for long-context and prompt-injection resistance.
Q: Do I need the full gateway running to use the plugin SDK?
Yes. The Plugin SDK registers tools that the gateway loads at runtime. There's no standalone "SDK-only" mode. The gateway runs as a local daemon (launchd on macOS, systemd on Linux) so it stays in the background without manual restarts.
Q: How do I handle ESM in my plugin if my existing project is CommonJS?
Use dynamic import() to pull in openclaw/plugin-sdk from a CJS context, or — cleaner — migrate your plugin to ESM. OpenClaw is ESM-only and that's not changing. Start new plugins with "type": "module" from day one.
Q: Is the Gateway WebSocket protocol documented?
Partially. The official docs cover the high-level Gateway architecture. For the full protocol schema, check dist/protocol.schema.json inside the package — that's the source of truth for event shapes.
Q: My script works locally but fails on a remote VPS — what's wrong?
Usually one of three things: Node below version 22 on the remote, gateway daemon not running, or ANTHROPIC_API_KEY not set in the remote environment. Run openclaw doctor — it's the fastest diagnostic tool you have.