Files
openclaw/docs/plugins/sdk-channel-plugins.md
2026-03-22 08:50:48 -07:00

4.5 KiB

title, sidebarTitle, summary, read_when
title sidebarTitle summary read_when
Channel Plugin SDK Channel Plugins Contracts and helpers for native messaging channel plugins, including actions, routing, pairing, and setup
You are building a native channel plugin
You need to implement the shared `message` tool for a channel
You need pairing, setup, or routing helpers for a channel

Channel Plugin SDK

Channel plugins use defineChannelPluginEntry(...) from openclaw/plugin-sdk/core and implement the ChannelPlugin contract.

Minimal channel entry

import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
import { exampleChannelPlugin } from "./src/channel.js";
import { setExampleRuntime } from "./src/runtime.js";

export default defineChannelPluginEntry({
  id: "example-channel",
  name: "Example Channel",
  description: "Example native channel plugin",
  plugin: exampleChannelPlugin,
  setRuntime: setExampleRuntime,
});

ChannelPlugin shape

Important sections of the contract:

  • meta: docs, labels, and picker metadata
  • capabilities: replies, polls, reactions, threads, media, and chat types
  • config and configSchema: account resolution and config parsing
  • setup and setupWizard: onboarding/setup flow
  • security: DM policy and allowlist behavior
  • messaging: target parsing and outbound session routing
  • actions: shared message tool discovery and execution
  • pairing, threading, status, lifecycle, groups, directory

For pure types, import from openclaw/plugin-sdk/channel-contract.

Shared message tool

Channel plugins own their channel-specific part of the shared message tool through ChannelMessageActionAdapter.

import { Type } from "@sinclair/typebox";
import { createMessageToolButtonsSchema } from "openclaw/plugin-sdk/channel-actions";

export const exampleActions = {
  describeMessageTool() {
    return {
      actions: ["send", "edit"],
      capabilities: ["buttons"],
      schema: {
        visibility: "current-channel",
        properties: {
          buttons: createMessageToolButtonsSchema(),
          threadId: Type.String(),
        },
      },
    };
  },
  async handleAction(ctx) {
    if (ctx.action === "send") {
      return {
        content: [{ type: "text", text: `send to ${String(ctx.params.to)}` }],
      };
    }

    return {
      content: [{ type: "text", text: `unsupported action: ${ctx.action}` }],
    };
  },
};

Key types:

  • ChannelMessageActionAdapter
  • ChannelMessageActionContext
  • ChannelMessageActionDiscoveryContext
  • ChannelMessageToolDiscovery

Outbound routing helpers

When a channel plugin needs custom outbound routing, implement messaging.resolveOutboundSessionRoute(...).

Use buildChannelOutboundSessionRoute(...) from plugin-sdk/core to return the standard route payload:

import { buildChannelOutboundSessionRoute } from "openclaw/plugin-sdk/core";

const messaging = {
  resolveOutboundSessionRoute({ cfg, agentId, accountId, target }) {
    return buildChannelOutboundSessionRoute({
      cfg,
      agentId,
      channel: "example-channel",
      accountId,
      peer: { kind: "direct", id: target },
      chatType: "direct",
      from: accountId ?? "default",
      to: target,
    });
  },
};

Pairing helpers

Use plugin-sdk/channel-pairing for DM approval flows:

import { createChannelPairingController } from "openclaw/plugin-sdk/channel-pairing";

const pairing = createChannelPairingController({
  core: runtime,
  channel: "example-channel",
  accountId: "default",
});

const result = pairing.issueChallenge({
  agentId: "assistant",
  requesterId: "user-123",
});

That surface also gives you scoped access to pairing storage helpers such as allowlist reads and request upserts.

Channel setup helpers

Use:

  • plugin-sdk/channel-setup for optional or installable channels
  • plugin-sdk/setup for setup adapters, DM policy, and allowlist prompts
  • plugin-sdk/webhook-ingress for plugin-owned webhook routes

Channel plugin guidance

  • Keep transport-specific execution inside the channel package.
  • Use channel-contract types in tests and local helpers.
  • Keep describeMessageTool(...) and handleAction(...) aligned.
  • Keep session routing in messaging, not in ad-hoc command handlers.
  • Prefer focused subpaths over broad runtime coupling.