Skip to main content

Overview

Schedule tasks to run in the future — whether that’s seconds from now, at a specific date/time, or on a recurring cron schedule. Scheduled tasks survive agent restarts and are persisted to SQLite. The scheduling system supports four modes:
Under the hood, scheduling uses Durable Object alarms to wake the agent at the right time. Tasks are stored in a SQLite table and executed in order.

Quick Start

Scheduling Modes

Delayed Execution

Pass a number to schedule a task to run after a delay in seconds:
Use cases:
  • Debouncing rapid events
  • Delayed notifications (“You left items in your cart”)
  • Retry with backoff
  • Rate limiting

Scheduled Execution

Pass a Date object to schedule a task at a specific time:
Use cases:
  • Appointment reminders
  • Deadline notifications
  • Scheduled content publishing
  • Time-based triggers

Recurring (Cron)

Pass a cron expression string for recurring schedules:
Cron syntax: minute hour day month weekday Common patterns:
Use cases:
  • Daily/weekly reports
  • Periodic cleanup jobs
  • Polling external services
  • Health checks
  • Subscription renewals

Interval

Use scheduleEvery() to run a task at fixed intervals (in seconds). Unlike cron, intervals support sub-minute precision and arbitrary durations:

Idempotency

scheduleEvery() is idempotent on the combination of callback name, interval, and payload — calling it multiple times with the same arguments does not create duplicate schedules. This makes it safe to call in onStart(), which runs on every Durable Object wake:
Calling scheduleEvery() with a different interval or payload creates a separate schedule, even for the same callback:
Different callbacks also get their own independent schedules:

Key Differences from Cron

Overlap Prevention

If a callback takes longer than the interval, the next execution is skipped (not queued). This prevents runaway resource usage:
When a skip occurs, you’ll see a warning in logs:

Error Resilience

If the callback throws an error, the interval continues — only that execution fails:
Use cases:
  • Sub-minute polling (every 10, 30, 45 seconds)
  • Intervals that don’t map to cron (every 90 seconds, every 7 minutes)
  • Rate-limited API polling with precise control
  • Real-time data synchronization

Keeping the Agent Alive

Durable Objects are evicted after a period of inactivity (typically 70-140 seconds with no incoming requests, WebSocket messages, or alarms). During long-running operations — streaming LLM responses, waiting on external APIs, running multi-step computations — the agent can be evicted mid-flight. keepAlive() prevents this by creating a 30-second heartbeat schedule that keeps the agent active until you are done:
The returned disposer function cancels the heartbeat. Always call it when the work is done — otherwise the heartbeat continues indefinitely.

keepAliveWhile()

For scoped work, use keepAliveWhile() — it runs an async function and automatically cleans up the heartbeat when it completes (or throws):
This is the recommended approach since you cannot forget to dispose the heartbeat.

How It Works

keepAlive() calls scheduleEvery(30, "_cf_keepAliveHeartbeat") under the hood. The internal _cf_keepAliveHeartbeat callback is a no-op — the alarm firing itself is what resets the inactivity timer. Because it uses the scheduling system:
  • The heartbeat does not conflict with your own schedules (the scheduling system multiplexes through a single alarm slot)
  • The heartbeat shows up in getSchedules() if you need to inspect it
  • Multiple concurrent keepAlive() calls each get their own schedule, so they do not interfere with each other

When to Use keepAlive()

keepAlive() is marked @experimental and may change between releases.

Managing Schedules

Get a Schedule

Retrieve a scheduled task by its ID:

List Schedules

Query scheduled tasks with optional filters:

Cancel a Schedule

Remove a scheduled task before it executes:
Example: Cancellable reminders

The Schedule Object

When you create or retrieve a schedule, you get a Schedule object:

Patterns

Rescheduling from Callbacks

For dynamic recurring schedules, schedule the next run from within the callback:

Retry on Failure

For immediate retries (within seconds), use the built-in retry option:
For longer recovery windows (minutes or hours), combine this.retry() for immediate retries with scheduled retries for extended outages:
See Retries for full documentation on retry options and patterns.

Self-Destructing Agents

You can safely call this.destroy() from within a scheduled callback:

Timezone-Aware Scheduling

JavaScript Dates are UTC by default. For timezone-aware scheduling:

AI-Assisted Scheduling

The SDK includes utilities for parsing natural language scheduling requests with AI. getSchedulePrompt() Returns a system prompt for parsing natural language into scheduling parameters:
When using scheduleSchema with OpenAI models via the AI SDK, you must pass providerOptions: { openai: { strictJsonSchema: false } } to generateObject. This is because the schema uses a discriminated union which is not compatible with OpenAI’s strict structured outputs mode.

Scheduling vs Queue vs Workflows

Use Queue when:
  • You need background processing without blocking the response
  • Tasks should run ASAP but don’t need to block
  • Order matters (FIFO)
Use Scheduling when:
  • Tasks need to run at a specific time
  • You need recurring jobs (cron)
  • Delayed execution (debouncing, retries)
Use Workflows when:
  • Multi-step processes with dependencies
  • Automatic retries with backoff
  • Human-in-the-loop approvals
  • Long-running tasks (minutes to hours)

API Reference

schedule()

Schedule a task for future execution. Parameters:
  • when - When to execute: number (seconds delay), Date (specific time), or string (cron expression)
  • callback - Name of the method to call
  • payload - Data to pass to the callback (must be JSON-serializable)
  • options.retry - Optional retry configuration. See Retries for details.
Returns: A Schedule object with the task details

scheduleEvery()

Schedule a task to run repeatedly at a fixed interval. Parameters:
  • intervalSeconds - Number of seconds between executions (must be > 0)
  • callback - Name of the method to call
  • payload - Data to pass to the callback (must be JSON-serializable)
  • options.retry - Optional retry configuration. See Retries for details.
Returns: A Schedule object with type: "interval" Behavior:
  • Idempotent on (callback, interval, payload) — calling with the same callback, interval, and payload returns the existing schedule instead of creating a duplicate. A different interval or payload creates a new, independent schedule.
  • First execution occurs after intervalSeconds (not immediately)
  • If callback is still running when next execution is due, it’s skipped (overlap prevention)
  • If callback throws an error, the interval continues
  • Cancel with cancelSchedule(id) to stop the entire interval

getSchedule()

Get a scheduled task by ID. This method is synchronous.

getSchedules()

Get scheduled tasks matching the criteria. This method is synchronous.

cancelSchedule()

Cancel a scheduled task. Returns true if cancelled, false if not found.

keepAlive()

Create a 30-second heartbeat schedule that prevents the Durable Object from being evicted due to inactivity. Returns a disposer function that cancels the heartbeat when called. The disposer is idempotent — calling it multiple times is safe.

keepAliveWhile()

Run an async function while keeping the Durable Object alive. The heartbeat is automatically started before the function runs and stopped when it completes (whether it succeeds or throws). Returns the value returned by the function. This is the recommended way to use keepAlive — it guarantees cleanup.

Limits

  • Maximum tasks: Limited by SQLite storage (each task is a row). Practical limit is tens of thousands per agent.
  • Task size: Each task (including payload) can be up to 2MB.
  • Minimum delay: 0 seconds (runs on next alarm tick)
  • Cron precision: Minute-level (not seconds)
  • Interval precision: Second-level
  • Cron jobs: After execution, automatically rescheduled for the next occurrence
  • Interval jobs: After execution, rescheduled for now + intervalSeconds; skipped if still running