Schedule Durable Work for AI Agents
Store every delayed task in durable scheduler state outside the agent process, attach an immutable execution identity, and claim that identity atomically before side effects. For recurrence, choose catch-up, skip, or coalesce explicitly. Use cron for fixed calendar times and a durable workflow when the scheduled job has multiple steps or waits.
Prerequisites
Before adding a timer, define four things: the durable scheduler that owns the due time, the handler that performs the work, an immutable identity for each intended execution, and a durable record of identities already claimed. Treat that record as part of the state an agent persists between runs, not as conversation history or an in-memory cache.
If the work has several dependent steps, waits for an external event, or must resume after a partial failure, also provide a durable workflow runner. A scheduler decides when to start; it does not by itself make every operation inside the task resumable.
Schedule the work
-
Move the due time outside the agent process.
Do not implement delayed work with
setTimeout, a sleeping request, or an in-memory priority queue. A delayed task should persist outside the agent process so a deploy or eviction does not erase the schedule. The process may disappear immediately after accepting the request; the durable schedule remains the record that work is due.Cloudflare’s Agents scheduling documentation, last updated June 3, 2026, describes delayed, date-based, cron, and interval schedules. It says scheduled tasks are stored in SQLite, use Durable Object alarms to wake the agent, and survive agent restarts. That mechanism suits work that must invoke an agent method at a future time. Its cost is platform coupling: the callback name, payload, schedule records, and cancellation path become part of the agent’s runtime design.
Use a durable workflow after the wake-up when the task contains recoverable stages rather than one bounded handler. Cloudflare Workflows documentation, last updated June 2, 2026, describes persisted multi-step execution, automatic retries, and waits lasting from seconds to days. That adds workflow state and retry behavior to operate, so it is the wrong answer for a single small action that can be safely repeated as one unit.
-
Choose the trigger from the timing condition.
Use a one-time delayed schedule when the due time belongs to one task: retry this operation in five minutes, expire this approval at a timestamp, or revisit this run after a cooling-off period. Use a recurring schedule when the calendar or interval itself creates work. If new data creates the work, use an event instead; the distinction is covered in cron schedules versus event-driven agent work.
For an Agents SDK instance,
schedule(delay, callback, payload)or a date-based schedule keeps the trigger beside that agent’s durable state. Cron expressions suit named calendar boundaries. Fixed intervals suit polling relative to the previous start, including sub-minute intervals. The June 2026 Agents documentation states thatscheduleEvery()prevents overlap by skipping an occurrence when the previous callback is still running. That is a skip policy, not catch-up.A platform-level cron trigger is useful when one Worker should initiate shared work rather than one agent instance owning the schedule. Cloudflare’s Cron Triggers documentation, last updated June 20, 2026, says triggers map five-field cron expressions to a Worker’s
scheduled()handler and execute in UTC. Configuration changes can take up to 15 minutes to propagate. The practical cost is that local-time business rules require explicit UTC conversion, while urgent schedule edits cannot be assumed to take effect immediately. -
Put an immutable execution identity in the payload.
Give every intended execution a
taskIdthat does not change across delivery attempts. Do not generate it inside the handler: by then, two deliveries of the same task would receive two identities and both could proceed. The scheduled payload should carry an immutable task identity so the handler can reject duplicate delivery.For a one-time task, create the identity when the request is accepted and store the same value in both the domain record and scheduled payload. For recurrence, derive an execution identity from the stable series identity and the scheduled boundary, such as
daily-summary:team-42:2026-08-26T00:00:00Z. Retries of that boundary keep the same identity; the next boundary gets a different one.Keep mutable instructions out of the identity. If the task should use current settings at execution time, put the stable object identifier in the payload and load those settings in the handler. If it must reproduce the originally approved action, store an immutable input version and include that version in the payload. Mixing these models makes cancellation and audit results ambiguous.
-
Choose what happens after missed executions.
Recurring schedules need a policy for missed executions because catch-up, skip, and coalesce produce different side effects. Define the policy in application code and tests instead of assuming that every scheduler behaves the same way.
-
Catch up when each period represents an obligation that must be processed separately. Create one execution identity per missed boundary and run each outstanding occurrence. This preserves per-period work, but a long outage can release a burst of calls, messages, or writes. Admit that burst through agent backpressure and queues when downstream capacity is bounded.
-
Skip when stale work has no value. Health checks, cache refreshes, and snapshots often fit this condition: execute the current boundary and mark earlier ones skipped. The tradeoff is permanent gaps, so skip is wrong when every period changes an external balance or fulfils a promised action.
-
Coalesce when the latest run can cover the whole gap. Create one execution whose payload records the first missed boundary, last missed boundary, and coalesced range. This limits bursts while retaining the fact that time was missed. It is wrong when combining periods would hide distinct side effects, recipients, or approvals.
Store the chosen policy with the schedule definition. A deployment should not silently change from catch-up to skip because a new handler happens to inspect only the latest timestamp.
-
-
Claim the identity before performing side effects.
At the start of the handler, atomically insert or claim
taskIdin durable storage under a uniqueness constraint. If the claim already exists as completed or in progress, reject the duplicate delivery. Only the winner may call external services, mutate domain state, or start a workflow.Record at least the identity, scheduled boundary, claim time, status, and final outcome. The identity answers “is this the same intended execution?”; the scheduler’s own schedule ID answers “which scheduler record delivered it?” Keep both when available. A scheduler can deduplicate schedule creation and still retry a delivery, while an operator can accidentally create a second schedule for the same domain action. Handler-level identity protects the side effect in both cases.
Mark completion only after the side effect has reached the durable point your application recognizes as done. If the external operation accepts an idempotency key, pass the same immutable task identity. If it does not, a crash between the external effect and your completion write remains an uncertainty that the scheduler alone cannot remove; route that case to reconciliation rather than blindly repeating it.
-
Test the failure boundaries, not only the clock.
Test one-time delivery across a deployment or forced agent restart. Deliver the same payload twice and confirm that only one handler claims it. For recurrence, simulate three missed boundaries and verify catch-up produces three distinct identities, skip produces none for stale boundaries, and coalesce produces one identity describing the range.
Test the configured time basis as well. Cloudflare Cron Triggers use UTC, and the documented local test endpoint accepts both a cron expression and an overridden scheduled time. Include a case around any business-calendar boundary your application translates into UTC. Finally, fail a multi-step task after one durable step and confirm the workflow resumes without repeating a completed side effect.
Expected result
Done means the durable scheduler retains every due time independently of the agent process, each recurring schedule has an explicit missed-execution policy, and every delivery carries an immutable task identity that the handler claims before side effects. Deploys and evictions do not erase pending work, while retries and duplicate delivery do not create a second accepted execution.
Sources
- Cloudflare’s Agents scheduling documentationdevelopers.cloudflare.com
- Cloudflare Workflows documentationdevelopers.cloudflare.com
- Cloudflare’s Cron Triggers documentationdevelopers.cloudflare.com
See also
Billing shape, duration ceilings, warm state, cold starts and concurrency limits compared for agent runtimes — with a per-condition recommendation.
Separate streamed text from committed agent state, route tool events independently, and make disconnects end in an explicit error or resumable turn.
Choose by consumer: schemas for program-read results, free text for people, and a hybrid when both need the same agent output.
Use one agent for context-heavy work; delegate checkable search and summaries when exploration is large, then cap parallelism to your rate budget.