Internal Hooks: Automate Commands and Lifecycle Events

Learn how internal hooks let you run compact JavaScript or TypeScript handlers inside the Gateway process for command and lifecycle automation. Covers choosing between internal hooks, plugin hooks, and bundled hooks.

Read this when

  • You want event-driven automation for /new, /reset, /stop, or session and Gateway events
  • You want to write, install, enable, or debug an internal hook
  • You need to understand hook discovery, event data, or reply delivery

Hooks

Internal hooks are compact JavaScript or TypeScript handlers that execute inside the Gateway process whenever OpenClaw emits an event. Reach for them to persist session context, record reset commands, or carry out brief side effects tied to message and session lifecycle events. OpenClaw ships bundled hooks for common jobs, so no plugin has to be written to make use of them.

Choose the right surface

You want to…Use
Save context on /new, log commands, or react to session and message eventsInternal hooks (HOOK.md plus a handler), described here
Modify prompts, intercept tools, control replies, or use lifecycle contracts with priorities and return valuesPlugin hooks through api.on(...)
Let another service start work through an HTTP requestWebhooks
Export telemetry rather than change behaviorDiagnostic events

Each of these is a distinct system. Event handlers for this page are configured by hooks.internal, while HTTP ingress is configured by hooks.enabled. Names of internal events, for example message:received, differ from the typed plugin names, for example message_received.

Warning

Hook code is trusted, not confined to a sandbox. The Gateway process's filesystem, network, and environment access is available to it. Before enabling any hook, review its code, particularly when it comes from a workspace or a downloaded package.

Quick start

Begin with command-logger: no extra binaries and no model calls are needed, and it leaves you a concrete file to inspect. Run the commands below on the Gateway host, using the same profile and config that Gateway uses:

openclaw hooks list
openclaw hooks info command-logger
openclaw hooks enable command-logger

Hook config changes take effect without a restart under the default hybrid reload mode. If reload mode is off, either run openclaw gateway restart or restart a foreground Gateway yourself. When your configuration has several agents and no implicit owner, add --agent <id>.

In a conversation that is safe to reset, an authorized user can send /new or /reset. After that, inspect the log on the Gateway host:

tail -n 5 ~/.openclaw/logs/commands.log

A new JSON line carrying "action":"new" or "action":"reset" is what you are looking for, along with a recent timestamp and that conversation's sessionKey. If a custom state directory is in use, read <stateDir>/logs/commands.log instead. This is evidence that a handler ran; on its own, openclaw hooks check is not.

Session and sender identifiers end up in the log. If keeping those records is unwanted, turn the hook off once you have finished testing:

openclaw hooks disable command-logger

Eligible, enabled, and loaded

Treat these three checks as independent:

  • Requirements satisfied: on the host performing the check, the hook's OS, binaries, environment, and config requirements all pass.
  • Enabled by config: the per-hook/source policy permits it. Workspace hooks need explicit opt-in; when broad discovery is on, bundled and managed hooks do not need that per-hook flag.
  • Loaded: the running Gateway picked the hook, imported its handler, and registered its events. The master switch and the configured name selection must also allow it.

The ready, eligible, and loadable fields reported by the CLI cover the first two checks plus a nonempty event list. What they do not prove is that the Gateway imported the handler, that the global selection contains it, or that its event has fired. Once changes are made, verify the real side effect or the hook-specific log.

On config reload, selected handlers are prepared first and then swapped in together. Should a selected handler fail to load, the previous handlers remain active. An event already in flight completes with its original handlers; events after that use the new selection. gateway:startup is not replayed by a reload.

Local, remote, and agent scope

The inventory of the selected Gateway is requested by hooks list, info, and check. When an implicit local Gateway is unavailable, or lacks the report method, it may fall back to local discovery. On failure, a configured remote Gateway or an explicit OPENCLAW_GATEWAY_URL does not fall back to the hooks on your laptop.

Local config is always what hooks enable and hooks disable inspect and modify. A remote Gateway is never updated by them over RPC. To change that host's hooks, run them on the Gateway host.

What --agent <id> selects is the workspace to inspect, not an isolated hook registry. The saved hooks.internal.entries.<hookKey> entry is global. Directory hooks from its selected workspace are loaded by the Gateway into a process-wide registry; merely inspecting some agent's hooks/ directory does not cause every such directory to be loaded. When a handler should act for one particular agent only, it must filter the event's agent or session. See Hook discovery.

Plugin hooks

Inside hooks list, plugin-managed internal hooks show up as plugin:<id>. They take part in this event system, yet the owning plugin is what you enable or disable, rather than toggling them via hooks enable or hooks disable. For typed api.on hooks, the directory loader's configured-name selection is neither a policy gate nor a replacement for activating the plugin.

Internal events are registered through the legacy api.registerHook API. Typed lifecycle names such as before_tool_call, message_received, or session_start are not invoked by it; registering any of those names emits a warning that points authors to api.on(...). Where a new integration needs typed lifecycle control, consult the Plugin hooks reference.

Best practices

For a single event, handlers execute in sequence: listeners registered for the family run first, then exact listeners, and within each group the order is the order of registration. The dispatcher waits for each handler, logs any error that is thrown and keeps going with the remaining handlers. Directory hooks offer no priority setting.

Different events are not serialized by this sequencing. Message notifications, patch notifications, and automatic reset work may run concurrently with other events and with agent processing. No general handler timeout, cancellation signal, durable event queue, automatic retry, or exactly-once guarantee exists. Work that is in flight can be lost if the process exits or restarts.

Keep side effects short and bounded. Await the work that belongs to the handler, set timeouts on network calls, limit data sizes, and design repeatable operations so retries do not duplicate their effects. Do not use void doHeavyWork(event) as a general solution: that work escapes the handler's wait/error boundary and can outlive its session or process. If work needs a durable job lifecycle, use an automation or service that owns it.

Filter unrelated events early and avoid logging message bodies, whole config objects, or credentials. Message and session data can be private. Keep only the minimum needed for the side effect, protect output files, and set retention. Long-lived timers, watchers, sockets, and clients belong to a plugin service with an explicit shutdown lifecycle, not a request/event handler.

CLI reference

See openclaw hooks for every public report and toggle option, JSON output fields, exit behavior, and install/update aliases.

Detailed topics

These pages hold the reference and how-to material that used to follow the quick start on this page.

PageRead it when
Writing hooksYou are writing a hook and need the file layout, handler contract, or HOOK.md fields.
Hook configuration and discoveryYou are enabling hooks, narrowing the selection, or tracing discovery across sources.
Bundled hooksYou want a shipped hook and need its behavior, options, and checks.
Hook event types and contextYou need an event key's trigger, wait behavior, or context fields.
Hook troubleshootingA hook is not discovered, not eligible, or not executing.

Where each section moved

Every section heading from the previous single-page version keeps its anchor here, so an existing link such as /automation/hooks#session-memory still resolves. Each entry points at the page that now holds the content.

1,419 words · updated Oct 25, 2026