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:- Debouncing rapid events
- Delayed notifications (“You left items in your cart”)
- Retry with backoff
- Rate limiting
Scheduled Execution
Pass aDate object to schedule a task at a specific time:
- Appointment reminders
- Deadline notifications
- Scheduled content publishing
- Time-based triggers
Recurring (Cron)
Pass a cron expression string for recurring schedules:minute hour day month weekday
Common patterns:
- Daily/weekly reports
- Periodic cleanup jobs
- Polling external services
- Health checks
- Subscription renewals
Interval
UsescheduleEvery() 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:
scheduleEvery() with a different interval or payload creates a separate schedule, even for the same callback:
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:Error Resilience
If the callback throws an error, the interval continues — only that execution fails:- 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:
keepAliveWhile()
For scoped work, usekeepAliveWhile() — it runs an async function and automatically cleans up the heartbeat when it completes (or throws):
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()
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:The Schedule Object
When you create or retrieve a schedule, you get aSchedule 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:this.retry() for immediate retries with scheduled retries for extended outages:
Self-Destructing Agents
You can safely callthis.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)
- Tasks need to run at a specific time
- You need recurring jobs (cron)
- Delayed execution (debouncing, retries)
- Multi-step processes with dependencies
- Automatic retries with backoff
- Human-in-the-loop approvals
- Long-running tasks (minutes to hours)
API Reference
schedule()
when- When to execute:number(seconds delay),Date(specific time), orstring(cron expression)callback- Name of the method to callpayload- Data to pass to the callback (must be JSON-serializable)options.retry- Optional retry configuration. See Retries for details.
Schedule object with the task details
scheduleEvery()
intervalSeconds- Number of seconds between executions (must be > 0)callback- Name of the method to callpayload- Data to pass to the callback (must be JSON-serializable)options.retry- Optional retry configuration. See Retries for details.
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()
getSchedules()
cancelSchedule()
true if cancelled, false if not found.
keepAlive()
keepAliveWhile()
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