Skip to main content
The Plugin SDK provides APIs for registering channels, tools, hooks, services, and HTTP handlers. Import from openclaw/plugin-sdk:
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";

Plugin API

The OpenClawPluginApi is passed to your plugin’s register and activate functions.

Properties

id
string
Plugin identifier (e.g., "matrix", "my-plugin")
name
string
Plugin display name
version
string
Plugin version from package.json
description
string
Plugin description
source
string
Absolute path to the plugin entry point
config
OpenClawConfig
OpenClaw configuration object. Access via api.config.channels, api.config.agents, etc.
pluginConfig
Record<string, unknown>
Plugin-specific configuration from config.json under plugins.entries[id].config
runtime
PluginRuntime
Runtime services for media, messaging, logging, and more. See Runtime Services.
logger
PluginLogger
Scoped logger for plugin messages:
api.logger.info("Plugin started");
api.logger.warn("Deprecation warning");
api.logger.error("Operation failed");
api.logger.debug?.("Verbose output");

Registration Methods

registerTool
(tool: AnyAgentTool | OpenClawPluginToolFactory, opts?: OpenClawPluginToolOptions) => void
Register an agent tool. Tools are exposed to the LLM and can be called during agent runs.
api.registerTool(createMyTool());
api.registerTool(createMyTool(), { optional: true });
api.registerTool(toolFactory, { names: ["tool1", "tool2"] });
Options:
  • name?: string - Tool name override
  • names?: string[] - Multiple tool names (for tool factories)
  • optional?: boolean - Require explicit allowlisting in agent config
registerHook
(events: string | string[], handler: InternalHookHandler, opts?: OpenClawPluginHookOptions) => void
Register a lifecycle hook. Hooks intercept events at key points in the message lifecycle.
api.registerHook("llm_input", async (event, ctx) => {
  api.logger.info(`LLM call: ${event.provider}/${event.model}`);
});

api.registerHook(["message_received", "message_sent"], handler);
See Hooks for available events.
registerChannel
(registration: OpenClawPluginChannelRegistration | ChannelPlugin) => void
Register a channel plugin. Channel plugins integrate messaging platforms.
api.registerChannel({ plugin: myChannelPlugin });
See Channels for details.
registerService
(service: OpenClawPluginService) => void
Register a background service. Services run persistent background tasks.
api.registerService({
  id: "my-service",
  start: async (ctx) => {
    // Start background task
  },
  stop: async (ctx) => {
    // Clean up
  }
});
registerHttpRoute
(params: { path: string; handler: OpenClawPluginHttpRouteHandler }) => void
Register an HTTP route for webhooks or custom endpoints.
api.registerHttpRoute({
  path: "/webhook/my-plugin",
  handler: async (req, res) => {
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(JSON.stringify({ ok: true }));
  }
});
Routes are automatically normalized to prevent conflicts.
registerHttpHandler
(handler: OpenClawPluginHttpHandler) => void
Register a global HTTP handler. Handlers are called for all requests.
api.registerHttpHandler((req, res) => {
  if (req.url?.startsWith("/my-prefix")) {
    // Handle request
    return true; // Handled
  }
  return false; // Pass to next handler
});
registerCli
(registrar: OpenClawPluginCliRegistrar, opts?: { commands?: string[] }) => void
Register CLI commands. Add custom commands to the openclaw CLI.
api.registerCli((ctx) => {
  ctx.program
    .command("my-command")
    .description("Custom command")
    .action(() => {
      ctx.logger.info("Command executed");
    });
});
registerCommand
(command: OpenClawPluginCommandDefinition) => void
Register a plugin command. Plugin commands bypass the LLM agent and are processed before built-in commands.
api.registerCommand({
  name: "status",
  description: "Check plugin status",
  acceptsArgs: false,
  requireAuth: true,
  handler: async (ctx) => {
    return { text: "Plugin is running" };
  }
});
registerProvider
(provider: ProviderPlugin) => void
Register an authentication provider. Providers integrate with openclaw login.
api.registerProvider({
  id: "my-provider",
  label: "My Provider",
  auth: [/* auth methods */]
});
registerGatewayMethod
(method: string, handler: GatewayRequestHandler) => void
Register a custom gateway method. Gateway methods are JSON-RPC endpoints exposed via the gateway.
api.registerGatewayMethod("my.method", async (params) => {
  return { result: "success" };
});

Utility Methods

resolvePath
(input: string) => string
Resolve paths relative to the plugin source directory.
const configPath = api.resolvePath("./config.json");
const srcPath = api.resolvePath("src/helper.ts");
on
<K extends PluginHookName>(hookName: K, handler: PluginHookHandlerMap[K], opts?: { priority?: number }) => void
Alternative API for registering lifecycle hooks with typed event handlers.
api.on("llm_input", async (event, ctx) => {
  api.logger.info(`Model: ${event.model}`);
});

Runtime Services

The api.runtime object provides access to OpenClaw’s core services.

Config

api.runtime.config.loadConfig();
api.runtime.config.writeConfigFile(config);

Media

const media = await api.runtime.media.loadWebMedia(url);
const mime = api.runtime.media.detectMime(buffer);
const metadata = await api.runtime.media.getImageMetadata(buffer);
const jpeg = await api.runtime.media.resizeToJpeg(buffer, { maxWidth: 1024 });

Text Processing

const chunks = api.runtime.channel.text.chunkMarkdownText(text, limit);
const mode = api.runtime.channel.text.resolveChunkMode(config);
const hasCommand = api.runtime.channel.text.hasControlCommand(text);

Reply Dispatching

await api.runtime.channel.reply.dispatchReplyFromConfig({
  config,
  channelId: "telegram",
  to: "123456789",
  text: "Hello"
});

Logging

const childLogger = api.runtime.logging.getChildLogger(
  { component: "my-plugin" },
  { level: "info" }
);

if (api.runtime.logging.shouldLogVerbose()) {
  childLogger.debug("Verbose log message");
}

State Directory

const stateDir = api.runtime.state.resolveStateDir(config);

Channel-Specific Services

The runtime provides channel-specific helpers:
// Discord
api.runtime.channel.discord.sendMessageDiscord(...);
api.runtime.channel.discord.probeDiscord(...);

// Slack
api.runtime.channel.slack.sendMessageSlack(...);
api.runtime.channel.slack.probeSlack(...);

// Telegram
api.runtime.channel.telegram.sendMessageTelegram(...);
api.runtime.channel.telegram.probeTelegram(...);

// WhatsApp
api.runtime.channel.whatsapp.sendMessageWhatsApp(...);
api.runtime.channel.whatsapp.loginWeb(...);

// Signal
api.runtime.channel.signal.sendMessageSignal(...);

// iMessage
api.runtime.channel.imessage.sendMessageIMessage(...);

// LINE
api.runtime.channel.line.sendMessageLine(...);
See the runtime types for the complete API.

Plugin SDK Exports

The openclaw/plugin-sdk package exports types and utilities:

Types

import type {
  OpenClawPluginApi,
  OpenClawPluginDefinition,
  OpenClawPluginService,
  PluginRuntime,
  RuntimeLogger,
  AnyAgentTool,
  ChannelPlugin,
  ChannelConfigSchema,
  HookEntry,
  OpenClawConfig,
  ProviderAuthContext,
  ProviderAuthResult
} from "openclaw/plugin-sdk";

Helper Functions

import {
  emptyPluginConfigSchema,
  normalizePluginHttpPath,
  registerPluginHttpRoute,
  normalizeWebhookPath,
  resolveWebhookPath,
  registerWebhookTarget,
  buildAgentMediaPayload,
  buildBaseChannelStatusSummary,
  buildOauthProviderAuthResult,
  acquireFileLock,
  withFileLock
} from "openclaw/plugin-sdk";

Channel Helpers

import {
  createAccountListHelpers,
  buildChannelConfigSchema,
  resolveChannelAccountConfigBasePath,
  recordInboundSession,
  createReplyPrefixContext,
  createTypingCallbacks,
  extractToolSend,
  formatDocsLink
} from "openclaw/plugin-sdk";

Config Schemas (Zod)

import {
  DiscordConfigSchema,
  GoogleChatConfigSchema,
  IMessageConfigSchema,
  MSTeamsConfigSchema,
  SignalConfigSchema,
  SlackConfigSchema,
  TelegramConfigSchema,
  WhatsAppConfigSchema,
  DmConfigSchema,
  GroupPolicySchema,
  MarkdownConfigSchema,
  ToolPolicySchema
} from "openclaw/plugin-sdk";

Example: Complete Plugin

import type { OpenClawPluginApi, AnyAgentTool } from "openclaw/plugin-sdk";
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
import { Type } from "@sinclair/typebox";

function createExampleTool(api: OpenClawPluginApi): AnyAgentTool {
  return {
    name: "example",
    description: "Example tool",
    input: Type.Object({
      message: Type.String()
    }),
    execute: async (args) => {
      api.logger.info(`Tool called: ${args.message}`);
      return { result: "success", details: { message: args.message } };
    }
  };
}

const plugin = {
  id: "example",
  name: "Example Plugin",
  description: "Demonstrates plugin SDK features",
  configSchema: emptyPluginConfigSchema(),
  
  register(api: OpenClawPluginApi) {
    // Register tool
    api.registerTool(createExampleTool(api));

    // Register hook
    api.registerHook("llm_input", async (event, ctx) => {
      api.logger.info(`LLM call: ${event.provider}/${event.model}`);
    });

    // Register HTTP route
    api.registerHttpRoute({
      path: "/webhook/example",
      handler: async (req, res) => {
        res.writeHead(200);
        res.end("OK");
      }
    });

    api.logger.info("Plugin registered");
  },

  activate(api: OpenClawPluginApi) {
    api.logger.info("Plugin activated");
  }
};

export default plugin;

Next Steps

  • Hooks - Implement lifecycle hooks
  • Tools - Build custom agent tools
  • Channels - Create channel integrations
  • Examples - Real-world plugin examples