Configure OpenClaw Plugins: Create Your Manifest File
This guide covers the native OpenClaw plugin manifest. If you are using compatible bundle layouts, check Plugin bundles instead.
Compatible bundle formats use different manifest files:
- Codex bundles (
.codex-plugin/plugin.json) - Claude bundles (
.claude-plugin/plugin.jsonor default layouts) - Cursor bundles (
.cursor-plugin/plugin.json) - Other auto-detected layouts
OpenClaw detects these layouts automatically, but it does not validate them against the openclaw.plugin.json schema described here.
For compatible bundles, OpenClaw reads bundle metadata, skill roots, Claude command roots, and settings.json defaults. It also looks for Claude bundle LSP defaults and supported hook packs when the layout matches runtime expectations.
Every native OpenClaw plugin must include an openclaw.plugin.json file in the plugin root. OpenClaw uses this manifest to validate configuration without executing any plugin code. Missing or invalid manifests are treated as errors and will block configuration validation. This ensures your setup is correct before the runtime starts.
You can find the full plugin system guide at Plugins. For the native capability model and external-compatibility guidance, see the Capability model.
What this file does
Section titled “What this file does”The openclaw.plugin.json file contains the metadata OpenClaw reads before it loads your plugin code.
Use it for:
- plugin identity
- config validation
- auth and onboarding metadata that should be available without booting plugin runtime
- alias and auto-enable metadata that should resolve before plugin runtime loads
- shorthand model-family ownership metadata that should auto-activate the plugin before runtime loads
- static capability ownership snapshots used for bundled compat wiring and contract coverage
- channel-specific config metadata that should merge into catalog and validation surfaces without loading runtime
- config UI hints
Do not use it for:
- registering runtime behavior and declaring code entrypoints
- npm install metadata
These details belong in your actual plugin code and your package.json file.
Minimal example
Section titled “Minimal example”{ "id": "voice-call", "configSchema": { "type": "object", "additionalProperties": false, "properties": {} }}Rich example
Section titled “Rich example”{ "id": "openrouter", "name": "OpenRouter", "description": "OpenRouter provider plugin", "version": "1.0.0", "providers": ["openrouter"], "modelSupport": { "modelPrefixes": ["router-"] }, "cliBackends": ["openrouter-cli"], "providerAuthEnvVars": { "openrouter": ["OPENROUTER_API_KEY"] }, "providerAuthAliases": { "openrouter-coding": "openrouter" }, "channelEnvVars": { "openrouter-chatops": ["OPENROUTER_CHATOPS_TOKEN"] }, "providerAuthChoices": [ { "provider": "openrouter", "method": "api-key", "choiceId": "openrouter-api-key", "choiceLabel": "OpenRouter API key", "groupId": "openrouter", "groupLabel": "OpenRouter", "optionKey": "openrouterApiKey", "cliFlag": "--openrouter-api-key", "cliOption": "--openrouter-api-key <key>", "cliDescription": "OpenRouter API key", "onboardingScopes": ["text-inference"] } ], "uiHints": { "apiKey": { "label": "API key", "placeholder": "sk-or-v1-...", "sensitive": true } }, "configSchema": { "type": "object", "additionalProperties": false, "properties": { "apiKey": { "type": "string" } } }}Top-level field reference
Section titled “Top-level field reference”| Field | Required | Type | What it means |
|---|---|---|---|
id | Yes | string | Canonical plugin id. This is the id used in plugins.entries.<id>. |
configSchema | Yes | object | Inline JSON Schema for this plugin’s config. |
enabledByDefault | No | true | Marks a bundled plugin as enabled by default. Omit it, or set any non-true value, to leave the plugin disabled by default. |
legacyPluginIds | No | string[] | Legacy ids that normalize to this canonical plugin id. |
autoEnableWhenConfiguredProviders | No | string[] | Provider ids that should auto-enable this plugin when auth, config, model refs, or other settings mention them. |
kind | No | "memory" | "context-engine" | Declares an exclusive plugin kind used by plugins.slots.*. |
channels | No | string[] | Channel ids owned by this plugin. Used for discovery and config validation. |
providers | No | string[] | Provider ids owned by this plugin. |
modelSupport | No | object | Manifest-owned shorthand model-family metadata used to auto-load the plugin before runtime. |
cliBackends | No | string[] | CLI inference backend ids owned by this plugin. Used for startup auto-activation from explicit config refs. |
commandAliases | No | object[] | Command names owned by this plugin that should produce plugin-aware config and CLI diagnostics before runtime loads. |
providerAuthEnvVars | No | Record<string, string[]> | Cheap provider-auth env metadata that OpenClaw can inspect without loading plugin code. |
providerAuthAliases | No | Record<string, string> | Provider ids that should reuse another provider id for auth lookup, for example a coding provider that shares the base provider API key and auth profiles. |
channelEnvVars | No | Record<string, string[]> | Cheap channel env metadata that OpenClaw can inspect without loading plugin code. Use this for env-driven channel setup or auth surfaces that generic startup/config helpers should see. |
providerAuthChoices | No | object[] | Cheap auth-choice metadata for onboarding pickers, preferred-provider resolution, simple CLI flag wiring, and setup helpers. |
contracts | No | object | Static bundled capability snapshot for speech, realtime transcription, realtime voice, media-understanding, image-generation, music-generation, video-generation, web-fetch, web search, and tool ownership. |
channelConfigs | No | Record<string, object> | Manifest-owned channel config metadata merged into discovery and validation surfaces before runtime loads. |
skills | No | string[] | Skill directories to load, relative to the plugin root. |
name | No | string | Human-readable plugin name. |
description | No | string | Short summary shown in plugin surfaces. |
version | No | string | Informational plugin version. |
uiHints | No | Record<string, object> | UI labels, placeholders, sensitivity hints, and display options for config fields. |
providerAuthChoices reference
Section titled “providerAuthChoices reference”You use providerAuthChoices to define how users handle onboarding or authentication. OpenClaw checks these entries before the provider runtime even starts.
| Field | Required | Type | What it means |
|---|---|---|---|
provider | Yes | string | Provider id this choice belongs to. |
method | Yes | string | Auth method id to dispatch to. |
choiceId | Yes | string | Stable auth-choice id used by onboarding and CLI flows. |
choiceLabel | No | string | User-facing label. If omitted, OpenClaw falls back to choiceId. |
choiceHint | No | string | Short helper text for the picker. |
assistantPriority | No | number | Lower values sort earlier in assistant-driven interactive pickers. |
assistantVisibility | No | "visible" | "manual-only" | Hide the choice from assistant pickers while still allowing manual CLI selection. |
deprecatedChoiceIds | No | string[] | Legacy choice ids that should redirect users to this replacement choice. |
groupId | No | string | Optional group id for grouping related choices. |
groupLabel | No | string | User-facing label for that group. |
groupHint | No | string | Short helper text for the group. |
optionKey | No | string | Internal option key for simple one-flag auth flows. |
cliFlag | No | string | CLI flag name, such as --openrouter-api-key. |
cliOption | No | string | Full CLI option shape, such as --openrouter-api-key <key>. |
cliDescription | No | string | Description used in CLI help. |
onboardingScopes | No | Array<"text-inference" | "image-generation"> | Which onboarding surfaces this choice should appear in. If omitted, it defaults to ["text-inference"]. |
commandAliases reference
Section titled “commandAliases reference”You should use commandAliases if your plugin owns a runtime command that users might accidentally put in plugins.allow or try to run as a root CLI command. This helps OpenClaw provide diagnostics without needing to import your plugin’s runtime code.
{ "commandAliases": [ { "name": "dreaming", "kind": "runtime-slash", "cliCommand": "memory" } ]}| Field | Required | Type | What it means |
|---|---|---|---|
name | Yes | string | Command name that belongs to this plugin. |
kind | No | "runtime-slash" | Marks the alias as a chat slash command rather than a root CLI command. |
cliCommand | No | string | Related root CLI command to suggest for CLI operations, if one exists. |
uiHints reference
Section titled “uiHints reference”The uiHints map lets you attach small rendering hints to your config fields. It helps you control how settings appear to the user.
{ "uiHints": { "apiKey": { "label": "API key", "help": "Used for OpenRouter requests", "placeholder": "sk-or-v1-...", "sensitive": true } }}Each field hint can include:
| Field | Type | What it means |
|---|---|---|
label | string | User-facing field label. |
help | string | Short helper text. |
tags | string[] | Optional UI tags. |
advanced | boolean | Marks the field as advanced. |
sensitive | boolean | Marks the field as secret or sensitive. |
placeholder | string | Placeholder text for form inputs. |
contracts reference
Section titled “contracts reference”You should use contracts strictly for static metadata about which capabilities your plugin owns. This allows OpenClaw to check these capabilities without importing the plugin runtime.
{ "contracts": { "speechProviders": ["openai"], "realtimeTranscriptionProviders": ["openai"], "realtimeVoiceProviders": ["openai"], "mediaUnderstandingProviders": ["openai", "openai-codex"], "imageGenerationProviders": ["openai"], "videoGenerationProviders": ["qwen"], "webFetchProviders": ["firecrawl"], "webSearchProviders": ["gemini"], "tools": ["firecrawl_search", "firecrawl_scrape"] }}Each list is optional:
| Field | Type | What it means |
|---|---|---|
speechProviders | string[] | Speech provider ids this plugin owns. |
realtimeTranscriptionProviders | string[] | Realtime-transcription provider ids this plugin owns. |
realtimeVoiceProviders | string[] | Realtime-voice provider ids this plugin owns. |
mediaUnderstandingProviders | string[] | Media-understanding provider ids this plugin owns. |
imageGenerationProviders | string[] | Image-generation provider ids this plugin owns. |
videoGenerationProviders | string[] | Video-generation provider ids this plugin owns. |
webFetchProviders | string[] | Web-fetch provider ids this plugin owns. |
webSearchProviders | string[] | Web-search provider ids this plugin owns. |
tools | string[] | Agent tool names this plugin owns for bundled contract checks. |
channelConfigs reference
Section titled “channelConfigs reference”You should use channelConfigs when a channel plugin needs access to config metadata before the runtime actually loads. It is a great way to keep things fast by providing necessary details early.
{ "channelConfigs": { "matrix": { "schema": { "type": "object", "additionalProperties": false, "properties": { "homeserverUrl": { "type": "string" } } }, "uiHints": { "homeserverUrl": { "label": "Homeserver URL", "placeholder": "https://matrix.example.com" } }, "label": "Matrix", "description": "Matrix homeserver connection", "preferOver": ["matrix-legacy"] } }}Each channel entry can include these fields:
| Field | Type | What it means |
|---|---|---|
schema | object | JSON Schema for channels.<id>. You must include this for each declared channel config entry. |
uiHints | Record<string, object> | Optional UI labels, placeholders, or hints for sensitive data in that config section. |
label | string | A label for the channel used in pickers and inspect views when runtime metadata isn’t ready. |
description | string | A short description for the channel used in inspect and catalog views. |
preferOver | string[] | IDs of legacy or lower-priority plugins that this channel should rank higher than in the UI. |
modelSupport reference
Section titled “modelSupport reference”You can use modelSupport to help OpenClaw identify your provider plugin from shorthand model IDs like gpt-5.4 or claude-sonnet-4.6. This identification happens before the plugin runtime loads.
{ "modelSupport": { "modelPrefixes": ["gpt-", "o1", "o3", "o4"], "modelPatterns": ["^computer-use-preview"] }}OpenClaw follows this specific order of precedence:
- Explicit
provider/modelreferences use the metadata from the owningprovidersmanifest. modelPatternsalways beatmodelPrefixes.- If both a non-bundled plugin and a bundled plugin match, the non-bundled plugin wins.
- Any remaining ambiguity is ignored until you or the config specify a provider.
Here are the fields you can use:
| Field | Type | What it means |
|---|---|---|
modelPrefixes | string[] | Prefixes that match shorthand model IDs using startsWith. |
modelPatterns | string[] | Regex sources matched against shorthand model IDs after any profile suffix is removed. |
Keep in mind that legacy top-level capability keys are deprecated. You should use openclaw doctor --fix to move speechProviders, realtimeTranscriptionProviders, realtimeVoiceProviders, mediaUnderstandingProviders, imageGenerationProviders, videoGenerationProviders, webFetchProviders, and webSearchProviders under contracts. Normal manifest loading won’t treat those top-level fields as capability ownership anymore.
Manifest versus package.json
Section titled “Manifest versus package.json”When you’re building a plugin, you’ll work with two main files. They have different roles, and it’s important to know which one handles what.
| File | Use it for |
|---|---|
openclaw.plugin.json | Discovery, config validation, auth-choice metadata, and UI hints that must exist before plugin code runs |
package.json | npm metadata, dependency installation, and the openclaw block used for entrypoints, install gating, setup, or catalog metadata |
If you aren’t sure where a piece of metadata belongs, you can follow this rule:
- If OpenClaw needs to know it before loading your plugin code, put it in
openclaw.plugin.json. - If it is about packaging, entry files, or npm install behavior, put it in
package.json.
package.json fields that affect discovery
Section titled “package.json fields that affect discovery”Some pre-runtime plugin metadata lives in your package.json under the openclaw block instead of the manifest.
Here are the important examples:
| Field | What it means |
|---|---|
openclaw.extensions | Declares native plugin entrypoints. |
openclaw.setupEntry | Lightweight setup-only entrypoint used during onboarding and deferred channel startup. |
openclaw.channel | Cheap channel catalog metadata like labels, docs paths, aliases, and selection copy. |
openclaw.channel.configuredState | Lightweight configured-state checker metadata that can answer “does env-only setup already exist?” without loading the full channel runtime. |
openclaw.channel.persistedAuthState | Lightweight persisted-auth checker metadata that can answer “is anything already signed in?” without loading the full channel runtime. |
openclaw.install.npmSpec / openclaw.install.localPath | Install/update hints for bundled and externally published plugins. |
openclaw.install.defaultChoice | Preferred install path when multiple install sources are available. |
openclaw.install.minHostVersion | Minimum supported OpenClaw host version, using a semver floor like >=2026.3.22. |
openclaw.install.allowInvalidConfigRecovery | Allows a narrow bundled-plugin reinstall recovery path when config is invalid. |
openclaw.startup.deferConfiguredChannelFullLoadUntilAfterListen | Lets setup-only channel surfaces load before the full channel plugin during startup. |
OpenClaw enforces openclaw.install.minHostVersion during install and manifest registry loading. If you use an invalid value, it gets rejected. If the value is valid but newer than the host, the host skips the plugin.
The openclaw.install.allowInvalidConfigRecovery field is quite specific. It doesn’t make every broken config installable. Right now, it only lets install flows recover from specific stale bundled-plugin upgrade failures, like a missing bundled plugin path or a stale channels.<id> entry for that same plugin. Other config errors will still block the install and point you to openclaw doctor --fix.
For openclaw.channel.persistedAuthState, you are providing package metadata for a tiny checker module:
{ "openclaw": { "channel": { "id": "whatsapp", "persistedAuthState": { "specifier": "./auth-presence", "exportName": "hasAnyWhatsAppAuth" } } }}You should use this when setup, doctor, or configured-state flows need a quick yes/no auth probe before the full channel plugin loads. The target export should be a small function that only reads persisted state. You should not route it through the full channel runtime barrel.
The openclaw.channel.configuredState field follows the same pattern for quick environment-only checks:
{ "openclaw": { "channel": { "id": "telegram", "configuredState": { "specifier": "./configured-state", "exportName": "hasTelegramConfiguredState" } } }}Use this when a channel can determine its configured state from environment variables or other tiny inputs. If your check needs full config resolution or the real channel runtime, you should keep that logic in the plugin config.hasConfiguredState hook instead.
JSON Schema requirements
Section titled “JSON Schema requirements”- Every plugin must ship a JSON Schema, even if it accepts no config.
- An empty schema is fine (for example,
{ "type": "object", "additionalProperties": false }). - Schemas are validated at config read/write time, not at runtime.
Validation behavior
Section titled “Validation behavior”When you’re setting things up, the system keeps a close eye on your configuration to make sure everything runs smoothly. If you use unknown keys under channels.*, you’ll run into errors unless a plugin manifest specifically declares that channel ID. You also need to make sure that any IDs you use in plugins.entries.<id>, plugins.allow, plugins.deny, or plugins.slots.* are actually discoverable. If the system can’t find them, it’ll throw an error.
If a plugin is installed but its manifest or schema is missing or broken, validation will fail, and the Doctor tool will let you know exactly what went wrong. On the other hand, if you have configuration for a plugin that’s currently disabled, the system keeps the config but gives you a warning in the logs and Doctor.
Check out the Configuration reference for the full plugins.* schema.
You’ll need a manifest for any native OpenClaw plugins, even if you’re loading them from your local filesystem. Just remember that the manifest is for discovery and validation; the runtime still loads the actual plugin module separately.
The good news is that native manifests use JSON5. This means you can use comments, trailing commas, and unquoted keys, provided the final result is an object. Just stick to the documented fields, because the loader ignores custom top-level keys.
There are a few “cheap metadata paths” designed to keep things fast without booting the full plugin runtime just to inspect environment names:
providerAuthEnvVarshandles auth probes and env-marker validation.providerAuthAliaseslets different provider variants share auth settings or API-key onboarding without hardcoding links in the core system.channelEnvVarsmanages shell-env fallbacks and setup prompts.providerAuthChoicestakes care of auth-choice pickers and CLI flag registration before the provider runtime loads. For more complex wizard metadata that needs actual provider code, you’ll want to look at Provider runtime hooks.
If you’re working with exclusive plugin kinds, you’ll select them through plugins.slots.*. For example, kind: "memory" is selected by plugins.slots.memory, and kind: "context-engine" is selected by plugins.slots.contextEngine (which defaults to the built-in legacy option).
You don’t have to include channels, providers, cliBackends, or skills if your plugin doesn’t use them. Also, if your plugin needs native modules, make sure you document the build steps and any package manager requirements, like pnpm’s allow-build-scripts (pnpm rebuild <package>).
Related
Section titled “Related”- Building Plugins — getting started with plugins
- Plugin Architecture — internal architecture
- SDK Overview — Plugin SDK reference
OpenClaw Expert
Still stuck?
If this page didn't answer your case, ask OpenClaw Expert for step-by-step guidance.