#10680: docs: clarify api.on() vs api.registerHook() for plugin hooks
docs
Cluster:
Plugin Hook Enhancements
# PR: Document `api.on()` vs `api.registerHook()` for plugin developers
**Type:** docs-only
## Problem
OpenClaw exposes two hook registration methods in the plugin API:
- `api.on(event, handler)` — registers a **typed hook** into `registry.typedHooks`. These are actually executed by the hook runner.
- `api.registerHook(events, handler, opts)` — registers hook **metadata** into `registry.hooks`. These are used for UI/diagnostics only and are **never executed** by the hook runner.
The names are similar enough that plugin developers naturally reach for `registerHook` first. When they do, registration succeeds without errors, the hook appears to be set up correctly, but the handler never fires. There is no warning logged. This leads to hours of silent-failure debugging.
An additional footgun: `registerHook()` silently drops entries when `opts.name` is missing — again, no error, no warning.
## What this PR does
- **`docs/plugin.md`**: Adds a "Registering typed hooks (inline)" section with a clear `api.on()` example, and a prominent warning callout explaining the difference between the two methods, including a comparison table and code examples showing correct vs incorrect usage.
- **`docs/hooks.md`**: Adds a "Plugin hook registration" section that summarizes the distinction and links to the plugin docs for full details.
## Who this helps
Any developer writing an OpenClaw plugin that needs event-driven hooks. This is the kind of trap you only fall into once — but it costs hours when you do. The docs now make the correct path obvious.
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This docs-only PR clarifies the distinction between plugin hook registration APIs:
- Adds a new **“Plugin hook registration”** section to `docs/hooks.md` that explains why `api.on()` is the correct choice for executable typed hooks, while `api.registerHook()` only records metadata for UI/diagnostics.
- Expands `docs/plugin.md` under **Plugin hooks** with separate guidance for registering hooks from a directory vs registering typed hooks inline, including a comparison table and examples.
- Also documents the naming convention difference between plugin typed hook keys (underscored) and workspace internal hook events (colon-separated) to reduce confusion.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk.
- Docs-only changes are internally consistent, follow existing linking conventions, and address the previously noted anchor/event-name concerns without introducing new broken references or contradictory guidance.
- No files require special attention
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#12819: docs(hooks): document plugin hook names and clarify naming
by JBrady · 2026-02-09
78.9%
#20580: feat(hooks): bridge after_tool_call to internal hook handler system
by CryptoKrad · 2026-02-19
76.2%
#6405: feat(security): Add HTTP API security hooks for plugin scanning
by masterfung · 2026-02-01
76.2%
#9603: fix: initialize global hook runner on plugin registry cache hit
by kevins88288 · 2026-02-05
75.8%
#11817: fix(build): compile bundled hook handlers into dist
by AnonO6 · 2026-02-08
75.4%
#9914: fix(hooks): resolve bundled hook dist paths and packaging checks
by zimmra · 2026-02-05
75.1%
#23019: fix(hooks): use globalThis singleton for internal hooks handlers Map
by karmafeast · 2026-02-21
74.6%
#4881: docs(plugin): clarify memory-lancedb is bundled (no npm install nee...
by AverageSuami · 2026-01-30
74.6%
#17930: fix: evaluate tool_result_persist hooks lazily to avoid race condition
by TheArkifaneVashtorr · 2026-02-16
74.3%
#14746: fix(hooks): use globalThis for handler registry to survive bundler ...
by openperf · 2026-02-12
73.3%