Cron (OpenClaw Scheduled Tasks)

OpenClaw's built-in scheduler runs inside the Gateway process. Jobs persist at ~/.openclaw/cron/jobs.json and survive Gateway restarts. Every execution creates a Background Task record. One-shot jobs auto-delete after successful completion by default.

Schedule Types

Type Flag Description
One-shot --at ISO 8601 timestamp or relative expression
Fixed interval --every Duration-based recurrence
Cron expression --cron Standard 5- or 6-field expression; optional --tz

Staggering: recurring top-of-hour jobs auto-stagger up to 5 minutes unless --exact or --stagger <duration> specified.
Day logic: when both day-of-month and day-of-week are non-wildcard, croner uses OR logic (fires when either matches).
Timezone: --at without TZ defaults to UTC; --cron without --tz uses host timezone.

Execution Styles

Style --session Session Use Case
Main session main Enqueued at next heartbeat turn Reminders, system events
Isolated isolated Fresh cron:<jobId> session Reports, background work
Current current Session bound at creation time Context-aware tasks
Custom session:xxx Persistent named session History-building workflows

Isolated jobs support --light-context (skip workspace bootstrap) and --tools <list> (restrict tool access).

Model selection precedence (isolated jobs):

  1. Gmail hook override (if applicable)
  2. Per-job --model
  3. Stored session override
  4. Agent/default

Delivery Modes

Mode Behavior
announce Post summary to target channel (default for isolated)
webhook HTTP POST finished-event payload to URL
none No outbound delivery (internal only)

Channel targets: --channel slack --to "channel:C123", --channel telegram --to "-1001234567890". Forum topics append :topic:<id>.

Failure destination: cron.failureDestination (global) or job.delivery.failureDestination (per-job); falls back to primary announce target.

Task Reconciliation

An active cron task stays live while the cron runtime tracks that job as running, regardless of stale session rows. Tasks are marked lost after a 5-minute grace window expires.

Inbound Webhooks

Webhooks turn OpenClaw into an event receiver as well as a scheduler.

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

Auth: Authorization: Bearer <token> or x-openclaw-token: <token> header (query-string rejected).

Endpoint Purpose
POST /hooks/wake Enqueue system event (text, optional mode)
POST /hooks/agent Run isolated turn (message required)
POST /hooks/<name> Custom mapped hook via hooks.mappings

Security: keep behind loopback/trusted proxy; set hooks.allowedAgentIds; keep hooks.allowRequestSessionKey=false.

Gmail PubSub

openclaw webhooks gmail setup --account openclaw@gmail.com

Uses Tailscale Funnel for push endpoint. Gateway auto-starts gog gmail watch serve on boot. Supports per-Gmail model override via hooks.gmail.model.

Configuration

{
  cron: {
    enabled: true,
    store: "~/.openclaw/cron/jobs.json",
    maxConcurrentRuns: 1,
    retry: {
      maxAttempts: 3,
      backoffMs: [60000, 120000, 300000],
      retryOn: ["rate_limit", "overloaded", "network", "server_error"],
    },
    sessionRetention: "24h",
    runLog: { maxBytes: "2mb", keepLines: 2000 },
  },
}

Disable: cron.enabled: false or env OPENCLAW_SKIP_CRON=1.

Retry: one-shot jobs retry up to 3× (exponential backoff on transient errors); recurring jobs use escalating backoff 30s → 60m. Sessions pruned after sessionRetention; run logs capped by maxBytes/keepLines.

CLI Quick Reference

openclaw cron add --name "Brief" --cron "0 7 * * *" --tz "America/Los_Angeles" \
  --session isolated --message "Summarize overnight updates" \
  --announce --channel slack --to "channel:C123"

openclaw cron list
openclaw cron edit <id>
openclaw cron run <id>            # force-execute now
openclaw cron run <id> --due      # run only if scheduled time reached
openclaw cron runs --id <id> --limit 50
openclaw cron remove <id>

Troubleshooting

openclaw cron status
openclaw cron runs --id <jobId> --limit 20
openclaw system heartbeat last
openclaw logs --follow
openclaw doctor

See Also