Schedule tasks

Last updated Oct 18, 2026 Schedule tasks to run in the future — whether that is seconds from now, at a specific date/time, or on a recurring cron schedule. Scheduled tasks survive agent restarts and are persisted to SQLite.

Scheduled tasks can do anything a request or message from a user can: make requests, query databases, send emails, read and write state. Scheduled tasks can invoke any regular method on your Agent.

Overview

The scheduling system supports four modes:

Mode Syntax Use case
Delayed this.schedule(60, ...) Run in 60 seconds
Scheduled this.schedule(new Date(...), ...) Run at specific time
Cron this.schedule("0 8 * * *", ...) Run on recurring schedule
Interval this.scheduleEvery(30, ...) Run every 30 seconds

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

import { Agent } from "agents";

export class ReminderAgent extends Agent {
    async onRequest(request) {
        const url = new URL(request.url);

// Schedule in 30 seconds
        await this.schedule(30, "sendReminder", {
            message: "Check your email",
        });

// Schedule at specific time
        await this.schedule(new Date("2025-02-01T09:00:00Z"), "sendReminder", {
            message: "Monthly report due",
        });

// Schedule recurring (every day at 8am)
        await this.schedule("0 8 * * *", "dailyDigest", {
            userId: url.searchParams.get("userId"),
        });

return new Response("Scheduled!");
    }

async sendReminder(payload) {
        console.log(`Reminder: ${payload.message}`);
        // Send notification, email, etc.
    }

async dailyDigest(payload) {
        console.log(`Sending daily digest to ${payload.userId}`);
        // Generate and send digest
    }
}
import { Agent } from "agents";

export class ReminderAgent extends Agent {
    async onRequest(request: Request) {
        const url = new URL(request.url);

// Schedule in 30 seconds
        await this.schedule(30, "sendReminder", {
            message: "Check your email",
        });

// Schedule at specific time
        await this.schedule(new Date("2025-02-01T09:00:00Z"), "sendReminder", {
            message: "Monthly report due",
        });

// Schedule recurring (every day at 8am)
        await this.schedule("0 8 * * *", "dailyDigest", {
            userId: url.searchParams.get("userId"),
        });

return new Response("Scheduled!");
    }

async sendReminder(payload: { message: string }) {
        console.log(`Reminder: ${payload.message}`);
        // Send notification, email, etc.
    }

async dailyDigest(payload: { userId: string }) {
        console.log(`Sending daily digest to ${payload.userId}`);
        // Generate and send digest
    }
}

Scheduling modes

Delayed execution

Pass a number to schedule a task to run after a delay in seconds:

// Run in 10 seconds
await this.schedule(10, "processTask", { taskId: "123" });

// Run in 5 minutes (300 seconds)
await this.schedule(300, "sendFollowUp", { email: "user@example.com" });

// Run in 1 hour
await this.schedule(3600, "checkStatus", { orderId: "abc" });
// Run in 10 seconds
await this.schedule(10, "processTask", { taskId: "123" });

// Run in 5 minutes (300 seconds)
await this.schedule(300, "sendFollowUp", { email: "user@example.com" });

// Run in 1 hour
await this.schedule(3600, "checkStatus", { orderId: "abc" });

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:

// Run tomorrow at noon
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setHours(12, 0, 0, 0);
await this.schedule(tomorrow, "sendReminder", { message: "Meeting time!" });

// Run at a specific timestamp
await this.schedule(new Date("2025-06-15T14:30:00Z"), "triggerEvent", {
    eventId: "conference-2025",
});

// Run in 2 hours using Date math
const twoHoursFromNow = new Date(Date.now() + 2 * 60 * 60 * 1000);
await this.schedule(twoHoursFromNow, "checkIn", {});
// Run tomorrow at noon
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setHours(12, 0, 0, 0);
await this.schedule(tomorrow, "sendReminder", { message: "Meeting time!" });

// Run at a specific timestamp
await this.schedule(new Date("2025-06-15T14:30:00Z"), "triggerEvent", {
    eventId: "conference-2025",
});

// Run in 2 hours using Date math
const twoHoursFromNow = new Date(Date.now() + 2 * 60 * 60 * 1000);
await this.schedule(twoHoursFromNow, "checkIn", {});

Use cases:

  • Appointment reminders
  • Deadline notifications
  • Scheduled content publishing
  • Time-based triggers

Recurring (cron)

Pass a cron expression string for recurring schedules:

// Every day at 8:00 AM
await this.schedule("0 8 * * *", "dailyReport", {});

// Every hour
await this.schedule("0 * * * *", "hourlyCheck", {});

// Every Monday at 9:00 AM
await this.schedule("0 9 * * 1", "weeklySync", {});

// Every 15 minutes
await this.schedule("*/15 * * * *", "pollForUpdates", {});

// First day of every month at midnight
await this.schedule("0 0 1 * *", "monthlyCleanup", {});
// Every day at 8:00 AM
await this.schedule("0 8 * * *", "dailyReport", {});

// Every hour
await this.schedule("0 * * * *", "hourlyCheck", {});

// Every Monday at 9:00 AM
await this.schedule("0 9 * * 1", "weeklySync", {});

// Every 15 minutes
await this.schedule("*/15 * * * *", "pollForUpdates", {});

// First day of every month at midnight
await this.schedule("0 0 1 * *", "monthlyCleanup", {});

Cron syntax: minute hour day month weekday

Field Values Special characters
Minute 0-59 * , - /
Hour 0-23 * , - /
Day of Month 1-31 * , - /
Month 1-12 * , - /
Day of Week 0-6 (0=Sunday) * , - /

Common patterns:

"* * * * *"; // Every minute
"*/5 * * * *"; // Every 5 minutes
"0 * * * *"; // Every hour (on the hour)
"0 0 * * *"; // Every day at midnight
"0 8 * * 1-5"; // Weekdays at 8am
"0 0 * * 0"; // Every Sunday at midnight
"0 0 1 * *"; // First of every month
"* * * * *"; // Every minute
"*/5 * * * *"; // Every 5 minutes
"0 * * * *"; // Every hour (on the hour)
"0 0 * * *"; // Every day at midnight
"0 8 * * 1-5"; // Weekdays at 8am
"0 0 * * 0"; // Every Sunday at midnight
"0 0 1 * *"; // First of every month

Use cases:

  • Daily/weekly reports
  • Periodic cleanup jobs
  • Polling external services
  • Health checks
  • Subscription renewals

Cron schedules are idempotent by default — calling schedule() with the same cron expression, callback, and payload multiple times returns the existing schedule instead of creating a duplicate. This makes cron schedules safe to set up in onStart().

Interval

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

// Poll every 30 seconds
await this.scheduleEvery(30, "poll", { source: "api" });

// Health check every 45 seconds
await this.scheduleEvery(45, "healthCheck", {});

// Sync every 90 seconds (1.5 minutes - cannot be expressed in cron)
await this.scheduleEvery(90, "syncData", { destination: "warehouse" });
// Poll every 30 seconds
await this.scheduleEvery(30, "poll", { source: "api" });

// Health check every 45 seconds
await this.scheduleEvery(45, "healthCheck", {});

// Sync every 90 seconds (1.5 minutes - cannot be expressed in cron)
await this.scheduleEvery(90, "syncData", { destination: "warehouse" });

Key differences from cron:

Feature Cron Interval
Minimum granularity 1 minute 1 second
Arbitrary intervals No (must fit cron pattern) Yes
Fixed schedule Yes (for example, "every day at 8am") No (relative to start)
Overlap prevention No Yes (built-in)

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:

class MyAgent extends Agent {
    async onStart() {
        // Safe to call on every wake — only one schedule is created
        await this.scheduleEvery(30, "poll", { source: "api" });
    }
}
class MyAgent extends Agent {
    async onStart() {
        // Safe to call on every wake — only one schedule is created
        await this.scheduleEvery(30, "poll", { source: "api" });
    }
}

A different interval or payload creates a new, independent schedule.

Overlap prevention:

If a callback takes longer than the interval, the next execution is skipped (not queued). This prevents runaway resource usage:

class PollingAgent extends Agent {
    async poll() {
        // If this takes 45 seconds and interval is 30 seconds,
        // the next poll is skipped (with a warning logged)
        const data = await slowExternalApi();
        await this.processData(data);
    }
}

// Set up 30-second interval
await this.scheduleEvery(30, "poll", {});
class PollingAgent extends Agent {
    async poll() {
        // If this takes 45 seconds and interval is 30 seconds,
        // the next poll is skipped (with a warning logged)
        const data = await slowExternalApi();
        await this.processData(data);
    }
}

// Set up 30-second interval
await this.scheduleEvery(30, "poll", {});

When a skip occurs, you will see a warning in logs:

Skipping interval schedule abc123: previous execution still running

Error resilience:

If the callback throws an error, the interval continues — only that execution fails:

class SyncAgent extends Agent {
    async syncData() {
        // Even if this throws, the interval keeps running
        const response = await fetch("https://api.example.com/data");
        if (!response.ok) throw new Error("Sync failed");
        // ...
    }
}
class SyncAgent extends Agent {
    async syncData() {
        // Even if this throws, the interval keeps running
        const response = await fetch("https://api.example.com/data");
        if (!response.ok) throw new Error("Sync failed");
        // ...
    }
}

Use cases:

  • Sub-minute polling (every 10, 30, 45 seconds)
  • Intervals that do not map to cron (every 90 seconds, every 7 minutes)
  • Rate-limited API polling with precise control
  • Real-time data synchronization

Managing scheduled tasks

Get a schedule

Retrieve a scheduled task by its ID:

const schedule = await this.getScheduleById(scheduleId);

if (schedule) {
    console.log(
        `Task ${schedule.id} will run at ${new Date(schedule.time * 1000)}`,
    );
    console.log(`Callback: ${schedule.callback}`);
    console.log(`Type: ${schedule.type}`); // "scheduled" | "delayed" | "cron" | "interval"
} else {
    console.log("Schedule not found");
}
const schedule = await this.getScheduleById(scheduleId);

if (schedule) {
    console.log(
        `Task ${schedule.id} will run at ${new Date(schedule.time * 1000)}`,
    );
    console.log(`Callback: ${schedule.callback}`);
    console.log(`Type: ${schedule.type}`); // "scheduled" | "delayed" | "cron" | "interval"
} else {
    console.log("Schedule not found");
}

List schedules

Query scheduled tasks with optional filters:

// Get all scheduled tasks
const allSchedules = await this.listSchedules();

// Get only cron jobs
const cronJobs = await this.listSchedules({ type: "cron" });

// Get tasks in the next hour
const upcoming = await this.listSchedules({
    timeRange: {
        start: new Date(),
        end: new Date(Date.now() + 60 * 60 * 1000),
    },
});

// Get a specific task by ID
const specific = await this.listSchedules({ id: "abc123" });

// Combine filters
const upcomingCronJobs = await this.listSchedules({
    type: "cron",
    timeRange: {
        start: new Date(),
        end: new Date(Date.now() + 24 * 60 * 60 * 1000),
    },
});

```ts // Get all scheduled tasks const allSchedules = await this.listSchedules();

// Get only cron jobs const cronJobs = await this.listSchedules({ type: "cron" });