CLI commands
Cron
openclaw cron
Manage cron jobs for the Gateway scheduler.
Create jobs quickly
openclaw cron create is an alias for openclaw cron add. For new jobs, put the schedule first and the prompt second:
openclaw cron create "0 7 * * *" \ "Summarize overnight updates." \ --name "Morning brief" \ --agent opsUse --webhook <url> when the job should POST the finished payload instead of delivering to a chat target:
openclaw cron create "0 18 * * 1-5" \ "Summarize today's deploys as JSON." \ --name "Deploy digest" \ --webhook "https://example.invalid/openclaw/cron"Use --command for deterministic shell-style jobs that should run inside OpenClaw cron without starting an isolated agent/model run:
openclaw cron create "*/15 * * * *" \ --name "Queue depth probe" \ --command "scripts/check-queue.sh" \ --command-cwd "/srv/app" \ --announce \ --channel telegram \ --to "-1001234567890"--command <shell> stores argv: ["sh", "-lc", <shell>]. Use --command-argv '["node","scripts/report.mjs"]' for exact argv execution. Command jobs capture stdout/stderr, record normal cron history, and route output through the same announce, webhook, or none delivery modes as isolated jobs. A command that prints only NO_REPLY is suppressed.
Sessions
--session accepts main, isolated, current, or session:<id>.
Session keys
mainbinds to the agent's main session.isolatedcreates a fresh transcript and session id for each run.currentbinds to the active session at creation time.session:<id>pins to an explicit persistent session key.
Isolated session semantics
Isolated runs reset ambient conversation context. Channel and group routing, send/queue policy, elevation, origin, and ACP runtime binding are reset for the new run. Safe preferences and explicit user-selected model or auth overrides can carry across runs.
Delivery
openclaw cron list and openclaw cron show <job-id> preview the resolved delivery route. For channel: "last", the preview shows whether the route resolved from the main or current session, or will fail closed.
Provider-prefixed targets can disambiguate unresolved announce channels. For example, to: "telegram:123" selects Telegram when delivery.channel is omitted or last. Only prefixes advertised by the loaded plugin are provider selectors. If delivery.channel is explicit, the prefix must match that channel; channel: "whatsapp" with to: "telegram:123" is rejected. Service prefixes such as imessage: and sms: remain channel-owned target syntax.
Delivery ownership
Isolated cron chat delivery is shared between the agent and the runner:
- The agent can send directly using the
messagetool when a chat route is available. announcefallback-delivers the final reply only when the agent did not send directly to the resolved target.webhookposts the finished payload to a URL.nonedisables runner fallback delivery.
Use cron add|create --webhook <url> or cron edit <job-id> --webhook <url> to set webhook delivery. Do not combine --webhook with chat delivery flags such as --announce, --no-deliver, --channel, --to, --thread-id, or --account.
--announce is runner fallback delivery for the final reply. --no-deliver disables that fallback but does not remove the agent's message tool when a chat route is available.
Reminders created from an active chat preserve the live chat delivery target for fallback announce delivery. Internal session keys may be lowercase; do not use them as a source of truth for case-sensitive provider IDs such as Matrix room IDs.
Failure delivery
Failure notifications resolve in this order:
delivery.failureDestinationon the job.- Global
cron.failureDestination. - The job's primary announce target (when no explicit failure destination is set).
Note: isolated cron runs treat run-level agent failures as job errors even when no reply payload is produced, so model/provider failures still increment error counters and trigger failure notifications.
Command cron jobs do not start an isolated agent turn. A zero exit code records
ok; non-zero exit, signal, timeout, or no-output timeout records error and
can trigger the same failure notification path.
If an isolated run times out before the first model request, openclaw cron show
and openclaw cron runs include a phase-specific error such as
setup timed out before runner start or
stalled before first model call (last phase: context-engine).
For CLI-backed providers, the pre-model watchdog stays active until the external
CLI turn starts, so session lookup, hook, auth, prompt, and CLI setup stalls are
reported as pre-model cron failures.
Scheduling
One-shot jobs
--at <datetime> schedules a one-shot run. Offset-less datetimes are treated as UTC unless you also pass --tz <iana>, which interprets the wall-clock time in the given timezone.
Recurring jobs
Recurring jobs use exponential retry backoff after consecutive errors: 30s, 1m, 5m, 15m, 60m. The schedule returns to normal after the next successful run.
Skipped runs are tracked separately from execution errors. They do not affect retry backoff, but openclaw cron edit <job-id> --failure-alert-include-skipped can opt failure alerts into repeated skipped-run notifications.
For isolated jobs that target a local configured model provider, cron runs a lightweight provider preflight before starting the agent turn. Loopback, private-network, and .local api: "ollama" providers are probed at /api/tags; local OpenAI-compatible providers such as vLLM, SGLang, and LM Studio are probed at /models. If the endpoint is unreachable, the run is recorded as skipped and retried on a later schedule; matching dead endpoints are cached for 5 minutes to avoid many jobs hammering the same local server.
Note: cron jobs, pending runtime state, and run history live in the shared SQLite state database. Legacy jobs.json, jobs-state.json, and runs/*.jsonl files are imported once and renamed with a .migrated suffix. After import, edit schedules with openclaw cron add|edit|remove instead of editing JSON files.
Manual runs
openclaw cron run <job-id> force-runs by default and returns as soon as the manual run is queued. Successful responses include { ok: true, enqueued: true, runId }. Use the returned runId to inspect the later result:
openclaw cron run <job-id>openclaw cron runs --id <job-id> --run-id <run-id>Add --wait when a script should block until that exact queued run records a terminal status:
openclaw cron run <job-id> --wait --wait-timeout 10m --poll-interval 2sWith --wait, the CLI still calls cron.run first, then polls cron.runs for the returned runId. The command exits 0 only when the run finishes with status ok. It exits non-zero when the run finishes with error or skipped, when the Gateway response does not include a runId, or when --wait-timeout expires. --poll-interval must be greater than zero.
Models
cron add|edit --model <ref> selects an allowed model for the job.
Cron --model is a job primary, not a chat-session /model override. That means:
- Configured model fallbacks still apply when the selected job model fails.
- Per-job payload
fallbacksreplaces the configured fallback list when present. - An empty per-job fallback list (
fallbacks: []in the job payload/API) makes the cron run strict. - When a job has
--modelbut no fallback list is configured, OpenClaw passes an explicit empty fallback override so the agent primary is not appended as a hidden retry target. - Local-provider preflight checks walk configured fallbacks before marking a cron run
skipped.
openclaw doctor reports jobs that already have payload.model set, including provider namespace counts and mismatches against agents.defaults.model. Use that check when auth, provider, or billing behavior looks different between live chat and scheduled jobs.
Isolated cron model precedence
Isolated cron resolves the active model in this order:
- Gmail-hook override.
- Per-job
--model. - Stored cron-session model override (when the user selected one).
- Agent or default model selection.
Fast mode
Isolated cron fast mode follows the resolved live model selection. Model config params.fastMode applies by default, but a stored session fastMode override still wins over config.
Live model switch retries
If an isolated run throws LiveSessionModelSwitchError, cron persists the switched provider and model (and switched auth profile override when present) for the active run before retrying. The outer retry loop is bounded to two switch retries after the initial attempt, then aborts instead of looping forever.
Run output and denials
Stale acknowledgement suppression
Isolated cron turns suppress stale acknowledgement-only replies. If the first result is just an interim status update and no descendant subagent run is responsible for the eventual answer, cron re-prompts once for the real result before delivery.
Silent token suppression
If an isolated cron run returns only the silent token (NO_REPLY or no_reply), cron suppresses both direct outbound delivery and the fallback queued summary path, so nothing is posted back to chat.
Structured denials
Isolated cron runs use structured execution-denial metadata from the embedded run as the authoritative denial signal. They also honor node-host UNAVAILABLE wrappers when the nested structured error message starts with SYSTEM_RUN_DENIED or INVALID_REQUEST.
Cron does not classify final-output prose or approval-looking refusal phrases as denials unless the embedded run also provides structured denial metadata, so ordinary assistant text is not treated as a blocked command.
cron list and run history surface the denial reason instead of reporting a blocked command as ok.
Retention
Retention and pruning are controlled in config:
cron.sessionRetention(default24h) prunes completed isolated run sessions.cron.runLog.keepLinesprunes retained SQLite run-history rows per job.cron.runLog.maxBytesremains accepted for compatibility with older file-backed run logs.
Migrating older jobs
Common edits
Update delivery settings without changing the message:
openclaw cron edit <job-id> --announce --channel telegram --to "123456789"Disable delivery for an isolated job:
openclaw cron edit <job-id> --no-deliverEnable lightweight bootstrap context for an isolated job:
openclaw cron edit <job-id> --light-contextAnnounce to a specific channel:
openclaw cron edit <job-id> --announce --channel slack --to "channel:C1234567890"Announce to a Telegram forum topic:
openclaw cron edit <job-id> --announce --channel telegram --to "-1001234567890" --thread-id 42Create an isolated job with lightweight bootstrap context:
openclaw cron create "0 7 * * *" \ "Summarize overnight updates." \ --name "Lightweight morning brief" \ --session isolated \ --light-context \ --no-deliver--light-context applies to isolated agent-turn jobs only. For cron runs, lightweight mode keeps bootstrap context empty instead of injecting the full workspace bootstrap set.
Create a command job with exact argv, cwd, env, stdin, and output limits:
openclaw cron create "*/30 * * * *" \ --name "Position export" \ --command-argv '["node","scripts/export-position.mjs"]' \ --command-cwd "/srv/app" \ --command-env "NODE_ENV=production" \ --command-input '{"mode":"summary"}' \ --timeout-seconds 120 \ --no-output-timeout-seconds 30 \ --output-max-bytes 65536 \ --webhook "https://example.invalid/openclaw/cron"Common admin commands
Manual run and inspection:
openclaw cron listopenclaw cron list --agent opsopenclaw cron get <job-id>openclaw cron show <job-id>openclaw cron run <job-id>openclaw cron run <job-id> --dueopenclaw cron run <job-id> --wait --wait-timeout 10mopenclaw cron run <job-id> --wait --wait-timeout 10m --poll-interval 2sopenclaw cron runs --id <job-id> --limit 50openclaw cron runs --id <job-id> --run-id <run-id>openclaw cron list shows all matching jobs by default. Pass --agent <id> to show only jobs whose effective normalized agent id matches; jobs without a stored agent id count as the configured default agent.
openclaw cron get <job-id> returns the stored job JSON directly. Use cron show <job-id> when you want the human-readable view with delivery-route preview.
cron list --json and cron show <job-id> --json include a top-level status field on each job, computed from enabled, state.runningAtMs, and state.lastRunStatus. Values: disabled, running, ok, error, skipped, or idle. This mirrors the human-readable status column so external tooling can read job state without re-deriving it.
cron runs entries include delivery diagnostics with the intended cron target, the resolved target, message-tool sends, fallback use, and delivered state.
Agent and session retargeting:
openclaw cron edit <job-id> --agent opsopenclaw cron edit <job-id> --clear-agentopenclaw cron edit <job-id> --session currentopenclaw cron edit <job-id> --session "session:daily-brief"openclaw cron add warns when --agent is omitted on agent-turn jobs and falls back to the default agent (main). Pass --agent <id> at create time to pin a specific agent.
Delivery tweaks:
openclaw cron edit <job-id> --announce --channel slack --to "channel:C1234567890"openclaw cron edit <job-id> --webhook "https://example.invalid/openclaw/cron"openclaw cron edit <job-id> --best-effort-deliveropenclaw cron edit <job-id> --no-best-effort-deliveropenclaw cron edit <job-id> --no-deliver