Introduction
OpenClaw provides a powerful event-driven hook system that lets you extend and automate agent behavior at key lifecycle points. Hooks respond to specific events such as commands, agent bootstrap, gateway startup, and message flow, giving you fine-grained control over your automation workflows.
Key Concepts
- Event Types: Categories of events that hooks can subscribe to, including
command,agent,gateway,message, andplugin. - Hook Discovery: The ordered process by which OpenClaw locates and loads hooks from workspace, managed, and bundled directories.
- HookHandler: The async function signature that every hook must export to process events.
- Event Object: The data structure passed to each handler containing type, action, session context, and messages.
Real World Context
In production environments, event-driven architectures are foundational to tools like GitHub Actions, Git hooks, and CI/CD pipelines. OpenClaw hooks follow the same philosophy: rather than polling for changes, your code reacts to specific lifecycle events. This pattern keeps automation composable, testable, and easy to reason about.
Deep Dive
OpenClaw defines five categories of events, each with specific actions:
typescript// Command events fire when the user issues CLI commands // Actions: new, reset, stop { type: 'command', action: 'new' } { type: 'command', action: 'reset' } { type: 'command', action: 'stop' } // Agent events fire during agent lifecycle // Action: bootstrap { type: 'agent', action: 'bootstrap' } // Gateway events fire when the server starts // Action: startup { type: 'gateway', action: 'startup' } // Message events fire during conversation flow // Actions: received, sent { type: 'message', action: 'received' } { type: 'message', action: 'sent' } // Plugin events fire for tool interactions // Action: tool_result_persist { type: 'plugin', action: 'tool_result_persist' }
The code above shows every event type and its available actions. The command type covers CLI interactions, agent and gateway handle lifecycle moments, message tracks conversation flow, and plugin captures tool results.
When OpenClaw needs to run hooks, it searches three locations in a specific priority order:
text1. Workspace hooks: ~workspace/hooks/ 2. Managed hooks: ~/.openclaw/hooks/ 3. Bundled hooks: dist/hooks/bundled/
This discovery order means workspace-level hooks take highest priority, allowing project-specific overrides. Managed hooks apply across all your projects, and bundled hooks ship with OpenClaw as defaults.
Every hook must export a HookHandler function. Here is the type signature and an example:
typescript// The HookHandler type defines the contract for all hooks type HookHandler = (event: HookEvent) => Promise<void>; // The event object carries all context your hook needs interface HookEvent { type: string; // e.g. 'command', 'agent', 'message' action: string; // e.g. 'new', 'bootstrap', 'received' sessionKey: string; // unique identifier for the current session timestamp: number; // Unix timestamp of the event messages: Message[]; // conversation messages at time of event context: Record<string, unknown>; // additional metadata } // Example: a hook that logs every new command const handler: HookHandler = async (event) => { if (event.type === 'command' && event.action === 'new') { console.log(`New session started: ${event.sessionKey}`); console.log(`Timestamp: ${new Date(event.timestamp).toISOString()}`); } }; export default handler;
The handler receives the full event object, checks the type and action to decide whether to act, and then performs its logic asynchronously. The sessionKey identifies which session triggered the event, while messages gives access to the conversation history and context provides any extra metadata.
Common Pitfalls
- Ignoring discovery order: Placing a hook in
~/.openclaw/hooks/when a workspace-level hook with the same name exists will cause the managed hook to be silently skipped. - Blocking the event loop: Since handlers are async, performing heavy synchronous operations inside them will block other hooks and degrade performance.
- Not filtering by action: Subscribing to an event type without checking the action field can cause your hook to fire on unintended triggers.
Best Practices
- Always filter on both
event.typeandevent.actionto ensure your hook only runs when intended. - Keep hook handlers focused on a single responsibility; compose multiple hooks rather than building monolithic ones.
- Use the
contextfield to pass data between hooks in the same event cycle rather than relying on global state.
Summary
- OpenClaw hooks respond to five event types:
command,agent,gateway,message, andplugin. - Hook discovery follows a priority chain: workspace, then managed, then bundled.
- Every hook exports an async
HookHandlerfunction that receives a structured event object. - The event object provides
type,action,sessionKey,timestamp,messages, andcontext. - Filtering on both type and action ensures hooks only fire for the intended events.