Schedule tasks·续2

Use Queue when:

  • You need background processing without blocking the response
  • Tasks should run ASAP but do not 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()

async schedule<T>(
  when: Date | string | number,
  callback: keyof this,
  payload?: T,
  options?: { retry?: RetryOptions; idempotent?: boolean }
): Promise<Schedule<T>>

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. Refer to Retries for details
  • options.idempotent - Deduplicate by callback + payload. Defaults to true for cron schedules, false for delayed and Date-based schedules

Returns: A Schedule object with the task details

Idempotency:

Cron schedules are idempotent by default — calling schedule("0 * * * *", "tick") multiple times with the same callback, cron expression, and payload returns the existing schedule instead of creating a duplicate. Set idempotent: false to override this.

For delayed and Date-based schedules, set idempotent: true to opt in to the same dedup behavior (matched on callback + payload). This is especially useful when calling schedule() in onStart() to avoid accumulating duplicate rows across Durable Object restarts:

class MyAgent extends Agent {
    async onStart() {
        // Without idempotent: true, this creates a new row on every DO restart
        await this.schedule(3600, "hourlyCleanup", {}, { idempotent: true });
    }
}
class MyAgent extends Agent {
    async onStart() {
        // Without idempotent: true, this creates a new row on every DO restart
        await this.schedule(3600, "hourlyCleanup", {}, { idempotent: true });
    }
}

Caution

Tasks that set a callback for a method that does not exist will throw an exception. Ensure that the method named in the callback argument exists on your Agent class.

scheduleEvery()

async scheduleEvery<T>(
  intervalSeconds: number,
  callback: keyof this,
  payload?: T,
  options?: { retry?: RetryOptions }
): Promise<Schedule<T>>

Schedule a task to run repeatedly at a fixed interval.

Parameters:

  • intervalSeconds - Number of seconds between executions (must be greater than 0)
  • callback - Name of the method to call
  • payload - Data to pass to the callback (must be JSON-serializable)
  • options.retry - Optional retry configuration. Refer to Retries for details.

Returns: A Schedule object with type: "interval"

Behavior:

  • First execution occurs after intervalSeconds (not immediately)
  • If callback is still running when next execution is due, it is skipped (overlap prevention)
  • If callback throws an error, the interval continues
  • Cancel with cancelSchedule(id) to stop the entire interval

getScheduleById()

async getScheduleById(id: string): Promise<Schedule<unknown> | undefined>

Get a scheduled task by ID. Returns undefined if not found. This method works in both top-level agents and sub-agents.

listSchedules()

async listSchedules(criteria?: {
  id?: string;
  type?: "scheduled" | "delayed" | "cron" | "interval";
  timeRange?: { start?: Date; end?: Date };
}): Promise<Schedule<unknown>[]>

Get scheduled tasks matching the criteria. This method works in both top-level agents and sub-agents.

getSchedule()

getSchedule<T>(id: string): Schedule<T> | undefined

Deprecated. Get a scheduled task by ID synchronously. This method only works in top-level agents. Use await this.getScheduleById(id) instead.

getSchedules()

getSchedules<T>(criteria?: {
  id?: string;
  type?: "scheduled" | "delayed" | "cron" | "interval";
  timeRange?: { start?: Date; end?: Date };
}): Schedule<T>[]

Deprecated. Get scheduled tasks matching the criteria synchronously. This method only works in top-level agents. Use await this.listSchedules(criteria) instead.

cancelSchedule()

async cancelSchedule(id: string): Promise<boolean>

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

keepAlive()

async keepAlive(): Promise<() => void>

Prevent the Durable Object from being evicted due to inactivity by holding a 30-second alarm-backed heartbeat reference. Returns a disposer function that releases the heartbeat when called. The disposer is idempotent — calling it multiple times is safe.

Always call the disposer when the work is done — otherwise the heartbeat continues indefinitely.

const dispose = await this.keepAlive();
try {
    // Long-running work that must not be interrupted
    const result = await longRunningComputation();
    await sendResults(result);
} finally {
    dispose();
}
const dispose = await this.keepAlive();
try {
    // Long-running work that must not be interrupted
    const result = await longRunningComputation();
    await sendResults(result);
} finally {
    dispose();
}

keepAliveWhile()

async keepAliveWhile<T>(fn: () => Promise<T>): Promise<T>

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.

const result = await this.keepAliveWhile(async () => {
    const data = await longRunningComputation();
    return data;
});
const result = await this.keepAliveWhile(async () => {
    const data = await longRunningComputation();
    return data;
});

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 holding an in-memory heartbeat reference and using the Durable Object alarm system directly. The alarm firing itself resets the inactivity timer.

  • The heartbeat does not conflict with your own schedules because the alarm system multiplexes through a single alarm slot.
  • No schedule rows are created, and the heartbeat is invisible to listSchedules().
  • Multiple concurrent keepAlive() calls use a reference count, so one disposer does not release another caller's heartbeat.
  • Inside sub-agents, keepAlive() delegates that heartbeat reference to the top-level parent because facets do not have independent alarm slots.

Multiple concurrent callers

Each keepAlive() call returns an independent disposer:

const dispose1 = await this.keepAlive();
const dispose2 = await this.keepAlive();

// Both heartbeats are active
dispose1(); // Only cancels the first heartbeat
// Agent is still alive via dispose2's heartbeat

dispose2(); // Now the agent can go idle
const dispose1 = await this.keepAlive();
const dispose2 = await this.keepAlive();

// Both heartbeats are active
dispose1(); // Only cancels the first heartbeat
// Agent is still alive via dispose2's heartbeat

dispose2(); // Now the agent can go idle

AIChatAgent

AIChatAgent automatically calls keepAlive() during streaming responses. You do not need to add it yourself when using AIChatAgent — every LLM stream is protected from idle eviction by default.

When to use keepAlive

Scenario Use keepAlive?
Streaming LLM responses via AIChatAgent No — already built in
Long-running computation in a custom Agent Yes
Waiting on a slow external API call Yes
Multi-step tool execution Yes
Short request-response handlers No — not needed
Background work via scheduling or workflows No — alarms already keep the DO active

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

Next steps

Push notifications

Send browser push notifications using scheduling and web-push.

Queue tasks

Immediate background task processing.

Run Workflows

Durable multi-step background processing.

Agents API

Complete API reference for the Agents SDK.

Was this helpful?

YesNo

On this page

Schedule tasks·续2 - 图1Docs

{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/agents/runtime/execution/schedule-tasks/#page","headline":"Schedule tasks","description":"Schedule delayed, date-based, cron, and interval tasks on Agents with persistent SQLite-backed execution.","url":"https://developers.cloudflare.com/agents/runtime/execution/schedule-tasks/","inLanguage":"en","image":"https://developers.cloudflare.com/agents/runtime/execution/schedule-tasks/og.png?v=dc9be7068b6b2c24","dateModified":"2026-10-18","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}


description: Spawn child agents with isolated storage and typed RPC using subAgent(), abortSubAgent(), and deleteSubAgent(). title: Sub-agents image: https://developers.cloudflare.com/agents/runtime/execution/sub-agents/og.png?v=1d540fe8d5fdfbbf


Skip to content

Documentation Index
Fetch the complete documentation index at: https://developers.cloudflare.com/agents/llms.txt
Use this file to discover all available pages before exploring further.