Skip to main content
Hook file layout, the handler contract, reply delivery, and the HOOK.md metadata fields. Part of the Hooks guide.

Writing hooks

This example replies to a reset command and writes a fixed log marker. It does not read message content, call a model, or contact an external service.

Hook structure

On the Gateway host, use a new managed hook directory. The following commands assume the default state directory and that reset-greeting does not already exist; choose another name rather than overwrite an existing hook.
A hook needs HOOK.md and a handler file. Discovery checks, in order, handler.ts, handler.js, index.ts, then index.js, using the first file it finds. The example uses JavaScript so no TypeScript types or SDK imports are needed. Enable and load it:
Send /new in a disposable conversation on a configured chat channel that can route replies, such as a direct message to the bot. Expect Reset hook ran. in that conversation and [reset-greeting] reset hook ran in Gateway logs. /reset triggers the same example. Normal command authorization still applies. Use an ordinary OpenClaw conversation, not an ACP-bound thread; bound sessions delegate reset handling to their owning runtime. Do not use Control UI/webchat or a sessions.reset RPC as the chat-reply check: those paths do not deliver this hook’s event.messages to the UI. The log marker can still show that a reset event ran. See Reply delivery for the exact boundary. Disable the example when finished:
Disabling leaves the files in place. To use a workspace directory instead, put the two files in <workspace>/hooks/reset-greeting/, then explicitly enable the hook. Workspace placement is not an agent sandbox or a guarantee that the Gateway will load that workspace’s hooks.

Handler implementation

A handler exports a function returning void or Promise<void>. The loader uses the default export unless metadata.openclaw.export names another export. Returned values do not block, cancel, or rewrite the operation. Every event has these fields: Treat context as an observation, not a live state-editing API. Fields vary by producer, and cfg is not present on every event. In particular, patch events carry cloned snapshots. The explicit mutable exception is agent:bootstrap’s context.bootstrapFiles.

Reply delivery

Pushing to event.messages is not a general send-message API: A missing recipient, unsupported route, send policy, or delivery failure can prevent a reply. Append messages before the handler’s promise settles; detached work that pushes later can miss the producer’s delivery step. To control normal agent replies or send cancellation, use the appropriate typed plugin hook.

HOOK.md format

HOOK.md uses YAML frontmatter followed by human-readable Markdown:
name defaults to the directory name; use a unique, stable name. description is shown in reports. The following fields belong under metadata.openclaw: Use hooks.internal.entries.<hookKey>.enabled to control activation, not a top-level enabled flag in HOOK.md. For historical requirement metadata, workspace.dir, browser.enabled, and browser.evaluateEnabled default to true when absent. workspace.dir is not a new setting you need to add to your config.