Introduction

While HEARTBEAT.md defines what your agent checks, the heartbeat configuration in openclaw.json controls when and how those checks run. This lesson covers intervals, active hours, target routing, and visibility flags that let you fine-tune heartbeat behavior for different environments and use cases.

Key Concepts

  • Interval: The time between heartbeat cycles, defaulting to 30 minutes (or 60 minutes for Anthropic OAuth accounts)
  • activeHours: A time window with timezone support that restricts when heartbeats fire
  • target: Controls where heartbeat results are routed: "last" (most recent conversation), "channel" (a specific channel), or "none" (silent)
  • showOk / showAlerts: Boolean flags controlling whether HEARTBEAT_OK and alert messages are displayed
  • useIndicator: Enables a visual status indicator that reflects the last heartbeat result

Real World Context

A DevOps team runs heartbeat checks on their staging environment. During business hours (9 AM to 6 PM EST), they want alerts sent to their Slack channel every 15 minutes. Outside business hours, they want silent heartbeats that only log results for review the next morning. Using activeHours and target routing, they configure a single agent to handle both scenarios without any custom code.

Deep Dive

Heartbeat configuration lives in the heartbeat section of your agent's configuration in openclaw.json:

json
{
  "agents": {
    "list": [
      {
        "id": "monitor",
        "heartbeat": {
          "interval": 30,
          "activeHours": {
            "start": "09:00",
            "end": "18:00",
            "timezone": "America/New_York"
          },
          "target": "channel",
          "showOk": false,
          "showAlerts": true,
          "useIndicator": true
        }
      }
    ]
  }
}

This configuration sets the monitor agent to run heartbeats every 30 minutes, but only between 9 AM and 6 PM Eastern time. Results are routed to a designated channel. Successful checks are suppressed (showOk: false) while alerts are shown (showAlerts: true). The visual indicator updates to reflect the latest status.

Interval Options

The default interval is 30 minutes for most accounts. Anthropic OAuth accounts default to 60 minutes due to rate limit considerations. You can set any value in minutes:

json
{
  "heartbeat": {
    "interval": 15
  }
}

This sets the heartbeat to fire every 15 minutes. Shorter intervals provide faster detection but consume more API tokens.

Target Routing

The target field determines where heartbeat results are sent:

json
{
  "heartbeat": {
    "target": "last"
  }
}

With "last", results appear in the most recently active conversation. With "channel", results go to a designated monitoring channel. With "none", results are logged internally but not sent anywhere visible. The "none" option is particularly useful for background monitoring where you only want to track results in logs.

Active Hours with Timezone

The activeHours block accepts IANA timezone identifiers:

json
{
  "heartbeat": {
    "activeHours": {
      "start": "06:00",
      "end": "22:00",
      "timezone": "Europe/London"
    }
  }
}

This restricts heartbeats to between 6 AM and 10 PM London time. Outside this window, heartbeat cycles are skipped entirely, saving API costs during overnight hours.

Visibility Flags

The three visibility flags work together to control output:

  • showOk: true displays HEARTBEAT_OK messages (default: true)
  • showAlerts: true displays alert messages when checks fail (default: true)
  • useIndicator: true shows a status icon (green/red) that reflects the last heartbeat result

A common production pattern is showOk: false, showAlerts: true, useIndicator: true, which suppresses the noise of successful checks while ensuring failures are immediately visible.

Common Pitfalls

  • Setting interval too low without considering cost: A 5-minute interval runs 288 heartbeats per day. At scale, this becomes expensive if each turn involves tool calls.
  • Forgetting timezone in activeHours: Without a timezone, the system uses UTC, which may not match your team's working hours.
  • Using target: "last" in multi-user setups: In a shared agent, "last" routes to whichever user was most recently active, which may not be the intended recipient.

Best Practices

  • Use activeHours to avoid wasting tokens overnight when no one is available to respond to alerts.
  • Set showOk to false in production to reduce noise and only surface actionable alerts.
  • Use target: "channel" for team monitoring so alerts go to a shared channel where anyone can respond.

Summary

  • The default heartbeat interval is 30 minutes (60 minutes for Anthropic OAuth accounts)
  • activeHours restricts heartbeats to a time window using IANA timezone identifiers
  • target routing controls where results are sent: "last", "channel", or "none"
  • showOk, showAlerts, and useIndicator control the visibility of heartbeat output
  • Combine showOk: false with showAlerts: true and useIndicator: true for clean production monitoring
✓ Completed