Install and use plugins
End-user guide for adding, enabling, and troubleshooting plugins.
Building plugins
First-plugin tutorial with the smallest working manifest.
Channel plugins
Build a messaging channel plugin.
Provider plugins
Build a model provider plugin.
SDK overview
Import map and registration API reference.
Public capability model
Capabilities are the public native plugin model inside OpenClaw. Native plugins can register one or more capability types:A plugin that registers only hooks is hook-only. Plugins with tools, commands, background services, or routes but no capabilities are non-capability plugins. Both patterns remain supported; gateway discovery is an explicit capability listed above.
External compatibility stance
The capability model is landed in core and used by bundled/native plugins today, but external plugin compatibility still needs a tighter bar than “it is exported, therefore it is frozen.”
Capability registration is the intended direction. Legacy hooks remain the safest no-breakage path for external plugins during the transition. Exported helper subpaths are not all equal — prefer narrow documented contracts over incidental helper exports.
Plugin shapes
OpenClaw classifies every loaded plugin into a shape based on its actual registration behavior (not just static metadata):plain-capability
plain-capability
Registers exactly one capability type (for example a provider-only plugin like
arcee or chutes).hybrid-capability
hybrid-capability
Registers multiple capability types (for example
openai owns text inference, speech, media understanding, and image generation).hook-only
hook-only
Registers only hooks (typed or custom), no capabilities, tools, commands, or services.
non-capability
non-capability
Registers tools, commands, services, or routes but no capabilities.
openclaw plugins inspect <id> to see a plugin’s shape and capability breakdown. See CLI reference for details.
Compatibility signals
openclaw doctor, openclaw plugins inspect <id>, openclaw status --all, and openclaw plugins doctor surface these compatibility notices:
None of the advisory/warn signals break your plugin today. These signals also appear in
openclaw status --all and openclaw plugins doctor.
Architecture overview
OpenClaw’s plugin system has four layers:1
Manifest + discovery
OpenClaw finds candidate plugins from configured paths, workspace roots, global plugin roots, and bundled plugins. Discovery reads native
openclaw.plugin.json manifests plus supported bundle manifests first.2
Enablement + validation
Core decides whether a discovered plugin is enabled, disabled, blocked, or selected for an exclusive slot such as memory.
3
Runtime loading
Native OpenClaw plugins are loaded in-process and register capabilities into a central registry. Packaged JavaScript loads through native
require; third-party local source TypeScript is the emergency Jiti fallback. Compatible bundles are normalized into registry records without importing runtime code.4
Surface consumption
The rest of OpenClaw reads the registry to expose tools, channels, provider setup, hooks, HTTP routes, CLI commands, and services.
- parse-time metadata comes from
registerCli(..., { descriptors: [...] }) - the real plugin CLI module can stay lazy and register on first invocation
- manifest/config validation should work from manifest/schema metadata without executing plugin code
- native capability discovery may load trusted plugin entry code to build a non-activating registry snapshot
- native runtime behavior comes from the plugin module’s
register(api)path withapi.registrationMode === "full"
Plugin metadata snapshot and lookup table
OnePluginCache starts on the first plugin metadata access, including CLI preflight before Gateway startup, and fills progressively as metadata and artifacts are needed. Gateway startup retains that owner and builds its immutable PluginMetadataSnapshot. The snapshot includes plugin metadata from all configured agent workspaces, including disabled plugins, with source precedence and workspace provenance preserved. It stores the installed plugin index, manifest registry, manifest diagnostics, owner maps, and a plugin id normalizer. Package contents and lazily loaded module exports belong to other typed views of the same cache, not the snapshot itself.
Plugin-aware config validation, startup auto-enable, and Gateway plugin bootstrap consume that snapshot instead of rebuilding manifest/index metadata independently. PluginLookUpTable is derived from the same snapshot and adds the startup plugin plan for the current runtime config.
Channel setup catalogs retain the requested workspace and load-path scope, including raw plugin shadows, so trust filtering can select the appropriate installed alternative.
After startup, runtime readers reuse that inventory without filesystem discovery, manifest rereads, or freshness checks. Narrow plugin selections are in-memory views of the same inventory. Changing config, account state, or an agent’s run workspace does not invalidate it. Plugin installs, updates, removals, manifest edits, and discovery-root changes become visible to the runtime after a Gateway restart.
Model-id normalization policies are prepared with each snapshot or narrowed view. Model selection, catalogs, and runtime normalization carry that view forward instead of rebuilding policies from its plugin list. An empty view remains authoritative and cannot inherit policies from a broader process snapshot.
The snapshot and lookup table keep repeated startup decisions on the fast path:
- channel ownership
- startup plugin planning
- startup plugin ids
- provider and CLI backend ownership
- setup provider, command alias, model catalog provider, and manifest contract ownership
- plugin config schema and channel config schema validation
- startup auto-enable decisions
openclaw plugins inspect <id> --runtime --json. Use openclaw doctor --fix for supported installation repairs, or fix the reported problem in plugin code, then restart the Gateway to load the repaired plugin.
Each plugin service startup attempt owns one cleanup operation, including failed starts. Hot replacement uses a five-second cleanup deadline; a timeout revokes the old service’s capabilities and rejects the replacement. Final Gateway shutdown waits up to five seconds before continuing independent teardown, then joins the same cleanup before retiring shared plugin state, registries, secrets, and metadata. It does not invoke the service’s stop handler again.
The cache rule is documented in Plugin architecture internals: Gateway retains one cache generation, while explicit management operations use isolated generations of the same cache. There are no wall-clock TTLs for Gateway metadata.
Install, update, registry refresh, and doctor flows may read fresh package metadata to validate their changes. Their snapshots and installed-index writes do not replace the running Gateway’s inventory. Runtime flows must use the startup snapshot or its lookup table instead of falling back to those cold management paths.
Activation planning
Activation planning is part of the control plane. Callers can ask which plugins are relevant to a concrete command, provider, channel, route, agent harness, or capability before loading broader runtime registries. The planner keeps current manifest behavior compatible:activation.*fields are explicit planner hintsproviders,channels,commandAliases,setup.providers,contracts.tools, and hooks remain manifest ownership fallback- the ids-only planner API stays available for existing callers
- the plan API reports reason labels so diagnostics can distinguish explicit hints from ownership fallback
Channel plugins and the shared message tool
Channel plugins do not need to register a separate send/edit/react tool for normal chat actions. OpenClaw keeps one sharedmessage tool in core, and channel plugins own the channel-specific discovery and execution behind it.
The current boundary is:
- core owns the shared
messagetool host, prompt wiring, session/thread bookkeeping, and execution dispatch - channel plugins own scoped action discovery, capability discovery, and any channel-specific schema fragments
- channel plugins own provider-specific session conversation grammar, such as how conversation ids encode thread ids or inherit from parent conversations
- channel plugins execute the final action through their action adapter
ChannelMessageActionAdapter.describeMessageTool(...). That unified discovery call lets a plugin return its visible actions, capabilities, and schema contributions together so those pieces do not drift apart.
Message action names use a deliberately closed, core-owned vocabulary so every transport can render every action. Plugins add action names through a core PR; runtime registration is intentionally unsupported.
When a channel-specific message-tool param carries a media source such as a local path or remote media URL, the plugin should also return mediaSourceParams from describeMessageTool(...). Core uses that explicit list to apply sandbox path normalization and outbound media-access hints without hardcoding plugin-owned param names. Prefer action-scoped maps there, not one channel-wide flat list, so a profile-only media param does not get normalized on unrelated actions like send.
Core passes runtime scope into that discovery step. Important fields include:
accountIdcurrentChannelIdchatType(direct,group, orchannelwhen the inbound route establishes it)currentThreadTscurrentMessageIdsessionKeysessionIdagentId- trusted inbound
requesterSenderId
message tool. Treat chatType as discovery scope supplied by the current inbound route, not something to infer again from an opaque channel id; it is absent when that route did not establish the conversation type.
This is why embedded-runner routing changes are still plugin work: the runner is responsible for forwarding the current chat/session identity into the plugin discovery boundary so the shared message tool exposes the right channel-owned surface for the current turn.
For channel-owned execution helpers, channel plugins should keep the execution runtime inside their own plugin modules. Core no longer owns the Discord, Slack, Telegram, or WhatsApp message-action runtimes under src/agents/tools. We do not publish separate plugin-sdk/*-action-runtime subpaths, and those plugins should import their own local runtime code directly from their plugin-owned modules.
The same boundary applies to provider-named SDK seams in general: core should not import channel-specific convenience barrels for Discord, Signal, Slack, WhatsApp, or similar plugins. If core needs a behavior, either consume the bundled plugin’s own api.ts / runtime-api.ts barrel or promote the need into a narrow generic capability in the shared SDK.
Bundled plugins follow the same rule. A bundled plugin’s runtime-api.ts should not re-export its own branded openclaw/plugin-sdk/<plugin-id> facade. Those branded facades remain compatibility shims for external plugins and older consumers, but bundled plugins should use local exports plus narrow generic SDK subpaths such as openclaw/plugin-sdk/channel-policy, openclaw/plugin-sdk/runtime-store, or openclaw/plugin-sdk/webhook-ingress. New code should not add plugin-id-specific SDK facades unless the compatibility boundary for an existing external ecosystem requires it.
For polls specifically, there are two execution paths:
outbound.sendPollis the shared baseline for channels that fit the common poll modelactions.handleAction("poll")is the preferred path for channel-specific poll semantics or extra poll parameters
Capability ownership model
OpenClaw treats a native plugin as the ownership boundary for a company or a feature, not as a grab bag of unrelated integrations. That means:- a company plugin should usually own all of that company’s OpenClaw-facing surfaces
- a feature plugin should usually own the full feature surface it introduces
- channels should consume shared core capabilities instead of re-implementing provider behavior ad hoc
Vendor multi-capability
Vendor multi-capability
google owns text inference, CLI backend, embeddings, speech, realtime voice, media understanding, image/music/video generation, and web search. openai owns text inference, embeddings, speech, realtime transcription, realtime voice, media understanding, image/video generation. minimax owns text inference plus media understanding, speech, image/music/video generation, and web search.Vendor single-capability
Vendor single-capability
arcee and chutes own text inference only; microsoft owns speech only. A vendor plugin can stay this narrow until it needs to cover more of that vendor’s surface.Feature plugin
Feature plugin
voice-call owns call transport, tools, CLI, routes, and Twilio media-stream bridging, but consumes shared speech, realtime transcription, and realtime voice capabilities instead of importing vendor plugins directly.- a vendor’s OpenClaw-facing surface lives in one plugin even if it spans text models, speech, images, and video
- other vendors can do the same for their own surface area
- channels do not care which vendor plugin owns the provider; they consume the shared capability contract exposed by core
- plugin = ownership boundary
- capability = core contract that multiple plugins can implement or consume
1
Define the capability
Define the missing capability in core.
2
Expose through the SDK
Expose it through the plugin API/runtime in a typed way.
3
Wire consumers
Wire channels/features against that capability.
4
Vendor implementations
Let vendor plugins register implementations.
Capability layering
Use this mental model when deciding where code belongs:- Core capability layer
- Vendor plugin layer
- Channel/feature plugin layer
Shared orchestration, policy, fallback, config merge rules, delivery semantics, and typed contracts.
- core owns reply-time TTS policy, fallback order, prefs, and channel delivery
elevenlabs,google,microsoft, andopenaiown synthesis implementationsvoice-callconsumes the telephony TTS runtime helper
Multi-capability company plugin example
A company plugin should feel cohesive from the outside. If OpenClaw has shared contracts for models, speech, realtime transcription, realtime voice, media understanding, image generation, video generation, web fetch, and web search, a vendor can own all of its surfaces in one place:- one plugin owns the vendor surface
- core still owns the capability contracts
- provider request translation and HTTP helpers stay in the vendor plugin
- channels and feature plugins consume
api.runtime.*helpers, not vendor code - contract tests can assert that the plugin registered the capabilities it claims to own
Capability example: video understanding
OpenClaw already treats image/audio/video understanding as one shared capability. The same ownership model applies there:1
Core defines the contract
Core defines the media-understanding contract.
2
Vendor plugins register
Vendor plugins register
describeImage, transcribeAudio, and describeVideo as applicable.3
Consumers use the shared behavior
Channels and feature plugins consume the shared core behavior instead of wiring directly to vendor code.
api.registerVideoGenerationProvider(...) implementations against it.
Need a concrete rollout checklist? See Adding capabilities.
Contracts and enforcement
The plugin API surface is intentionally typed and centralized inOpenClawPluginApi. That contract defines the supported registration points and the runtime helpers a plugin may rely on.
Why this matters:
- plugin authors get one stable internal standard
- core can reject duplicate ownership such as two plugins registering the same provider id
- startup can surface actionable diagnostics for malformed registration
- contract tests can enforce bundled-plugin ownership and prevent silent drift
Runtime registration enforcement
Runtime registration enforcement
The plugin registry validates registrations as plugins load. Examples: duplicate provider ids, duplicate speech provider ids, and malformed registrations produce plugin diagnostics instead of undefined behavior.
Contract tests
Contract tests
Bundled plugins are captured in contract registries during test runs so OpenClaw can assert ownership explicitly. Today this is used for model providers, speech providers, web search providers, and bundled registration ownership.
What belongs in a contract
- Good contracts
- Bad contracts
- typed
- small
- capability-specific
- owned by core
- reusable by multiple plugins
- consumable by channels/features without vendor knowledge
Execution model
Native OpenClaw plugins run in-process with the Gateway. They are not sandboxed. A loaded native plugin has the same process-level trust boundary as core code. Compatible bundles are safer by default because OpenClaw currently treats them as metadata/content packs. In current releases, that mostly means bundled skills. Use allowlists and explicit install/load paths for non-bundled plugins. Treat workspace plugins as development-time code, not production defaults. For bundled workspace package names, keep the plugin id anchored in the npm name:@openclaw/<id> by default, or an approved typed suffix such as -provider, -plugin, -speech, -sandbox, or -media-understanding when the package intentionally exposes a narrower plugin role.
Trust note:
plugins.allow permits plugin ids to load; it does not verify source provenance or choose which same-id copy loads. An auto-discovered workspace plugin does not shadow a bundled plugin merely because that id is enabled or allowlisted.For intentional local overrides, use plugins.load.paths to select the plugin path. Tracked global installs can also override ordinary bundled copies, while bundled plugins from OPENCLAW_DEV_SOURCE_ROOT retain priority over tracked globals. See Discovery precedence for the full order.An alias of the same independently validated bundled entry retains bundled provenance; a different local copy does not inherit trust from its name or allowlist entry. Checkout runners supply the development selector automatically, including for compiled plugins. See development debugging.Bundled-plugin trust is resolved from the source snapshot — the manifest and code on disk at load time — rather than from install metadata. A corrupted or substituted install record cannot silently widen a bundled plugin’s trust surface beyond what the actual source claims.Export boundary
OpenClaw exports capabilities, not implementation convenience. Keep capability registration public. Trim non-contract helper exports:- bundled-plugin-specific helper subpaths
- runtime plumbing subpaths not intended as public API
- vendor-specific convenience helpers
- setup/onboarding helpers that are implementation details
plugin-sdk/gateway-runtime, plugin-sdk/security-runtime, and injected plugin API capabilities.