package.json metadata), manifests (openclaw.plugin.json), setup entries, and config schemas.
Package metadata
Yourpackage.json needs an openclaw field that tells the plugin system what your plugin provides:
- Channel plugin
- Provider plugin / ClawHub baseline
Publishing externally on ClawHub requires
compat and build. Canonical publish snippets live in docs/snippets/plugin-publish/.openclaw fields
string[]
Entry point files (relative to package root). Valid source entries for workspace and git checkout development.
string[]
Built JavaScript peers for
extensions, preferred when OpenClaw loads an installed npm package. See SDK entry points for the source/built resolution order.string
Lightweight setup-only entry (optional).
string
Built JavaScript peer for
setupEntry. Requires setupEntry to also be set.object
{ id, label } fallback plugin identity, used when a plugin has no channel/provider metadata to derive an id or label from.object
Channel catalog metadata for setup, picker, quickstart, and status surfaces.
object
Install hints:
npmSpec, localPath, defaultChoice, minHostVersion, expectedIntegrity, allowInvalidConfigRecovery, requiredPlatformPackages.object
Startup behavior flags.
object
pluginApi version range this plugin supports. Required for external ClawHub publishes.Provider ids (
providers: string[]) are manifest metadata, not package metadata. Declare them in openclaw.plugin.json, not here — see Plugin manifest.openclaw.channel
openclaw.channel is cheap package metadata for channel discovery and setup surfaces before runtime loads.
Channel-owned setup fields
Channel plugins should define setup fields once in runtime code withdefineChannelSetupContract(...) and publish the matching serializable projection under openclaw.channel.setup.fields. The runtime definition infers the plugin-local input type, parses both guided and non-interactive values, and keeps channel-specific keys out of core types. Package metadata lets openclaw channels add <channel-id> --help and openclaw channels add --channel <channel-id> --help discover only the selected channel’s options without loading the plugin.
string, boolean, integer, string-list, and choice. Use sensitive: true for credentials. Each field key must equal the camelCased attribute name of its long CLI flag, including any negated form, such as apiToken for --api-token. Boolean fields may add cli.negatedFlags when both positive and --no-* forms are needed. channel, account, and the account display name remain the shared control envelope.
The released setup/ChannelSetupInput adapter stays available for existing external plugins. New plugins should expose setupContract; OpenClaw always prefers it when both are present.
Example:
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.
Onboarding behavior
Onboarding behavior
Interactive onboarding uses
openclaw.install for install-on-demand surfaces: if your plugin exposes provider auth choices or channel setup/catalog metadata before runtime loads, onboarding can prompt for ClawHub, npm, or local install, install or enable the plugin, then continue the selected flow. ClawHub choices use clawhubSpec and are preferred when present; npm choices require trusted catalog metadata with a registry npmSpec (exact versions and expectedIntegrity are optional pins, enforced on install/update when set). Keep “what to show” in openclaw.plugin.json and “how to install it” in package.json.minHostVersion enforcement
minHostVersion enforcement
If
minHostVersion is set, install and non-bundled manifest-registry loading both enforce it. Older hosts skip external plugins; invalid version strings are rejected. Bundled source plugins are assumed to be co-versioned with the host checkout.Pinned npm installs
Pinned npm installs
For pinned npm installs, keep the exact version in
npmSpec and add the expected artifact integrity:allowInvalidConfigRecovery scope
allowInvalidConfigRecovery scope
allowInvalidConfigRecovery is not a general bypass for broken configs. It is narrow bundled-plugin recovery only, letting reinstall/setup repair known upgrade leftovers like a missing bundled plugin path or a stale channels.<id> 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.Setup-time gateway methods
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 normalize to operator.admin.
Plugin manifest
Every native plugin must ship anopenclaw.plugin.json in the package root. OpenClaw uses this to validate config without executing plugin code.
channels (and provider plugins add providers):
ClawHub publishing
Skills and plugin packages use separate ClawHub publish commands. For plugin packages, use the package-specific command:clawhub skill publish <path> is a different command for publishing a skill folder, not a plugin package. See Publishing on ClawHub.Setup entry
setup-entry.ts is a lightweight alternative to index.ts that OpenClaw loads when it only needs setup surfaces (onboarding, config repair, disabled channel inspection):
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.
When OpenClaw uses setupEntry instead of the full entry
When OpenClaw uses setupEntry instead of the full entry
- The channel is disabled but needs setup/onboarding surfaces.
- The channel is enabled but unconfigured.
What setupEntry must register
What setupEntry must register
- The channel plugin object (via
defineSetupPluginEntry). - Setup-time runtime surfaces declared through
registerSetupRuntime, when needed.
config.* or update.*.What setupEntry should NOT include
What setupEntry should NOT include
- 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 broaderplugin-sdk/setup umbrella when you only need part of the setup surface:
Use the broader
plugin-sdk/setup seam when you want the full shared setup toolbox, including config-patch helpers such as moveSingleAccountChannelSectionToDefaultAccount(...).
Use createSetupTranslator(...) for fixed setup wizard copy. It uses the first nonblank value from OPENCLAW_LOCALE, LC_ALL, LC_MESSAGES, and LANG, in that order, then falls back to English. Set OPENCLAW_LOCALE=en for an explicit English override. Keep plugin-specific setup text in plugin-owned code and use shared catalog keys only for common setup labels, status text, and official bundled plugin setup copy.
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 setup input fields
ChannelSetupInput is a generic envelope shared by setup callers and channel
plugins. Its permanently typed fields are name, token, tokenFile,
useEnv, allowFrom, and defaultTo. Additional plugin-owned keys can still
be present on the runtime input object, but the shared type does not declare an
index signature. Each plugin must declare and narrow its own setup fields or
validate them with a plugin-owned schema at the adapter boundary:
ChannelSetupInput remain temporarily typed for external source compatibility.
They are deprecated. A 2026-07-22 registry sweep of 426 published out-of-tree
channel plugins removed 21 fields with no readers and retained 22 with known
readers. Each retained field is deleted as soon as no published plugin reads it;
no version boundary is required. New and bundled plugins must not rely on this
tier; declare the fields they own locally.
Channel-owned single-account promotion
When a channel upgrades from a single-account top-level config tochannels.<id>.accounts.*, the default shared behavior moves promoted account-scoped values into accounts.default.
Every channel plugin can extend or narrow that promotion through its setup adapter:
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
singleAccountKeysToMove marks the promotion contract complete. Declare the field even when it is an empty array to opt out of legacy key promotion. Adapters that omit the field retain a reader-backed pre-declaration promotion tier for already-published plugins. The 2026-07-22 registry sweep removed 23 keys with no published dependents and retained six common keys plus the setup-only rooms key. Each retained key is deleted as soon as its published readers migrate to declarations; no version boundary is required.
Declare openclaw.setupFeatures.configPromotion: true in the plugin package manifest when doctor must load these declarations from the lightweight bundled setup artifact. The setup-only plugin surface and the full channel plugin must expose the same declarations.
When calling moveSingleAccountChannelSectionToDefaultAccount(...) with an already resolved plugin, pass its setup adapter as setupSurface. Caller-supplied setup surfaces take precedence over loaded and bundled lookup, which keeps scoped or setup-only plugins independent of global registration.
Matrix is the current bundled example. If exactly one named Matrix account already exists, or if
defaultAccount points at an existing non-canonical key such as Ops, promotion preserves that account instead of creating a new accounts.default entry.Config schema
Plugin config is validated against the JSON Schema in your manifest. Users configure plugins via:api.pluginConfig during registration.
For channel-specific config, use the channel config section instead:
Building channel config schemas
UsebuildChannelConfigSchema to convert a Zod schema into the ChannelConfigSchema wrapper used by plugin-owned config artifacts:
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 foropenclaw onboard. The wizard is a ChannelSetupWizard object on the ChannelPlugin:
ChannelSetupWizard also supports textInputs, dmPolicy, allowFrom, groupAccess, prepare, finalize, and more. See the Discord plugin’s src/setup-core.ts for a full bundled example.
Standard channel setup status
Standard channel setup status
For channel setup status blocks that only vary by labels, scores, and optional extra lines, prefer
createStandardChannelSetupStatus(...) from openclaw/plugin-sdk/setup instead of hand-rolling the same status object in each plugin.Optional channel setup surface
Optional channel setup surface
For optional setup surfaces that should only appear in certain contexts, use
createOptionalChannelSetupSurface from openclaw/plugin-sdk/channel-setup: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.Binary-backed setup helpers
Binary-backed setup helpers
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 detectioncreateCliPathTextInput(...)for path-backed text inputscreateDelegatedSetupWizardProxy(...)whensetupEntryneeds to forward status, prepare, or finalize behavior to a heavier full wizard lazilycreateDelegatedTextInputShouldPrompt(...)whensetupEntryonly needs to delegate atextInputs[*].shouldPromptdecision
Publishing and installing
External plugins: publish to ClawHub, then install:- npm
- ClawHub only
- npm package spec
clawhub:, npm:, git:, or npm-pack: for deterministic source selection — see Manage plugins.For npm-sourced installs,
openclaw plugins install installs the package into a per-plugin project under ~/.openclaw/npm/projects with lifecycle scripts disabled (--ignore-scripts). Keep plugin dependency trees pure JS/TS and avoid packages that require postinstall builds.Gateway startup does not install plugin dependencies. npm/git/ClawHub install flows own dependency convergence; local plugins must already have their dependencies installed.
Related
- Building plugins — step-by-step getting started guide
- Plugin manifest — full manifest schema reference
- SDK entry points —
definePluginEntryanddefineChannelPluginEntry