24 KiB
summary, title, sidebarTitle, read_when
| summary | title | sidebarTitle | read_when | |||
|---|---|---|---|---|---|---|
| Setup wizards, setup-entry.ts, config schemas, and package.json metadata | Plugin setup and config | Setup and config |
|
Reference for plugin packaging (package.json metadata), manifests (openclaw.plugin.json), setup entries, and config schemas.
Package metadata
Your package.json needs an openclaw field that tells the plugin system what your plugin provides:
openclaw fields
Entry point files (relative to package root).
Lightweight setup-only entry (optional).
Channel catalog metadata for setup, picker, quickstart, and status surfaces.
Provider ids registered by this plugin.
Install hints: `npmSpec`, `localPath`, `defaultChoice`, `minHostVersion`, `expectedIntegrity`, `allowInvalidConfigRecovery`.
Startup behavior flags.
openclaw.channel
openclaw.channel is cheap package metadata for channel discovery and setup surfaces before runtime loads.
| Field | Type | What it means |
|---|---|---|
id |
string |
Canonical channel id. |
label |
string |
Primary channel label. |
selectionLabel |
string |
Picker/setup label when it should differ from label. |
detailLabel |
string |
Secondary detail label for richer channel catalogs and status surfaces. |
docsPath |
string |
Docs path for setup and selection links. |
docsLabel |
string |
Override label used for docs links when it should differ from the channel id. |
blurb |
string |
Short onboarding/catalog description. |
order |
number |
Sort order in channel catalogs. |
aliases |
string[] |
Extra lookup aliases for channel selection. |
preferOver |
string[] |
Lower-priority plugin/channel ids this channel should outrank. |
systemImage |
string |
Optional icon/system-image name for channel UI catalogs. |
selectionDocsPrefix |
string |
Prefix text before docs links in selection surfaces. |
selectionDocsOmitLabel |
boolean |
Show the docs path directly instead of a labeled docs link in selection copy. |
selectionExtras |
string[] |
Extra short strings appended in selection copy. |
markdownCapable |
boolean |
Marks the channel as markdown-capable for outbound formatting decisions. |
exposure |
object |
Channel visibility controls for setup, configured lists, and docs surfaces. |
quickstartAllowFrom |
boolean |
Opt this channel into the standard quickstart allowFrom setup flow. |
forceAccountBinding |
boolean |
Require explicit account binding even when only one account exists. |
preferSessionLookupForAnnounceTarget |
boolean |
Prefer session lookup when resolving announce targets for this channel. |
Example:
{
"openclaw": {
"channel": {
"id": "my-channel",
"label": "My Channel",
"selectionLabel": "My Channel (self-hosted)",
"detailLabel": "My Channel Bot",
"docsPath": "/channels/my-channel",
"docsLabel": "my-channel",
"blurb": "Webhook-based self-hosted chat integration.",
"order": 80,
"aliases": ["mc"],
"preferOver": ["my-channel-legacy"],
"selectionDocsPrefix": "Guide:",
"selectionExtras": ["Markdown"],
"markdownCapable": true,
"exposure": {
"configured": true,
"setup": true,
"docs": true
},
"quickstartAllowFrom": true
}
}
}
exposure supports:
configured: include the channel in configured/status-style listing surfacessetup: include the channel in interactive setup/configure pickersdocs: mark the channel as public-facing in docs/navigation surfaces
openclaw.install
openclaw.install is package metadata, not manifest metadata.
| Field | Type | What it means |
|---|---|---|
npmSpec |
string |
Canonical npm spec for install/update flows. |
localPath |
string |
Local development or bundled install path. |
defaultChoice |
"npm" | "local" |
Preferred install source when both are available. |
minHostVersion |
string |
Minimum supported OpenClaw version in the form >=x.y.z. |
expectedIntegrity |
string |
Expected npm dist integrity string, usually sha512-..., for pinned installs. |
allowInvalidConfigRecovery |
boolean |
Lets bundled-plugin reinstall flows recover from specific stale-config failures. |
```json
{
"openclaw": {
"install": {
"npmSpec": "@wecom/wecom-openclaw-plugin@1.2.3",
"expectedIntegrity": "sha512-REPLACE_WITH_NPM_DIST_INTEGRITY",
"defaultChoice": "npm"
}
}
}
```
`allowInvalidConfigRecovery` is not a general bypass for broken configs. It is for narrow bundled-plugin recovery only, so reinstall/setup can repair known upgrade leftovers like a missing bundled plugin path or stale `channels.` entry for that same plugin. If config is broken for unrelated reasons, install still fails closed and tells the operator to run `openclaw doctor --fix`.
Deferred full load
Channel plugins can opt into deferred loading with:
{
"openclaw": {
"extensions": ["./index.ts"],
"setupEntry": "./setup-entry.ts",
"startup": {
"deferConfiguredChannelFullLoadUntilAfterListen": true
}
}
}
When enabled, OpenClaw loads only setupEntry during the pre-listen startup phase, even for already-configured channels. The full entry loads after the gateway starts listening.
If your setup/full entry registers gateway RPC methods, keep them on a plugin-specific prefix. Reserved core admin namespaces (config.*, exec.approvals.*, wizard.*, update.*) stay core-owned and always resolve to operator.admin.
Plugin manifest
Every native plugin must ship an openclaw.plugin.json in the package root. OpenClaw uses this to validate config without executing plugin code.
{
"id": "my-plugin",
"name": "My Plugin",
"description": "Adds My Plugin capabilities to OpenClaw",
"configSchema": {
"type": "object",
"additionalProperties": false,
"properties": {
"webhookSecret": {
"type": "string",
"description": "Webhook verification secret"
}
}
}
}
For channel plugins, add kind and channels:
{
"id": "my-channel",
"kind": "channel",
"channels": ["my-channel"],
"configSchema": {
"type": "object",
"additionalProperties": false,
"properties": {}
}
}
Even plugins with no config must ship a schema. An empty schema is valid:
{
"id": "my-plugin",
"configSchema": {
"type": "object",
"additionalProperties": false
}
}
See Plugin manifest for the full schema reference.
ClawHub publishing
For plugin packages, use the package-specific ClawHub command:
clawhub package publish your-org/your-plugin --dry-run
clawhub package publish your-org/your-plugin
Setup entry
The setup-entry.ts file is a lightweight alternative to index.ts that OpenClaw loads when it only needs setup surfaces (onboarding, config repair, disabled channel inspection).
// setup-entry.ts
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { myChannelPlugin } from "./src/channel.js";
export default defineSetupPluginEntry(myChannelPlugin);
This avoids loading heavy runtime code (crypto libraries, CLI registrations, background services) during setup flows.
Bundled workspace channels that keep setup-safe exports in sidecar modules can use defineBundledChannelSetupEntry(...) from openclaw/plugin-sdk/channel-entry-contract instead of defineSetupPluginEntry(...). That bundled contract also supports an optional runtime export so setup-time runtime wiring can stay lightweight and explicit.
Those startup gateway methods should still avoid reserved core admin namespaces such as `config.*` or `update.*`.
- CLI registrations.
- Background services.
- Heavy runtime imports (crypto, SDKs).
- Gateway methods only needed after startup.
Narrow setup helper imports
For hot setup-only paths, prefer the narrow setup helper seams over the broader plugin-sdk/setup umbrella when you only need part of the setup surface:
| Import path | Use it for | Key exports |
|---|---|---|
plugin-sdk/setup-runtime |
setup-time runtime helpers that stay available in setupEntry / deferred channel startup |
createPatchedAccountSetupAdapter, createEnvPatchedAccountSetupAdapter, createSetupInputPresenceValidator, noteChannelLookupFailure, noteChannelLookupSummary, promptResolvedAllowFrom, splitSetupEntries, createAllowlistSetupWizardProxy, createDelegatedSetupWizardProxy |
plugin-sdk/setup-adapter-runtime |
environment-aware account setup adapters | createEnvPatchedAccountSetupAdapter |
plugin-sdk/setup-tools |
setup/install CLI/archive/docs helpers | formatCliCommand, detectBinary, extractArchive, resolveBrewExecutable, formatDocsLink, CONFIG_DIR |
Use the broader plugin-sdk/setup seam when you want the full shared setup toolbox, including config-patch helpers such as moveSingleAccountChannelSectionToDefaultAccount(...).
The setup patch adapters stay hot-path safe on import. Their bundled single-account promotion contract-surface lookup is lazy, so importing plugin-sdk/setup-runtime does not eagerly load bundled contract-surface discovery before the adapter is actually used.
Channel-owned single-account promotion
When a channel upgrades from a single-account top-level config to channels.<id>.accounts.*, the default shared behavior is to move promoted account-scoped values into accounts.default.
Bundled channels can narrow or override that promotion through their setup contract surface:
singleAccountKeysToMove: extra top-level keys that should move into the promoted accountnamedAccountPromotionKeys: when named accounts already exist, only these keys move into the promoted account; shared policy/delivery keys stay at the channel rootresolveSingleAccountPromotionTarget(...): choose which existing account receives promoted values
Config schema
Plugin config is validated against the JSON Schema in your manifest. Users configure plugins via:
{
plugins: {
entries: {
"my-plugin": {
config: {
webhookSecret: "abc123",
},
},
},
},
}
Your plugin receives this config as api.pluginConfig during registration.
For channel-specific config, use the channel config section instead:
{
channels: {
"my-channel": {
token: "bot-token",
allowFrom: ["user1", "user2"],
},
},
}
Building channel config schemas
Use buildChannelConfigSchema to convert a Zod schema into the ChannelConfigSchema wrapper used by plugin-owned config artifacts:
import { z } from "zod";
import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
const accountSchema = z.object({
token: z.string().optional(),
allowFrom: z.array(z.string()).optional(),
accounts: z.object({}).catchall(z.any()).optional(),
defaultAccount: z.string().optional(),
});
const configSchema = buildChannelConfigSchema(accountSchema);
For third-party plugins, the cold-path contract is still the plugin manifest: mirror the generated JSON Schema into openclaw.plugin.json#channelConfigs so config schema, setup, and UI surfaces can inspect channels.<id> without loading runtime code.
Setup wizards
Channel plugins can provide interactive setup wizards for openclaw onboard. The wizard is a ChannelSetupWizard object on the ChannelPlugin:
import type { ChannelSetupWizard } from "openclaw/plugin-sdk/channel-setup";
const setupWizard: ChannelSetupWizard = {
channel: "my-channel",
status: {
configuredLabel: "Connected",
unconfiguredLabel: "Not configured",
resolveConfigured: ({ cfg }) => Boolean((cfg.channels as any)?.["my-channel"]?.token),
},
credentials: [
{
inputKey: "token",
providerHint: "my-channel",
credentialLabel: "Bot token",
preferredEnvVar: "MY_CHANNEL_BOT_TOKEN",
envPrompt: "Use MY_CHANNEL_BOT_TOKEN from environment?",
keepPrompt: "Keep current token?",
inputPrompt: "Enter your bot token:",
inspect: ({ cfg, accountId }) => {
const token = (cfg.channels as any)?.["my-channel"]?.token;
return {
accountConfigured: Boolean(token),
hasConfiguredValue: Boolean(token),
};
},
},
],
};
The ChannelSetupWizard type supports credentials, textInputs, dmPolicy, allowFrom, groupAccess, prepare, finalize, and more. See bundled plugin packages (for example the Discord plugin src/channel.setup.ts) for full examples.
```typescript
import { createOptionalChannelSetupSurface } from "openclaw/plugin-sdk/channel-setup";
const setupSurface = createOptionalChannelSetupSurface({
channel: "my-channel",
label: "My Channel",
npmSpec: "@myorg/openclaw-my-channel",
docsPath: "/channels/my-channel",
});
// Returns { setupAdapter, setupWizard }
```
`plugin-sdk/channel-setup` also exposes the lower-level `createOptionalChannelSetupAdapter(...)` and `createOptionalChannelSetupWizard(...)` builders when you only need one half of that optional-install surface.
The generated optional adapter/wizard fail closed on real config writes. They reuse one install-required message across `validateInput`, `applyAccountConfig`, and `finalize`, and append a docs link when `docsPath` is set.
For binary-backed setup UIs, prefer the shared delegated helpers instead of copying the same binary/status glue into every channel:
- `createDetectedBinaryStatus(...)` for status blocks that vary only by labels, hints, scores, and binary detection
- `createCliPathTextInput(...)` for path-backed text inputs
- `createDelegatedSetupWizardStatusResolvers(...)`, `createDelegatedPrepare(...)`, `createDelegatedFinalize(...)`, and `createDelegatedResolveConfigured(...)` when `setupEntry` needs to forward to a heavier full wizard lazily
- `createDelegatedTextInputShouldPrompt(...)` when `setupEntry` only needs to delegate a `textInputs[*].shouldPrompt` decision
Publishing and installing
External plugins: publish to ClawHub or npm, then install:
```bash openclaw plugins install @myorg/openclaw-my-plugin ```OpenClaw tries ClawHub first and falls back to npm automatically.
```bash
openclaw plugins install clawhub:@myorg/openclaw-my-plugin
```
There is no matching `npm:` override. Use the normal npm package spec when you want the npm path after ClawHub fallback:
```bash
openclaw plugins install @myorg/openclaw-my-plugin
```
In-repo plugins: place under the bundled plugin workspace tree and they are automatically discovered during build.
Users can install:
openclaw plugins install <package-name>
Related
- Building plugins — step-by-step getting started guide
- Plugin manifest — full manifest schema reference
- SDK entry points —
definePluginEntryanddefineChannelPluginEntry