Skip to main content

Building Channel Plugins

This guide walks through building a channel plugin that connects OpenClaw to a messaging platform. By the end you will have a working channel with DM security, pairing, reply threading, and outbound messaging.
If you have not built any OpenClaw plugin before, read Getting Started first for the basic package structure and manifest setup.

How channel plugins work

Channel plugins do not need their own send/edit/react tools. OpenClaw keeps one shared message tool in core. Your plugin owns:
  • Config — account resolution and setup wizard
  • Security — DM policy and allowlists
  • Pairing — DM approval flow
  • Outbound — sending text, media, and polls to the platform
  • Threading — how replies are threaded
Core owns the shared message tool, prompt wiring, session bookkeeping, and dispatch.

Walkthrough

1

Package and manifest

Create the standard plugin files. The channel field in package.json is what makes this a channel plugin:
2

Build the channel plugin object

The ChannelPlugin interface has many optional adapter surfaces. Start with the minimum — id and setup — and add adapters as you need them.Create src/channel.ts:
src/channel.ts
Instead of implementing low-level adapter interfaces manually, you pass declarative options and the builder composes them:You can also pass raw adapter objects instead of the declarative options if you need full control.
3

Wire the entry point

Create index.ts:
index.ts
defineChannelPluginEntry handles the setup/full registration split automatically. See Entry Points for all options.
4

Add a setup entry

Create setup-entry.ts for lightweight loading during onboarding:
setup-entry.ts
OpenClaw loads this instead of the full entry when the channel is disabled or unconfigured. It avoids pulling in heavy runtime code during setup flows. See Setup and Config for details.
5

Handle inbound messages

Your plugin needs to receive messages from the platform and forward them to OpenClaw. The typical pattern is a webhook that verifies the request and dispatches it through your channel’s inbound handler:
Inbound message handling is channel-specific. Each channel plugin owns its own inbound pipeline. Look at bundled channel plugins (e.g. extensions/msteams, extensions/googlechat) for real patterns.
6

Test

Write colocated tests in src/channel.test.ts:
src/channel.test.ts
For shared test helpers, see Testing.

File structure

Advanced topics

Threading options

Fixed, account-scoped, or custom reply modes

Message tool integration

describeMessageTool and action discovery

Target resolution

inferTargetChatType, looksLikeId, resolveTarget

Runtime helpers

TTS, STT, media, subagent via api.runtime

Next steps