Heartbeats & Hooks
Ambient awareness vs precise scheduling. Event-driven automation. From basic to advanced.
Part 1: Heartbeats
What is a heartbeat?
Every 30 minutes (by default), OpenClaw sends your AI a pulse — a periodic check-in prompt. Your AI reads a file called HEARTBEAT.md and acts on whatever's in it. If nothing needs attention, it stays silent. If something does, it messages you.
Think of it like a doctor checking your pulse: most of the time nothing's wrong, they say nothing. Occasionally they say "actually, something's up."
Heartbeat vs Cron — the real difference
| Heartbeat | Cron Job |
|---|---|
| Runs in your main session | Runs in an isolated session |
| Approximate timing (~30 min) | Exact timing ("9:00 AM sharp") |
| Has full conversation context | Starts fresh each time |
| Batches multiple checks together | One task per job |
| Best for: ambient monitoring | Best for: precise scheduled tasks |
| Silent unless something to report | Always delivers output to channel |
The decision flowchart
- Need it at exactly 9:00 AM? → Cron
- Batching 3+ periodic checks? → Heartbeat
- Need main session context? → Heartbeat
- Want a different/cheaper model? → Cron
- One-shot reminder in 20 minutes? → Cron
- Monitoring for unusual events? → Heartbeat
Setting up HEARTBEAT.md
Create this file in your workspace. Keep it short — it's loaded every poll cycle.
cat > ~/.openclaw/workspace/HEARTBEAT.md << 'EOF'
# HEARTBEAT.md
## Active Checks
- Check APPROVAL_QUEUE.md — flag anything pending for more than 2 hours
- Check memory/$(date +%Y-%m-%d).md — any open items from today?
- If an urgent email has arrived since last check, flag it
## Quiet Hours
- No proactive messages 23:00–08:00 unless genuinely urgent
- Do not message about routine items during quiet hours
## Cooldown
- Don't repeat the same alert within 2 hours
EOF
Configuring heartbeat timing
The heartbeat interval is set in your openclaw.json:
"heartbeat": {
"enabled": true,
"interval": "30m",
"channel": "telegram"
}
Tracking heartbeat state
To prevent repeat alerts, your AI can track what it last checked:
# ~/.openclaw/workspace/memory/heartbeat-state.json
{
"lastChecks": {
"email": 1773785000,
"calendar": 1773785000,
"approvals": 1773785000
},
"lastAlerts": {
"pending-approval-x": 1773785000
}
}
Your AI reads this file, compares timestamps, and only checks things that are due. It writes back after each check. No repeat alerts, no noise.
---Part 2: Hooks
What is a hook?
A hook is a small script that fires automatically when something happens inside OpenClaw. Not on a schedule — on an event. Session starts: hook fires. Command runs: hook fires. Message arrives: hook fires.
Hooks run inside the Gateway, not in the agent session. They're fast, lightweight, and run in the background.
The two types
| Internal Hooks | External Webhooks |
|---|---|
| Run inside OpenClaw on agent events | External HTTP endpoints that trigger OpenClaw |
| Examples: boot-md, session-memory, command-logger | Examples: GitHub webhook → Jinn reviews PR |
Config: hooks.internal |
Config: hooks.enabled + token |
| Bundled with OpenClaw | You set them up in external services |
The bundled hooks you should enable
"hooks": {
"internal": {
"enabled": true,
"entries": {
"boot-md": { "enabled": true },
"bootstrap-extra-files": { "enabled": true },
"session-memory": { "enabled": true },
"command-logger": { "enabled": true }
}
}
}
What each one does:
- boot-md — loads your workspace files (SOUL.md, MEMORY.md, etc.) at the start of every session
- bootstrap-extra-files — loads additional files you specify at boot
- session-memory — writes session summaries to memory at the end of each session
- command-logger — logs every command the AI runs, with timestamps and outcomes
External webhooks
You can expose a webhook endpoint from your Gateway so external services can trigger your AI:
"hooks": {
"enabled": true,
"token": "your-secret-token",
"path": "/hooks",
"allowedAgentIds": ["main"]
}
This exposes three endpoints on your Gateway:
| Endpoint | What it does |
|---|---|
POST /hooks/wake |
Wakes the main agent with a message |
POST /hooks/agent |
Routes a message to a specific agent |
POST /hooks/<name> |
Fires a named mapped hook |
Example: GitHub PR triggers Jinn to review it
# 1. Get your Gateway's public URL (via Cloudflare tunnel or similar)
openclaw gateway tunnel
# 2. In GitHub: Settings → Webhooks → Add webhook
# URL: https://your-tunnel.trycloudflare.com/hooks/wake
# Secret: your-secret-token
# Events: Pull requests
# 3. The webhook body tells Jinn what to do:
# POST /hooks/wake
# { "message": "New PR opened: {{PR title}}. Please review it." }
Creating custom hooks (advanced)
You can write your own hooks in TypeScript. Each hook lives in a directory with two files:
~/.openclaw/hooks/
└── my-custom-hook/
├── HOOK.md # Metadata: name, events, description
└── handler.ts # TypeScript: what to do when event fires
The HOOK.md format:
# my-custom-hook
## Events
- session:start
- command:after
## Description
Logs session starts and post-command states to a local file.
## Enabled
true
The handler receives an event context object with:
event.type— what happened (session:start, command:after, etc.)event.agentId— which agent triggered itevent.sessionId— the session IDevent.payload— event-specific data
Hook event types
| Category | Events |
|---|---|
| Session | session:start, session:end, session:reset |
| Command | command:before, command:after |
| Agent | agent:turn:start, agent:turn:end |
| Message | message:inbound, message:outbound |
| Gateway | gateway:start, gateway:stop |
Managing hooks via CLI
# List all discovered hooks
openclaw hooks list
# Check hook details
openclaw hooks info --name boot-md
# Enable/disable a hook
openclaw hooks enable --name my-custom-hook
openclaw hooks disable --name my-custom-hook
# Check why a hook isn't running
openclaw hooks check --name my-custom-hook
Best Practices
event.type immediately in your handler and return early if it's not the event you care about. Don't do expensive operations then discard.
~/.openclaw/.env and read them via process.env.
cloudflared tunnel --url http://localhost:18789
Questions & Suggestions
Have a question about this page? Spotted something wrong? Want to suggest an improvement? We read everything and respond to all paid-tier questions.