Hook Patterns: Quality Validation, Context Injection, and More
Hooks are OpenClaw's event system. They run when commands fire, sessions start, or the gateway boots. Here are the patterns I use in production.
What Are Hooks?
Hooks are small TypeScript functions that run in response to events. They let you customize OpenClaw behavior without modifying core code.
Event Types:
- Command events:
/new,/reset,/stop - Agent events:
agent:bootstrap - Gateway events:
gateway:startup
Pattern 1: Session Memory
Save conversation context when you reset a session. This is my most-used hook.
Use case: I issue /new to start fresh. But I want to search past conversations later.
How it works:
- User sends
/new command:newevent fires- session-memory hook extracts last 15 lines of conversation
- Uses LLM to generate descriptive filename slug
- Saves to
~/.openclaw/workspace/memory/YYYY-MM-DD-slug.md
Example output:
# Session: 2026-01-16 14:30:00 UTC
- **Session Key**: agent:main:main
- **Session ID**: abc123
- **Source**: telegram
## Last 15 lines
[conversation excerpt]Configuration:
"hooks": {
"internal": {
"enabled": true,
"entries": {
"session-memory": {
"enabled": true
}
}
}
}Pattern 2: Command Auditing
Log every command to a JSONL file for debugging and compliance.
Use case: When something goes wrong, I need to know what commands were issued and when.
Implementation:
const handler: HookHandler = async (event) => {
if (event.type !== 'command') return;
const logEntry = {
timestamp: event.timestamp.toISOString(),
action: event.action,
sessionKey: event.sessionKey,
senderId: event.context.senderId,
source: event.context.commandSource
};
await fs.appendFile(
'~/.openclaw/logs/commands.log',
JSON.stringify(logEntry) + '\n'
);
};Log output:
{"timestamp":"2026-02-08T14:30:00.000Z","action":"new","sessionKey":"agent:main:main","senderId":"+1234567890","source":"telegram"}
{"timestamp":"2026-02-08T15:45:22.000Z","action":"stop","sessionKey":"agent:main:main","senderId":"user@example.com","source":"whatsapp"}Query the log:
# View recent commands
tail -n 20 ~/.openclaw/logs/commands.log | jq .
# Filter by action
grep '"action":"new"' ~/.openclaw/logs/commands.log | jq .Pattern 3: Bootstrap File Injection
Modify which files get injected into the agent's context at session start.
Use case: During a "purge window" (9pm-9:15pm), swap SOUL.md with SOUL_EVIL.md for personality changes.
Implementation:
const handler: HookHandler = async (event) => {
if (event.type !== 'agent' || event.action !== 'bootstrap') return;
const now = new Date();
const hour = now.getHours();
const minute = now.getMinutes();
// Purge window: 21:00-21:15
const inPurgeWindow = hour === 21 && minute < 15;
if (inPurgeWindow) {
const soulIndex = event.context.bootstrapFiles?.findIndex(
f => f.name === 'SOUL.md'
);
if (soulIndex !== -1) {
event.context.bootstrapFiles[soulIndex] = {
name: 'SOUL.md',
content: await fs.readFile('SOUL_EVIL.md', 'utf-8')
};
}
}
};Configuration:
"hooks": {
"internal": {
"enabled": true,
"entries": {
"soul-evil": {
"enabled": true,
"chance": 0.1,
"purge": {
"at": "21:00",
"duration": "15m"
}
}
}
}
}Pattern 4: Startup Automation
Run tasks when the gateway starts.
Use case: Send myself a morning briefing when the gateway boots.
BOOT.md example:
# Morning Boot Routine
1. Check weather for San Francisco
2. List unread emails from past 12 hours
3. Check GitHub notifications
4. Send summary to the operator via Telegram
If any critical issues (failed CI, urgent emails), send alert.How it works:
- Gateway starts
gateway:startupevent fires- boot-md hook reads BOOT.md
- Runs instructions via agent runner
- Sends messages via message tool
Pattern 5: Quality Validation
Validate agent output before it's sent to the user.
Use case: Prevent sending email drafts that don't include required fields.
Implementation sketch:
const handler: HookHandler = async (event) => {
if (event.type !== 'tool_result') return;
// Check if this is an email send
if (event.toolName === 'gog' && event.params.includes('gmail send')) {
// Validate email has subject and body
if (!event.params.includes('--subject') ||
!event.params.includes('--body')) {
event.messages.push(
'⚠️ Email validation failed: Missing subject or body'
);
return; // Don't send
}
}
};Pattern 6: Context Injection
Add dynamic context to every session based on external state.
Use case: Inject current time, weather, and calendar events at session start.
Implementation:
const handler: HookHandler = async (event) => {
if (event.type !== 'agent' || event.action !== 'bootstrap') return;
const weather = await getWeather('San Francisco');
const events = await getTodayCalendarEvents();
const contextFile = {
name: '_DYNAMIC_CONTEXT.md',
content: `# Dynamic Context
**Current Time**: ${new Date().toISOString()}
**Weather**: ${weather}
**Today's Events**: ${events.length} events
`
};
event.context.bootstrapFiles?.push(contextFile);
};Creating Custom Hooks
Step 1: Create hook directory
mkdir -p ~/.openclaw/hooks/my-hookStep 2: Create HOOK.md
---
name: my-hook
description: "Does something useful"
metadata: { "openclaw": { "emoji": "🎯", "events": ["command:new"] } }
---
# My Custom Hook
This hook does something useful when you issue `/new`.Step 3: Create handler.ts
import type { HookHandler } from "../../src/hooks/hooks.js";
import ProductCTA from "@/components/ProductCTA";
import EmailCapture from "@/components/EmailCapture";
const handler: HookHandler = async (event) => {
if (event.type !== 'command' || event.action !== 'new') {
return;
}
console.log('[my-hook] Running!');
// Your logic here
};
export default handler;Step 4: Enable hook
openclaw hooks enable my-hookStep 5: Restart gateway
Best Practices
1. Keep Handlers Fast
Hooks run during command processing. Don't block:
// ✓ Good - fire and forget
const handler: HookHandler = async (event) => {
void processInBackground(event);
};
// ✗ Bad - blocks command processing
const handler: HookHandler = async (event) => {
await slowDatabaseQuery(event);
};2. Handle Errors Gracefully
const handler: HookHandler = async (event) => {
try {
await riskyOperation(event);
} catch (err) {
console.error('[my-hook] Failed:', err);
// Don't throw - let other handlers run
}
};3. Filter Events Early
const handler: HookHandler = async (event) => {
// Return early if not relevant
if (event.type !== 'command' || event.action !== 'new') {
return;
}
// Your logic here
};The Bottom Line
Hooks are how you make OpenClaw yours. The bundled hooks (session-memory, command-logger, boot-md) cover common cases. But custom hooks let you build exactly what you need.
Start with the bundled hooks. Enable session-memory and command-logger. Then build custom hooks as needs arise. And remember: hooks are event-driven. They react. They don't initiate.
Continue Learning
Ready to build?
Get the OpenClaw Starter Kit — config templates, 5 production-ready skills, deployment checklist. Go from zero to running in under an hour.
$14 $6.99
Get the Starter Kit →Also in the OpenClaw store
Get the free OpenClaw quickstart guide
Step-by-step setup. Plain English. No jargon.