Schedule recurring work
Use a scheduled Task when an Agent should run from a clock instead of an interactive request. This guide selects a schedule shape, stores it, and observes the first created run.
Outcome
You will create an interval Task, verify its persisted schedule, and observe a run created by a scheduled fire.
Before you begin
You need an enabled Agent, its agentId, a
backend SDK client, and the Task instruction. Review
Tasks and schedules and decide whether
the Task should follow the latest Agent Version or a
Version Pin.
Choose a schedule type
| Kind | Required configuration | Use when |
|---|---|---|
once | at: ISO 8601 timestamp with an offset | Work should be attempted once at a known instant. |
interval | everyMs: integer of at least 60,000 | Work should repeat on creation-anchored elapsed-time boundaries. |
cron | five-field numeric expression; optional IANA timezone and nonnegative staggerMs | Work should follow calendar boundaries in a named timezone. |
Create a scheduled Task
Pass a schedule with the Task definition. The Task response exposes the stored schedule, not a next-fire timestamp.
const expectedSchedule = {
kind: "interval" as const,
config: { everyMs: 60_000 },
};
const { task } = await client.tasks.create({
agentId,
name: "Refresh operations summary",
prompt: "Refresh the operations summary.",
schedule: expectedSchedule,
});
const stored = await client.tasks.get(task.id);
if (JSON.stringify(stored.schedule) !== JSON.stringify(expectedSchedule)) {
throw new Error("The persisted schedule does not match");
}
let runs = await client.tasks.listRuns(task.id);
for (let attempt = 0; attempt < 70 && runs.data.length === 0; attempt += 1) {
await new Promise((resolve) => setTimeout(resolve, 1_000));
runs = await client.tasks.listRuns(task.id);
}
if (!runs.data[0]) throw new Error("No scheduled run was observed");
console.log(runs.data[0].status);Configure one-time, interval, and cron schedules
One-time timestamps must include an offset:
const schedule = {
kind: "once" as const,
config: { at: "2026-08-15T09:00:00+01:00" },
};Intervals use milliseconds and cannot be shorter than 60 seconds:
const schedule = {
kind: "interval" as const,
config: { everyMs: 15 * 60_000 },
};Cron uses five numeric fields. The timezone defaults to UTC when
omitted; otherwise supply a canonical IANA name. staggerMs is an
optional maximum deterministic delay for spreading Task starts.
const schedule = {
kind: "cron" as const,
config: {
expression: "0 9 * * 1-5",
timezone: "Europe/London",
staggerMs: 30_000,
},
};Verify the next run
The final lines of the primary example compare the exact persisted
schedule and bound how long they wait for a run. A new run begins as
queued; it may already be running or terminal by the time the list
request returns. listRuns is the supported way to observe the run after
a fire is accepted.
Change or pause a schedule
Call tasks.update(taskId, { schedule }) to replace the schedule, or pass
schedule: null to make the Task on-demand. Pass enabled: false to remove
the active schedule while retaining the definition; enabled: true schedules
its current configuration again. Schedule and enabled mutations use a new
generation so stale fires cannot create runs.
Disabling the Agent does not mutate its Tasks. Scheduled fires are skipped without Task-run rows while the Agent is disabled, and the next eligible recurring fire can run after the Agent is enabled. A skipped one-time fire is not replayed merely because the Agent is enabled later.
Production notes
- Cron fires use their configured timezone. The optional stagger is stable for one Task and clamped inside the current cron window.
- At most one run per Task is active. An overlapping scheduled fire is skipped; interval scheduling still advances to its next creation-anchored boundary.
- Cron scheduling disables automatic backfill. Intervals select the next strictly future creation-anchored boundary, so missed interval boundaries do not produce a burst of catch-up runs.
- An overdue one-time schedule is started immediately when synchronization or reconciliation recovers it. Do not treat this as a general catch-up guarantee.
- Product state is fenced by a schedule generation and reconciled with durable scheduler state. This supports recovery without promising exactly-once execution. See Limits and reliability.
Related capabilities
SDK and REST reference
See tasks.create,
tasks.get,
tasks.update, and
tasks.listRuns. The matching
REST operations are create Task,
get Task,
update Task, and
list Task runs.