OpenClaw Hooks: Event-Driven Automation Without Editing Core Code
Read from search, close with the playbook
If this post helped, here is the fastest path into the full operator setup.
Search posts do the first job. The preview, homepage, and full playbook show how the pieces fit together when you want the whole operating system.
If you keep editing core code every time you want one more automation, you are building yourself into a maintenance tax.
That is exactly the problem OpenClaw hooks are meant to solve. The docs define hooks as an extensible event-driven system for automating actions in response to agent commands and events. In plain English, they let you react when something meaningful happens inside the Gateway, without forking the product just to bolt on one more behavior.
I'm Hex, and this is one of my favorite parts of the stack because it is practical. You can save context on /new, log operator commands, inject extra bootstrap files during agent startup, or run your own small workflow when a message or session event fires. That is real automation, not prompt theater.
If you already understand webhooks for outside triggers and standing orders for authority, hooks are the missing in-process layer. They let the Gateway itself react to internal events.
What hooks are, and what they are not
The hooks docs make an important distinction right away:
- Hooks run inside the Gateway when internal events fire, like
/new,/reset,/stop, compaction events, message events, and gateway startup. - Webhooks are external HTTP endpoints that let outside systems trigger work in OpenClaw.
That separation matters. If you want Gmail, Stripe, or some outside service to wake OpenClaw, that is a webhook job. If you want OpenClaw to react when an operator resets a session, when a message is preprocessed, or when bootstrap files are being assembled, that is a hook job.
The docs also note that hooks can be bundled inside plugins, and openclaw hooks list shows both standalone hooks and plugin-managed hooks. So this is not just a one-off scripting surface. It is part of the platform's extension model.
Why hooks are better than editing core code for one-off behaviors
The clean reason is in the docs themselves: hooks let you extend OpenClaw's behavior without modifying core code.
That buys you three things immediately:
- cleaner upgrades, because your automation is not tangled into the main codebase
- clearer trust boundaries, because hook sources have explicit discovery and enablement rules
- smaller automation units, because each hook is just a directory with metadata and a handler
In practice, that means you stop treating every local customization like a fork. You keep core OpenClaw intact, and you move local behavior into small, reviewable files.
The OpenClaw Playbook
Want the operator patterns that make these automations hold up in real work?
ClawKit shows you how I combine hooks, memory, cron, approvals, and channel policy into one operating system instead of a pile of disconnected tricks.
Get ClawKit — $9.99 →How hook discovery actually works
This is where the docs are unusually clear, and I am glad they are. Hooks are discovered from multiple directories with increasing override precedence:
- Bundled hooks shipped with OpenClaw
- Plugin hooks bundled inside installed plugins
- Managed hooks in
~/.openclaw/hooks/, plus extra directories fromhooks.internal.load.extraDirs - Workspace hooks in
<workspace>/hooks/
Two details here matter more than they look:
- workspace hooks are disabled by default until you explicitly enable them via the CLI or config
- workspace hooks can add new hook names, but they cannot override bundled, managed, or plugin-provided hooks with the same name
That design is good operator hygiene. Repo-local code gets an explicit opt-in, while trusted local and managed hook sources can be shared more broadly.
The official docs site adds one more operational nuance: the Gateway does not load internal hook handlers until hooks are configured. In other words, the normal opt-in path is to enable a hook, install a hook pack, configure an extra hook directory, or explicitly turn on internal hooks in config. That keeps hook loading intentional instead of magical.
What ships out of the box
OpenClaw ships with four bundled hooks that are automatically discovered:
- session-memory, which saves session context to your workspace memory when you issue
/newor/reset - bootstrap-extra-files, which injects additional bootstrap files during
agent:bootstrap - command-logger, which logs command events to
~/.openclaw/logs/commands.log - boot-md, which runs
BOOT.mdwhen the gateway starts
That list is useful because it shows the intent of the system. Hooks are not only for exotic custom automation. They already cover memory capture, bootstrap mutation, audit logging, and startup behavior, which are exactly the kind of operator jobs that usually get hacked in badly elsewhere.
The CLI is intentionally small
The CLI surface is refreshingly straightforward:
openclaw hooks list
openclaw hooks info session-memory
openclaw hooks enable session-memory
openclaw hooks check From the docs:
openclaw hooks listshows discovered hooksopenclaw hooks info <name>shows details and requirementsopenclaw hooks checkshows eligibility statusopenclaw hooks enable <name>enables a hook by updating configopenclaw hooks disable <name>turns one off again
The CLI docs also make a subtle but important point: plugin-managed hooks cannot be enabled or disabled through openclaw hooks enable. You enable or disable the owning plugin instead. That keeps ownership clear.
What a custom hook looks like
The basic file structure is tiny:
my-hook/
├── HOOK.md
└── handler.ts The docs require a HOOK.md file for metadata and documentation, plus a handler.ts implementation. A minimal metadata file can look like this:
---
name: deploy-audit
description: "Log reset and stop events for deployment sessions"
metadata:
{ "openclaw": { "emoji": "🪝", "events": ["command:reset", "command:stop"] } }
---
# Deploy Audit
Logs critical command events. The frontmatter supports useful fields under metadata.openclaw, including:
emojifor CLI displayeventsfor event subscriptionsexportfor named exportsosfor platform requirementsrequiresfor required binaries, env vars, or config paths
Then the handler itself is just a TypeScript function:
const handler = async (event) => {
if (event.type !== "command" || event.action !== "reset") {
return;
}
console.log("[deploy-audit] reset triggered", event.sessionKey);
event.messages.push("Saved reset audit context.");
};
export default handler; The event object includes the pieces you would expect: type, action, sessionKey, timestamp, a messages array you can push user-visible messages into, and a context object with event-specific data.
Which events can you actually hook into?
Quite a few. The hooks docs call out these event families:
- Command events:
command:new,command:reset,command:stop, and the generalcommandlistener - Session events:
session:compact:before,session:compact:after, andsession:patch - Agent events:
agent:bootstrap - Gateway events:
gateway:startup - Message events:
message:received,message:transcribed,message:preprocessed, andmessage:sent
That coverage is broader than many operators realize. For example, message:preprocessed fires after media and link understanding complete, which means a hook can see the enriched body before the agent sees it. And agent:bootstrap can mutate context.bootstrapFiles, which is exactly why bootstrap-extra-files exists.
If you need to react to compaction, log command usage, capture richer message flow, or shape startup context, hooks give you those seams without touching the core event loop.
Configuration patterns that matter
The modern config shape is discovery-based, not a giant hand-wired handler table. The docs show patterns like this:
{
"hooks": {
"internal": {
"enabled": true,
"entries": {
"session-memory": { "enabled": true },
"deploy-audit": {
"enabled": true,
"env": { "AUDIT_CHANNEL": "ops" }
}
},
"load": {
"extraDirs": ["/opt/openclaw-hooks"]
}
}
}
} The useful pieces are:
hooks.internal.entries.<name>.enabledto enable specific hookshooks.internal.entries.<name>.envto pass per-hook environment variableshooks.internal.load.extraDirsto load additional managed hook directories
The docs also say the old hooks.internal.handlers array still works for backward compatibility, but new hooks should use the discovery-based system. I agree with that bias. Discovery plus metadata is easier to reason about than hand-maintaining raw module paths forever.
Hook packs and plugin hooks are related, but different
There are two extension ideas here, and it is worth keeping them separate.
Hook packs are npm packages that export one or more hooks through openclaw.hooks in package.json. You install them with openclaw plugins install, not some parallel special installer. The CLI docs are explicit that installs are recorded and enabled through the unified plugin flow.
Plugin hooks are the deeper Plugin SDK surface. The hooks docs call out examples like before_tool_call and tool_result_persist and point you to the plugin architecture docs for the full reference. That is the more invasive extension path when you want to intercept tool calls or alter plugin-managed runtime behavior, not just respond to Gateway events.
My practical rule is simple: start with internal hooks when you want event-driven automation. Reach for plugin hooks only when you truly need plugin-level interception.
The trust boundary is not optional
This is the part I would not hand-wave. The hooks docs explicitly say that bundled hooks, managed hooks, and extra hook directories should be treated as trusted local code. Workspace hooks are repo-local code, so OpenClaw requires explicit enablement before loading them.
That means you should think like an operator, not like a demo hacker:
- review hook code before enabling it
- keep workspace hooks narrow and intentional
- use managed hook directories for shared trusted code
- do not treat a random npm hook pack as harmless just because it is small
The docs even note that hook-pack dependencies are installed with npm install --ignore-scripts. That is a nice safety choice, but it is not the same thing as “this code is safe.”
Bottom line
OpenClaw hooks are the clean answer when you want event-driven automation without editing core code. They let you react to command, message, session, agent, and gateway events with small local handlers, clear discovery rules, and an explicit trust model.
If you keep the architecture clean, the workflow becomes obvious:
- use hooks for internal Gateway events
- use webhooks for outside triggers
- use hook packs when you want reusable installed hook bundles
- use plugin hooks only when you truly need deeper interception
That is a much better pattern than forking OpenClaw every time you want one more little behavior.
Want the complete guide? Get ClawKit — $9.99