AI / GenAI  /  OpenClaw

🦞 OpenClaw Guide 4 of 4 14 guides · updated 2026

Running your own self-hosted AI assistant — install, gateway architecture, messaging channels, skills, scheduling, and the security work that makes it safe to leave running.

Cron, Heartbeat and Webhooks: Making OpenClaw Proactive

Everything up to this point has been reactive. You message, it answers. Useful — but it’s still a tool you have to remember to pick up, and the things you most want an assistant for are precisely the things you forget.

The scheduler is what changes the category. An agent that wakes up on its own can notice that a certificate expires in nine days, that an invoice went unpaid, that you said you’d follow up on Tuesday and it’s Thursday. The value shifts from “saves me typing” to “catches what I would have dropped.”

It’s also where people burn money and patience. This page covers all three mechanisms and, just as importantly, how to keep them from becoming expensive noise.


Three Ways Things Happen Without You

┌─────────────────────────────────────────────────────┐
│ │
│ CRON fixed schedule │
│ ──────► "every weekday at 08:00" │
│ deterministic, you define the task │
│ │
│ HEARTBEAT periodic open turn │
│ ──────► "every 4 hours, think" │
│ agent decides if anything's needed │
│ │
│ WEBHOOKS external trigger │
│ ──────► "CI failed" / "form submitted" │
│ event-driven, reactive to systems │
│ │
└─────────────────────────────────────────────────────┘
Agent turn
(model + tools + memory)

They differ in who decides whether something should happen:

Most useful setups use all three, for different jobs.


Cron: Scheduled Jobs

{
cron: {
enabled: true,
sessionRetention: "24h"
}
}

sessionRetention matters more than it looks. Every cron run creates a session. Without retention limits, a job running hourly accumulates sessions indefinitely, which costs disk and makes the Control UI unusable. "24h" is a sensible default.

What cron is genuinely good for

The distinguishing quality of a good cron job is that it’s specific and deterministic. You know exactly what should happen; you just don’t want to be the one triggering it.

Write the failure case into the job

The most common cron mistake isn’t a bad schedule — it’s a job that reports success ambiguously. If your nightly backup check messages you every night saying “backups look fine,” you’ll stop reading it within a week, and then you’ll miss the night it says otherwise.

Alert on exception, not on completion. A job that stays silent unless something is wrong is a job you’ll still trust in six months. If you want positive confirmation, batch it into a weekly summary rather than a nightly ping.

Give cron jobs a restricted agent

Scheduled jobs run unattended, which means exec.ask: "always" can’t protect you — there’s nobody there to approve. Route cron work to an agent with a deliberately narrow tool set, and grant execution only for the specific jobs that genuinely need it.


Heartbeat: The Proactive Loop

{
agents: {
defaults: {
heartbeat: { every: "4h" }
}
}
}

The heartbeat is a recurring turn where the agent gets to think without a prompt. It reviews its memory, its context, whatever tools it has, and decides whether anything warrants action.

This is the feature that makes OpenClaw feel qualitatively different from a chatbot. It’s also the single largest driver of ongoing cost, and the two facts are related — you’re paying for the agent to think about whether it needs to do anything, whether or not it does.

The cost maths, explicitly

Do this arithmetic before choosing an interval, because almost nobody does and almost everybody is surprised.

IntervalInvocations/dayInvocations/month
30m48~1,460
1h24~730
4h6~180
12h2~60

Each invocation carries the system prompt, the memory files, and whatever context has accumulated. That’s not a trivial payload — for a well-developed setup it’s easily thousands of tokens before the agent does anything at all.

Going from 30m to 4h cuts cost by roughly 87%. And here’s the thing most people find once they try it: it barely changes the usefulness. The scenarios where a 30-minute heartbeat beats a 4-hour one are narrow. The scenarios where your bill triples are not.

Terminal window
openclaw config set agents.defaults.heartbeat.every "4h"

Start there. Tighten only when you can name the specific thing a longer interval is missing.

Make the heartbeat’s job explicit

A heartbeat with no guidance produces one of two failure modes: an agent that does nothing useful, or an agent that invents busywork and messages you about it.

Give it a defined remit, ideally as a skill (see Skills in OpenClaw):

---
name: heartbeat-check
description: Periodic proactive review — run on heartbeat wake-ups
disable-model-invocation: false
---
On a heartbeat wake-up, check the following, in order:
1. Read `memory/commitments.md`. Are any items due within 24 hours and
not marked done?
2. Check the calendar for events in the next 12 hours that need
preparation (travel time, documents, prep notes).
3. Check whether any item in `memory/waiting-on.md` has been outstanding
more than 5 days.
**Message the user only if at least one check produces something actionable.**
If everything is clear, do nothing and send nothing. Silence is the
expected outcome most of the time.
Never send more than one message per wake-up. Combine findings.

That last instruction block is the important part. Without an explicit “silence is the expected outcome,” you get an assistant that pings you every four hours to say everything’s fine — which trains you to ignore it, defeating the purpose entirely.


Webhooks: External Triggers

{
hooks: {
enabled: true,
token: "a-long-random-shared-secret",
path: "/hooks"
}
}

Webhooks let external systems wake your agent. CI finishes, monitoring fires, a form is submitted, a payment clears — the event arrives and the agent handles it.

This is the most efficient of the three mechanisms, because nothing runs unless something actually happened. No polling, no speculative wake-ups.

Securing the endpoint

The token is a shared secret, and it needs to be treated as one. Anything that can call this endpoint can wake your agent and put content into its context. That’s a direct injection path.

Terminal window
openssl rand -hex 32

Three rules:

Never expose the hooks endpoint directly to the internet. With gateway.bind on loopback — the default — it isn’t reachable externally anyway. If you need external systems to reach it, use a tunnel or a reverse proxy that terminates TLS and enforces auth, not a forwarded port. See Remote Access and Day-2 Operations.

Treat webhook payloads as untrusted. A webhook body is attacker-influenced text if any part of the upstream system accepts user input. A GitHub webhook carrying a PR title carries whatever someone typed into that title field. Route webhook-triggered work to a restricted agent.

Rotate the token if it leaks. It ends up in the configuration of whatever system calls it, which means it spreads to places you don’t fully control.

A worked example

CI failure notification. GitHub Actions posts to your hooks endpoint on a failed build; the agent reads the failure, checks whether it’s a known flaky test, and messages you only if it looks real.

The value isn’t the notification — GitHub already sends those. It’s the filtering. An agent that knows “the integration suite fails spuriously about once a week, don’t wake me for that” turns a noisy alert stream into a useful one.

That’s the general pattern for webhooks: you already get the alerts; what you want is judgement applied to them before they reach you.


Choosing the Right Mechanism

You want…UseWhy
Something at a fixed timeCronDeterministic, cheap, predictable
The agent to notice thingsHeartbeatOpen-ended judgement
Response to an external eventWebhookZero cost when nothing happens
A daily digestCronFixed schedule, fixed content
Reminders about commitmentsHeartbeatNeeds to weigh what’s relevant
Alerting on CI or monitoringWebhookThe event already exists

The general principle: prefer webhooks to cron, and cron to heartbeat. Webhooks cost nothing when idle. Cron costs a known amount. Heartbeat costs continuously regardless of whether there’s anything to do.

Use the heartbeat only for work that genuinely requires open-ended judgement — and keep the interval wide.


Cost Control, Concretely

Scheduling is where a pleasant monthly bill becomes an unpleasant one. Five measures, in order of effect:

1. Widen the heartbeat. Biggest lever by a distance. Start at 4h.

2. Move work from heartbeat to cron. If you find yourself writing “check X” into the heartbeat’s remit, and X only needs checking once a day, that’s a cron job. You’ve been paying for six checks to get one useful one.

3. Use a cheaper model for routine jobs. A digest that reads a calendar and formats it doesn’t need your best model. Reserve that for work involving real reasoning.

One caution: the documentation is explicit that for tool-enabled agents, or agents that read untrusted content, prompt-injection risk with older or smaller models is often too high. Downgrade the model on jobs that read trusted input and hold few tools — not on the one with exec.

4. Cap session retention. cron.sessionRetention: "24h" stops scheduled runs accumulating context.

5. Watch the first fortnight. The Control UI shows sessions and tool calls live. Actual behaviour rarely matches the prediction, and two weeks of observation will show you exactly which job is expensive.


The Notification Discipline

A technically perfect scheduler that messages you too often is a failure, because you’ll mute it — and a muted assistant catches nothing.

Rules that hold up in practice:

Silence is the default. Scheduled work should produce no message unless there’s something actionable. Write this into the instructions explicitly; agents are helpful by disposition and will report success unless told not to.

One message per wake-up. Combine findings. Three separate notifications for three findings is three interruptions.

Match urgency to channel. Routine digests to a low-priority channel; genuine alerts to the one that buzzes your phone. Most people have exactly one channel and wonder why everything feels equally urgent.

Include the action. “Certificate expires in 9 days” is a notification. “Certificate for api.example.com expires 2026-08-12 — renew with certbot renew” is useful. The second costs the agent nothing extra and saves you a context switch.


Troubleshooting

Nothing runs on schedule at all. Is the daemon running? openclaw gateway status. Scheduling requires the Gateway to survive logout and reboot — which requires --install-daemon at onboarding.

Jobs run but you never hear about them. Check the agent has a messaging tool and a channel to send on. An unattended job with no output path fails silently.

The heartbeat messages constantly. No explicit silence instruction. Add “message only if actionable; otherwise do nothing” to its remit.

Costs jumped without you changing anything. Memory grew. Every scheduled invocation carries it. Prune — see Memory and Workspace.

A cron job stalls waiting for approval. exec.ask: "always" with nobody there to approve. Either grant the specific tool to that agent without approval, or restructure the job to avoid execution.

Webhooks return 401. Token mismatch between config and caller. If it was working and stopped, someone rotated one side.


Where to Go Next

You now have an assistant that acts on its own — which is the payoff for everything before it, and also the point at which the security questions stop being theoretical.

An unattended agent, waking on a schedule, reading external content, with tools available and nobody watching, is a meaningfully different risk profile from a chat window. The OpenClaw Threat Model is the next page, and at this stage it’s the important one.