Inside OpenClaw’s Heartbeat: The Technical Deep-Dive Every Agent Builder Needs
Everyone talks about the heartbeat killing SaaS. Nobody explains what actually happens when it fires. Let’s fix that.
We recently published The Heartbeat That Killed SaaS, exploring why OpenClaw’s proactive cron mechanism represents an architectural shift in how software works. The response was overwhelming, but the most common question was: “How does it actually work?”
This post answers that question. We’re going source-code deep into OpenClaw’s heartbeat: what fires, what gets checked, what the agent receives as context, and how HEARTBEAT_OK suppression prevents your Telegram from exploding every 30 minutes.
If you’re building on Augmi or running your own OpenClaw agent, this is the reference you’ll keep coming back to.
The Heartbeat in One Paragraph
Every 30 minutes (by default), OpenClaw’s internal scheduler triggers a heartbeat turn in the agent’s main session. The agent receives a system prompt instructing it to read HEARTBEAT.md from the workspace. It evaluates the checklist against its current context and tools. If nothing needs attention, it replies HEARTBEAT_OK and the message is silently suppressed. If something does need attention, it sends an alert to your configured channel (Telegram, Discord, Slack). That’s it. No daemon. No external service. Just a cron job that gives your AI agent a reason to wake up and think.
HEARTBEAT.md: The Agent’s To-Do List
At the core of the heartbeat is a single markdown file: HEARTBEAT.md. It lives in your agent’s workspace directory and defines what the agent should care about during each heartbeat cycle.
What It Looks Like
Here’s a real-world example:
# HEARTBEAT.md
## Morning (9am - 12pm)
- Quick scan: anything urgent in inboxes?
- Check calendar for conflicts today
- Review overnight alerts from monitoring
## Throughout Day
- Scan Slack for direct mentions I haven't responded to
- Check if any PR reviews are waiting on me
- Monitor production error rates via Datadog MCP
## Evening (after 6pm)
- Summarize what happened today
- Flag anything that needs attention tomorrow
- Check if scheduled deployments completed
## Anytime
- If API latency > 500ms on /api/checkout, alert immediately
- If disk usage > 80% on any production server, alert
- Watch for emails from legal@company.com
How the Agent Reads It
During a heartbeat turn, the agent receives a system prompt that includes:
Read HEARTBEAT.md if it exists (workspace context). Follow it strictly.
Do not infer or repeat old tasks from prior chats.
If nothing needs attention, reply HEARTBEAT_OK.
Three things are important here:
- “Follow it strictly” - The agent doesn’t freelance. It checks exactly what’s in the file.
- “Do not infer or repeat old tasks” - Prevents the agent from re-alerting on things it already told you about.
- “Reply HEARTBEAT_OK” - The escape hatch. If everything is fine, say the magic words and go back to sleep.
The Empty File Optimization
If HEARTBEAT.md is empty or contains only whitespace and markdown headers, OpenClaw skips the API call entirely. No tokens burned. This is the default state for new Augmi agents, which ship with:
# HEARTBEAT.md
# Keep this file empty (or with only comments) to skip heartbeat API calls.
# Add tasks below when you want the agent to check something periodically.
This means you pay nothing for heartbeats until you actively choose to use them.
The Scheduler: How Heartbeats Fire
OpenClaw’s heartbeat scheduler lives in heartbeat-runner.ts and operates as an internal cron mechanism within the gateway process.
Default Intervals
| Auth Mode | Default Interval |
|---|---|
| Standard (API key) | 30 minutes |
| Anthropic OAuth / setup-token | 1 hour |
| Custom | Configurable |
How It Works
- Timer fires at the configured interval
- Scheduler calls
runHeartbeatOnce({ reason: "cron:jobId" }) - Gateway checks if
HEARTBEAT.mdhas content (empty = skip) - If content exists, a new agent turn is injected into the main session
- Agent executes the heartbeat turn with full context + tools
- Response is evaluated for
HEARTBEAT_OKsuppression - Timer resets for next interval
Active Hours: Don’t Wake Me at 3am
You can restrict heartbeats to specific hours:
{
"heartbeat": {
"every": "30m",
"activeHours": {
"start": "09:00",
"end": "22:00",
"timezone": "America/New_York"
}
}
}
Outside the active window, heartbeats are silently skipped until the next tick falls inside the window. Start time is inclusive, end time is exclusive. You can use "24:00" for end-of-day.
If you omit the timezone, OpenClaw uses the agent’s configured userTimezone, falling back to the host system timezone.
What Happens During a Heartbeat Turn
A heartbeat turn is a full agent turn. The agent has access to all the same tools and context as a regular conversation turn. Here’s the exact sequence:
1. Context Assembly
The agent receives:
- Full conversation history from the session
- Workspace context (including HEARTBEAT.md)
- System prompt with heartbeat-specific instructions
- Available tools (Read, Write, Edit, Bash, plus any MCP servers)
2. Agent Reasoning
The agent reads HEARTBEAT.md and evaluates each item. It can:
- Read files to check project state
- Run bash commands to query APIs, check server status, etc.
- Use MCP tools to access Slack, email, calendar, databases
- Write files to update state or logs
3. Decision Point
After evaluation, the agent makes a binary decision:
Nothing needs attention:
HEARTBEAT_OK
Something needs attention:
Alert: Your 2pm meeting with the design team conflicts with the
deployment window you scheduled. The deployment is set for 1:45pm
and typically takes 30-45 minutes. Want me to reschedule one of them?
4. Message Routing
The response is routed based on the target configuration:
| Target | Behavior |
|---|---|
"last" (default) |
Sends to the most recently used external channel |
"none" |
Internal processing only, no message delivered |
| Channel name | Sends to specific channel (e.g., “telegram”, “discord”) |
HEARTBEAT_OK: The Suppression Protocol
This is the mechanism that makes heartbeats practical. Without it, you’d get a Telegram message every 30 minutes saying “everything’s fine.” Nobody wants that.
How Recognition Works
HEARTBEAT_OK must appear at the start or end of the agent’s reply. If it appears in the middle, it’s treated as regular text.
# Recognized (suppressed)
"HEARTBEAT_OK"
"HEARTBEAT_OK - all systems normal"
"Checked all items. HEARTBEAT_OK"
# NOT recognized (delivered as normal message)
"I found HEARTBEAT_OK in the logs but there's an issue..."
The ackMaxChars Threshold
After stripping HEARTBEAT_OK from the response, OpenClaw checks the remaining character count against ackMaxChars (default: 300).
- Remaining chars <= 300: Entire response is silently dropped. No notification.
- Remaining chars > 300: Response is delivered to the target channel.
This allows the agent to include brief status notes that get suppressed:
# Suppressed (< 300 chars after stripping)
"HEARTBEAT_OK. All 3 endpoints healthy. No new emails."
# Delivered (> 300 chars after stripping)
"HEARTBEAT_OK. However, I noticed the /api/checkout endpoint
is responding at 487ms average over the last hour, approaching
your 500ms threshold. The issue appears to be related to a
slow database query on the orders table. I've identified the
specific query and can optimize it if you'd like. Additionally,
there are 3 unread emails from legal@company.com that arrived
in the last 2 hours."
Session Preservation
Heartbeat-only replies (suppressed HEARTBEAT_OK) do not keep the session alive. OpenClaw restores the updatedAt timestamp after a heartbeat, preventing heartbeat activity from artificially extending idle session expiry. This is critical for cost management, because it means heartbeat-only activity doesn’t block session cleanup.
Heartbeat vs. Regular Turns
Understanding the differences helps you reason about cost and behavior:
| Aspect | Regular Turn | Heartbeat Turn |
|---|---|---|
| Trigger | User message | Scheduled interval |
| Prompt | User’s input | Heartbeat system prompt |
| HEARTBEAT.md | Not auto-loaded | Explicitly loaded |
| HEARTBEAT_OK | No special handling | Suppression logic applied |
| Session impact | Extends session lifetime | Does NOT extend lifetime |
| Model | Agent’s primary model | Can use cheaper override |
| Cost pattern | Pay per interaction | Continuous burn |
| Tool access | Full | Full (same profile) |
The model override is particularly useful. You can run heartbeat checks on a cheaper, faster model while keeping your primary agent on a more capable one:
{
"heartbeat": {
"every": "30m",
"model": "openrouter/openai/gpt-4o-mini"
}
}
Full Configuration Reference
Here’s the complete heartbeat configuration schema:
{
"agents": {
"defaults": {
"heartbeat": {
// Core settings
"every": "30m", // Interval: "15m", "1h", "2h30m", "0m" to disable
"model": null, // Override model for heartbeat turns
"prompt": null, // Override default heartbeat prompt
// Delivery settings
"target": "last", // "last", "none", or channel name
"to": null, // Specific destination (phone, username, etc.)
"accountId": null, // Multi-account routing
// Suppression settings
"ackMaxChars": 300, // Max chars after HEARTBEAT_OK for suppression
"includeReasoning": false,// Deliver separate reasoning messages
// Time restriction
"activeHours": {
"start": "09:00", // HH:MM, inclusive
"end": "22:00", // HH:MM, exclusive (24:00 allowed)
"timezone": "America/New_York" // IANA timezone or "user"
}
}
},
// Per-agent overrides
"list": [
{
"name": "assistant",
"heartbeat": {
"every": "15m",
"model": "openrouter/openai/gpt-4o-mini"
}
}
]
}
}
Important precedence rule: If any agent in agents.list defines heartbeat settings, only those agents run heartbeats. Agents without explicit heartbeat config are excluded from the schedule.
Manual Triggers and System Events
Heartbeats aren’t limited to the cron schedule. You can trigger them manually or via system events.
CLI Trigger
# Fire immediately
openclaw system event --text "Check for urgent follow-ups" --mode now
# Queue for next scheduled heartbeat
openclaw system event --text "Review deployment status" --mode next-heartbeat
System Event Triggers
OpenClaw can trigger additional heartbeat runs in response to system events like incoming messages or command completions. The gateway calls requestHeartbeatNow() to inject an immediate heartbeat turn.
This is powerful but comes with a caveat: if heartbeat turns execute commands, and command completions trigger heartbeats, you can create feedback loops. This is a known issue the OpenClaw team is actively addressing with re-entrancy protection.
Cost Reality Check
Heartbeats run full agent turns with complete conversation history. This means real token consumption at every interval.
Rough Cost Estimates (per agent, per day)
| Model | 30min interval (48/day) | 1hr interval (24/day) |
|---|---|---|
| Claude Sonnet | ~$3-5/day | ~$1.50-2.50/day |
| Claude Opus | ~$15-30/day | ~$7-15/day |
| GPT-4o Mini | ~$0.10-0.50/day | ~$0.05-0.25/day |
Costs scale with conversation history length. A fresh session with a small HEARTBEAT.md burns minimal tokens. A session with thousands of messages in history burns significantly more.
Cost Optimization Strategies
- Use a cheaper model override for heartbeat turns
- Keep HEARTBEAT.md small - every character is prompt tokens
- Set active hours - no need to check calendars at 3am
- Increase interval for less critical monitoring (1h or 2h)
- Leave HEARTBEAT.md empty until you need proactive behavior
- Set target to “none” if you only need internal state updates
On Augmi, new agents ship with an empty HEARTBEAT.md by default, so you pay zero for heartbeats until you’re ready to use them.
Known Gotchas
1. Exec-Triggered Feedback Loops
If your HEARTBEAT.md includes tasks that run bash commands, each command completion can trigger another heartbeat via requestHeartbeatNow(). This creates a loop: heartbeat runs command, command triggers heartbeat, heartbeat runs command. The fix is coming via re-entrancy protection (#2804).
2. Context Compression Breaks Heartbeats
When the conversation context hits the compression threshold, heartbeats can stop firing (#2935). Workaround: keep heartbeat sessions lean or use a dedicated session for heartbeat activity.
3. Empty File + wakeMode Conflict
Cron jobs configured with wakeMode: "now" are incorrectly skipped when HEARTBEAT.md is empty (#2217). If you need always-executing cron jobs, ensure HEARTBEAT.md has content.
HEARTBEAT.md Templates for Common Use Cases
DevOps Monitoring
# HEARTBEAT.md - DevOps
## Critical (always check)
- Run `curl -s -o /dev/null -w "%{http_code}" https://api.myapp.com/health`
- If not 200, alert immediately with status code
- Check error rate: `curl -s https://api.myapp.com/metrics | grep error_rate`
- Alert if > 1%
## Hourly
- Summarize any new alerts from PagerDuty (via MCP)
- Check deployment pipeline status
Personal Productivity
# HEARTBEAT.md - Personal
## Morning (first check of the day)
- Scan calendar for conflicts or double-bookings
- Check email for anything from my manager or direct reports
- Review Slack for overnight mentions
## Throughout Day
- Any PRs waiting on my review for > 4 hours?
- Any blocked tasks in my Jira queue?
## Evening (last check)
- Quick summary: what happened today?
- Anything I need to prep for tomorrow?
Business Intelligence
# HEARTBEAT.md - Business
## Every Check
- Pull latest metrics from Stripe dashboard (via MCP)
- Compare today's revenue to same day last week
- Alert if daily revenue drops > 20% from average
## Morning Only
- Summarize overnight customer support tickets
- Flag any churn signals (cancellation emails, downgrade requests)
How Augmi Handles Heartbeats
When you deploy an OpenClaw agent through Augmi, we generate the initial configuration including the empty HEARTBEAT.md template. The heartbeat infrastructure runs inside the agent’s Fly.io machine alongside the OpenClaw gateway.
You can edit HEARTBEAT.md directly through the agent’s terminal or control panel. Changes take effect on the next heartbeat cycle, no restart required.
We’re actively building dashboard features to make heartbeat management more visual: editing HEARTBEAT.md through a UI, viewing heartbeat execution logs, and tracking heartbeat costs separately from interactive usage. Stay tuned.
The Bottom Line
The heartbeat is deceptively simple. It’s a cron job that sends a prompt. But that simplicity is the point. By reducing proactive AI to a scheduled turn with a markdown checklist, OpenClaw made autonomous agent behavior something you can reason about, configure, and control.
The agent reads a file. It checks the list. It decides if anything matters. If not, it says HEARTBEAT_OK and the message disappears. If something does matter, it tells you.
That’s the entire mechanism that’s reshaping how we think about software.
Want to deploy your own heartbeat-driven agent? Get started on Augmi and have an agent running in under a minute.
For the industry analysis of why this matters, read The Heartbeat That Killed SaaS.
