Skip to content

Schedule Automated Tasks with OpenClaw Cron Jobs

Setting up your first automated task with OpenClaw cron is straightforward and takes just a few commands. You can schedule a quick reminder or list all your active jobs directly from your terminal using the CLI.

  1. Run the following command to add a one-shot reminder that deletes itself after running:
Terminal window
# Add a one-shot reminder
openclaw cron add \
--name "Reminder" \
--at "2026-02-01T16:00:00Z" \
--session main \
--system-event "Reminder: check the cron docs draft" \
--wake now \
--delete-after-run
# Check your jobs
openclaw cron list
# See run history
openclaw cron runs --id <job-id>
  1. You can check all your currently scheduled jobs with this command:
Terminal window
# Check your jobs
openclaw cron list
  1. If you need to see the execution history for a specific job, use the runs command:
Terminal window
# See run history
openclaw cron runs --id <job-id>
  1. Use the specific job ID from the list to track individual performance or debug issues.

The Gateway manages the entire scheduling lifecycle, ensuring your jobs persist even if the process restarts. It handles everything from waking the agent to cleaning up browser processes to keep your environment tidy.

  1. The cron engine runs directly inside the Gateway process rather than inside the model itself.
  2. Your job definitions are saved at ~/.openclaw/cron/jobs.json, so you won’t lose your schedules when you restart the service.
  3. The runtime execution state is stored separately in ~/.openclaw/cron/jobs-state.json.
  4. If you use git to track your definitions, you should track jobs.json but add jobs-state.json to your gitignore file.
  5. Note that older versions of OpenClaw can read jobs.json, but they might treat jobs as new because runtime fields are now moved to the state file.
  6. Every execution creates a background task record for tracking and auditing purposes.
  7. By default, one-shot jobs using the --at flag will auto-delete after they succeed.
  8. For isolated runs, the system tries its best to close any tracked browser tabs or processes for that specific cron:<jobId> session once the run finishes.
  9. The system also prevents stale replies; if the first result is just a status update like “on it,” OpenClaw will re-prompt for the actual result before delivery.
  10. Task reconciliation is owned by the runtime, meaning a task stays live as long as the cron runtime tracks it, even if old session rows exist.
  11. Once the runtime stops owning the job and a 5-minute grace window expires, maintenance can mark the task as lost.

You have three main ways to define when your tasks should run, ranging from simple delays to complex recurring patterns. All timestamps are treated as UTC unless you specify a different timezone.

KindCLI flagDescription
at--atOne-shot timestamp (ISO 8601 or relative like 20m)
every--everyFixed interval
cron--cron5-field or 6-field cron expression with optional --tz
  1. If you need local wall-clock scheduling, add a flag like --tz America/New_York to your command.
  2. To prevent load spikes, recurring top-of-the-hour tasks are automatically staggered by up to 5 minutes.
  3. You can use --exact to force precise timing or --stagger 30s to set your own explicit window.
  4. OpenClaw uses croner to parse expressions, which follows standard Vixie behavior for day-of-month and day-of-week logic.
  5. When both day fields are specified, the job runs if either condition is met (OR logic), not both.
# Intended: "9 AM on the 15th, only if it's a Monday"
# Actual: "9 AM on every 15th, AND 9 AM on every Monday"
0 9 15 * 1
  1. To require both conditions, use the + modifier like 0 9 15 * +1 or handle the logic within your prompt or command.

The execution style determines the context and environment your task uses when it runs. You can choose to run tasks in your main session or create isolated environments for background chores.

Style--session valueRuns inBest for
Main sessionmainNext heartbeat turnReminders, system events
IsolatedisolatedDedicated cron:<jobId>Reports, background chores
Current sessioncurrentBound at creation timeContext-aware recurring work
Custom sessionsession:custom-idPersistent named sessionWorkflows that build on history
  1. Main session jobs add a system event and can wake the heartbeat using --wake now or --wake next-heartbeat.
  2. Isolated jobs run in a fresh session, and the system automatically cleans up browser processes afterward to avoid orphaned processes.
  3. Custom sessions allow you to build workflows that remember previous runs, which is great for things like daily standup summaries.
  4. For isolated jobs, the system prefers the final output from subagents over any interim status text from the parent.
  5. You can customize isolated jobs with several payload options:
  • --message: The prompt text (this is required for isolated runs).
  • --model / --thinking: Overrides for the model and its reasoning level.
  • --light-context: Skips injecting workspace bootstrap files.
  • --tools exec,read: Limits which tools the job can access.
  1. Model selection follows a specific order: GitHub or Gmail hook overrides come first, followed by the job payload, then the stored session override, and finally the agent default.
  2. If a model has params.fastMode configured, isolated runs will use it by default unless a session override exists.
  3. If a run triggers a model-switch handoff, OpenClaw will retry with the new model and persist the selection, though it stops after 2 retries to avoid infinite loops.

Configure OpenClaw delivery and output modes

Section titled “Configure OpenClaw delivery and output modes”

When you run tasks, you need to know how OpenClaw handles the results through different delivery and output modes. You can choose where the information goes based on your specific needs for each job.

  1. The announce mode delivers a summary to your target channel, which is the standard choice for isolated sessions.
  2. The webhook mode uses a POST request to send the finished event JSON payload to a specific URL.
  3. The none mode is for internal use only and does not perform any external delivery.

You can use the command --announce --channel telegram --to "-1001234567890" for channel delivery. If you are sending to Telegram forum topics, use the format -1001234567890:topic:123. For Slack, Discord, or Mattermost, you need to use explicit prefixes like channel:<id> or user:<id>.

For isolated jobs owned by cron, the runner is responsible for the final delivery path. The agent is asked to provide a plain-text summary, which is then sent through announce, webhook, or kept internal with none. Using --no-deliver does not give delivery control back to the agent; it simply keeps the run internal. If a task specifically mentions messaging an external recipient, the agent should record who and where that message goes in its output instead of trying to send it directly.

Notifications for failures follow their own path:

  1. cron.failureDestination allows you to set a global default for all failure notifications.
  2. job.delivery.failureDestination lets you override that setting for a specific job.
  3. If you do not set either and the job already uses announce, failure notifications will go to that primary target.
  4. Note that delivery.failureDestination is only supported for jobs where sessionTarget="isolated", unless your primary delivery mode is webhook.

Setting up your automated tasks is simple once you see how the CLI commands look in practice. You can use these examples to build everything from quick reminders to complex weekly reports.

One-shot reminder (main session):

Terminal window
openclaw cron add \
--name "Calendar check" \
--at "20m" \
--session main \
--system-event "Next heartbeat: check calendar." \
--wake now

Recurring isolated job with delivery:

Terminal window
openclaw cron add \
--name "Morning brief" \
--cron "0 7 * * *" \
--tz "America/Los_Angeles" \
--session isolated \
--message "Summarize overnight updates." \
--announce \
--channel slack \
--to "channel:C1234567890"

Isolated job with model and thinking override:

Terminal window
openclaw cron add \
--name "Deep analysis" \
--cron "0 6 * * 1" \
--tz "America/Los_Angeles" \
--session isolated \
--message "Weekly deep analysis of project progress." \
--model "opus" \
--thinking high \
--announce

Configure OpenClaw Webhooks for External Triggers

Section titled “Configure OpenClaw Webhooks for External Triggers”

You can easily set up Gateway to listen for external events by exposing HTTP webhook endpoints. This allows your external services or third-party apps to trigger actions within your OpenClaw environment by sending simple requests.

  1. Open your configuration file to enable the feature.
  2. Define a secure token and the specific path where you want to receive requests.
{
hooks: {
enabled: true,
token: "shared-secret",
path: "/hooks",
},
}

To keep your setup secure, every request you send to the webhook needs to prove it is authorized. You have two main ways to provide the required token.

  1. Include the token in the header as Authorization: Bearer <token>, which is the method I recommend for most setups.
  2. Alternatively, you can use the x-openclaw-token: <token> header if that fits your workflow better.
  3. Keep in mind that OpenClaw will reject any tokens sent via query strings to prevent them from leaking in logs.

If you want to drop a system event into your main session, the wake endpoint is your best friend. This is perfect for simple notifications or triggers that don’t need a separate agent.

Terminal window
curl -X POST http://127.0.0.1:18789/hooks/wake \
-H 'Authorization: Bearer SECRET' \
-H 'Content-Type: application/json' \
-d '{"text":"New email received","mode":"now"}'
  1. The text field is required and should contain the description of the event you want to record.
  2. The mode field is optional; it defaults to now but you can set it to next-heartbeat if you want to delay the trigger.

Sometimes you need to run a specific, isolated agent task without affecting your main session. This endpoint allows you to trigger a single turn for an agent with specific parameters.

Terminal window
curl -X POST http://127.0.0.1:18789/hooks/agent \
-H 'Authorization: Bearer SECRET' \
-H 'Content-Type: application/json' \
-d '{"message":"Summarize inbox","name":"Email","model":"openai/gpt-5.4-mini"}'
  1. You must provide the message field to tell the agent what to do.
  2. You can also use optional fields like name, agentId, wakeMode, deliver, channel, to, model, thinking, and timeoutSeconds to fine-tune the execution.

You can define custom webhook names by using the hooks.mappings section in your config. This is very powerful because it lets you transform any incoming JSON payload into a wake or agent action using templates or code transforms.

Security is a big deal when exposing endpoints, so follow these best practices to keep your OpenClaw setup safe from unauthorized access.

  1. Keep your webhook endpoints behind a loopback address, a tailnet, or a trusted reverse proxy.
  2. Use a dedicated token for your webhook instead of reusing your main Gateway auth tokens.
  3. Always put hooks.path on a dedicated subpath because using the root / is rejected for safety.
  4. Limit which agents can be reached by setting the hooks.allowedAgentIds option.
  5. Keep hooks.allowRequestSessionKey set to false unless your specific use case requires callers to select their own sessions.
  6. If you do enable session keys, make sure to set hooks.allowedSessionKeyPrefixes to restrict the allowed session key shapes.
  7. You don’t need to worry about payload injection as much because hook payloads are wrapped with safety boundaries by default.

Connect Gmail to OpenClaw via Google PubSub

Section titled “Connect Gmail to OpenClaw via Google PubSub”

You can connect your Gmail inbox directly to OpenClaw using Google PubSub for real-time triggers. This setup allows you to wire inbox events to your agents so they can process incoming mail as soon as it arrives.

Before you start, make sure you have the gcloud CLI and gog (gogcli) installed. You also need OpenClaw hooks enabled and a public HTTPS endpoint, which you can easily get using Tailscale.

The fastest way to get running is to use the built-in setup wizard provided by the CLI. It automates most of the heavy lifting for you.

  1. Run the setup command and provide your Gmail address.
Terminal window
openclaw webhooks gmail setup --account openclaw@gmail.com
  1. This command automatically writes the hooks.gmail configuration and enables the Gmail preset.
  2. It also configures Tailscale Funnel to handle the push endpoint for you.

Once you have hooks.enabled set to true and your Gmail account configured, Gateway manages the watcher process for you.

  1. The Gateway will start gog gmail watch serve automatically every time it boots up.
  2. It also takes care of auto-renewing the watch so you don’t miss any emails.
  3. If you want to handle the watcher yourself, you can opt out by setting the OPENCLAW_SKIP_GMAIL_WATCHER=1 environment variable.

If you prefer to do things by hand or need a custom configuration, you can manually set up the GCP project and PubSub topic.

  1. Log in and select the GCP project that owns the OAuth client you are using with gog.
Terminal window
gcloud auth login
gcloud config set project <project-id>
gcloud services enable gmail.googleapis.com pubsub.googleapis.com
  1. Create the PubSub topic and grant the Gmail service account permission to publish events to it.
Terminal window
gcloud pubsub topics create gog-gmail-watch
gcloud pubsub topics add-iam-policy-binding gog-gmail-watch \
--member=serviceAccount:gmail-api-push@system.gserviceaccount.com \
--role=roles/pubsub.publisher
  1. Start the watch manually to begin monitoring your inbox for new messages.
Terminal window
gog gmail watch start \
--account openclaw@gmail.com \
--label INBOX \
--topic projects/<project-id>/topics/gog-gmail-watch

You can customize which AI model processes your emails by adding a specific override in your config file. This is useful if you want to use a cheaper or faster model for email summaries.

{
hooks: {
gmail: {
model: "openrouter/meta-llama/llama-3.3-70b-instruct:free",
thinking: "off",
},
},
}

Managing your automated tasks shouldn’t be a headache. You can handle everything from listing active tasks to forcing a manual run directly through the CLI.

  1. View all your scheduled tasks by running openclaw cron list.
  2. Update an existing job’s prompt or model with openclaw cron edit.
  3. Trigger a job manually using openclaw cron run.
  4. Add the —due flag to only run a job if its scheduled time has passed.
  5. Track previous executions with openclaw cron runs.
  6. Clean up your list by using openclaw cron remove.
  7. Assign jobs to specific agents in multi-agent environments using the —agent flag.
Terminal window
# List all jobs
openclaw cron list
# Edit a job
openclaw cron edit <jobId> --message "Updated prompt" --model "opus"
# Force run a job now
openclaw cron run <jobId>
# Run only if due
openclaw cron run <jobId> --due
# View run history
openclaw cron runs --id <jobId> --limit 50
# Delete a job
openclaw cron remove <jobId>
# Agent selection (multi-agent setups)
openclaw cron add --name "Ops sweep" --cron "0 6 * * *" --session isolated --message "Check ops queue" --agent ops
openclaw cron edit <jobId> --clear-agent

Regarding model overrides, using openclaw cron add|edit —model … updates the specific model for that job. If the model you choose is permitted, that specific provider and model will be used by the isolated agent during the run. If the model is not allowed, the system will show a warning and use the agent’s default model instead. While configured fallback chains still work, a simple —model override without a per-job fallback list will not automatically try the agent’s primary model as a hidden retry target.

Configure OpenClaw cron settings and retries

Section titled “Configure OpenClaw cron settings and retries”

Your OpenClaw cron behavior is defined within your main configuration file using JSON. This allows you to customize everything from where jobs are stored to how the system handles network errors and rate limits.

  1. Toggle the entire system on or off using the enabled key.
  2. Point to a specific JSON file in the store setting to save your jobs.
  3. Define how many tasks can run at once with maxConcurrentRuns.
  4. Set up retry parameters to handle temporary failures automatically.
  5. Use webhookToken to secure your external triggers.
  6. Control log sizes and session history with runLog and sessionRetention.
{
cron: {
enabled: true,
store: "~/.openclaw/cron/jobs.json",
maxConcurrentRuns: 1,
retry: {
maxAttempts: 3,
backoffMs: [60000, 120000, 300000],
retryOn: ["rate_limit", "overloaded", "network", "server_error"],
},
webhookToken: "replace-with-dedicated-webhook-token",
sessionRetention: "24h",
runLog: { maxBytes: "2mb", keepLines: 2000 },
},
}

The system creates a runtime state sidecar based on your cron.store path. For example, if your store is at ~/clawd/cron/jobs.json, the state is saved in ~/clawd/cron/jobs-state.json. If your path does not end in .json, the system simply adds -state.json to the end. You can stop the scheduler by setting cron.enabled: false or by using the environment variable OPENCLAW_SKIP_CRON=1.

For error handling, the system uses two types of retries. One-shot retry handles transient issues like network errors and server overloads by trying up to 3 times with exponential backoff. If an error is permanent, the job is disabled immediately. Recurring retry applies to scheduled tasks, using a backoff period between 30 seconds and 60 minutes that resets once a run succeeds. To keep your system healthy, cron.sessionRetention prunes old session entries after 24 hours by default, and the runLog settings automatically trim log files based on size and line count.

If you are having trouble with your OpenClaw troubleshooting process, this guide will help you go to the logs and configurations. Use these steps to identify why your tasks might not be running as expected.

Start by running these CLI commands in order to get a clear picture of your system state. These tools allow you to inspect everything from the Gateway health to specific cron execution histories.

  1. Check the general status and specific components:
Terminal window
openclaw status
openclaw gateway status
openclaw cron status
openclaw cron list
openclaw cron runs --id <jobId> --limit 20
openclaw system heartbeat last
openclaw logs --follow
openclaw doctor

Resolve issues when OpenClaw cron is not firing

Section titled “Resolve issues when OpenClaw cron is not firing”

When your scheduled tasks fail to start, the issue usually lies in the environment configuration or the Gateway status. Follow these steps to verify your setup.

  1. Check the cron.enabled setting in your configuration and ensure the OPENCLAW_SKIP_CRON environment variable isn’t set.
  2. Confirm that the Gateway is running continuously, as it is responsible for triggering the schedules.
  3. For cron schedules, verify that the timezone set with the --tz flag matches your host machine’s timezone.
  4. If you see reason: not-due in the run output, it means a manual run was checked with openclaw cron run <jobId> --due and the job was not actually scheduled to run yet.

If a job fires but you don’t receive the message, the problem is likely in the delivery target or authentication settings. Use these points to trace the communication path.

  1. Verify if the delivery mode is set to none, which means the system is not expected to send any external messages.
  2. Check if the delivery target is missing or invalid, such as a missing channel or to field, which causes the outbound step to be skipped.
  3. Look for channel authentication errors like unauthorized or Forbidden that indicate your credentials are blocking the delivery.
  4. If an isolated run returns only the silent token (NO_REPLY / no_reply), OpenClaw suppresses both the direct outbound delivery and the fallback queued summary.
  5. For cron-owned isolated jobs, do not expect the agent to use the message tool as a fallback; the runner owns the final delivery, and using --no-deliver keeps the process internal.

Timezone mismatches are a frequent cause of tasks running at the wrong time. Keep these rules in mind when configuring your schedules.

  1. Any cron job configured without the --tz flag will default to using the Gateway host timezone.
  2. All at schedules that do not specify a timezone are automatically treated as UTC.
  3. The heartbeat activeHours feature relies on the specific timezone resolution you have configured in your system.

To better understand the underlying mechanics of these features, you can explore the following resources. These guides provide more context on how tasks and timezones are handled within the system.

  1. Automation & Tasks — all automation mechanisms at a glance
  2. Background Tasks — task ledger for cron executions
  3. Heartbeat — periodic main-session turns
  4. Timezone — timezone configuration

AI Setup Assistant

OpenClaw

OpenClaw Expert

Still stuck?

If this page didn't answer your case, ask OpenClaw Expert for step-by-step guidance.