---
title: Scheduled tasks
description: Declare recurring, timezone-aware Think turns and deterministic handlers with getScheduledTasks() and a typed scheduling DSL.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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.

[Skip to content](#%5Ftop) 

# Scheduled tasks

Use `getScheduledTasks()` when code should create recurring Think turns or deterministic scheduled handlers. Think reconciles the declarations on startup, stores a durable one-shot schedule for the next occurrence, and re-arms the next occurrence after each run.

* [  JavaScript ](#tab-panel-4982)
* [  TypeScript ](#tab-panel-4983)

JavaScript

```

import { Think, defineScheduledTasks } from "@cloudflare/think";


export class DigestAgent extends Think {

  getDefaultTimezone() {

    return "Europe/London";

  }


  getScheduledTasks() {

    return defineScheduledTasks({

      weeklyCommitReport: {

        schedule: "every week on monday at 09:00",

        prompt:

          "Compile all my GitHub commits for the last week and send a concise summary.",

      },

      workout: {

        schedule: "every day at 08:00 in Europe/London",

        prompt: "Start my workout.",

      },

      customerDigest: {

        schedule: "every day at 09:00",

        timezone: "America/New_York",

        metadata: { workflowName: "customer-digest" },

        retry: { maxAttempts: 3 },

        handler: async ({

          idempotencyKey,

          scheduledFor,

          scheduleKind,

          timezone,

        }) => {

          await this.env.DIGEST_WORKFLOW.create({

            id: idempotencyKey,

            params: { scheduledFor, scheduleKind, timezone },

          });

        },

      },

    });

  }

}


```

TypeScript

```

import { Think, defineScheduledTasks } from "@cloudflare/think";


export class DigestAgent extends Think<Env> {

  getDefaultTimezone() {

    return "Europe/London";

  }


  getScheduledTasks() {

    return defineScheduledTasks({

      weeklyCommitReport: {

        schedule: "every week on monday at 09:00",

        prompt:

          "Compile all my GitHub commits for the last week and send a concise summary.",

      },

      workout: {

        schedule: "every day at 08:00 in Europe/London",

        prompt: "Start my workout.",

      },

      customerDigest: {

        schedule: "every day at 09:00",

        timezone: "America/New_York",

        metadata: { workflowName: "customer-digest" },

        retry: { maxAttempts: 3 },

        handler: async ({

          idempotencyKey,

          scheduledFor,

          scheduleKind,

          timezone,

        }) => {

          await this.env.DIGEST_WORKFLOW.create({

            id: idempotencyKey,

            params: { scheduledFor, scheduleKind, timezone },

          });

        },

      },

    });

  }

}


```

The DSL supports `every <n> minutes`, `every <n> hours`, `every day at HH:mm`, `every weekday at HH:mm`, and `every week on monday,wednesday at HH:mm`. Wall-clock schedules require either an inline timezone, a task `timezone`, or `getDefaultTimezone()`. If an alarm is late, Think runs the intended occurrence once and schedules the next future occurrence; it does not backfill missed runs.

Each task must define exactly one of `prompt` or `handler`. Prompt tasks create a durable submission with [submitMessages()](https://developers.cloudflare.com/agents/harnesses/think/programmatic-submissions/). Handler tasks receive `{ taskId, scheduledFor, scheduledForDate, occurrenceKey, idempotencyKey, schedule, scheduleKind, timezone, metadata }` and are intended for app-owned work such as creating a Workflow run or writing a run ledger. Delivery is at-least-once; use `idempotencyKey` or `occurrenceKey` for your own durable idempotency.

Static declarations reconcile on startup. If `getScheduledTasks()` reads product-owned data that can change while the Durable Object is live, call `internal_reconcileScheduledTasks()` after updating that data. During reconciliation Think records the task row before creating the underlying Agent schedule, so a missing `schedule_id` is only a pending reconcile state and is repaired on the next reconcile. The task `retry` option retries the prompt or handler action before the failure is logged. The next occurrence is still scheduled after the action succeeds or exhausts its retries, so failed occurrences do not block future runs.

## When to use a workflow instead

For a recurring job whose steps matter — multiple deterministic steps, long waits, or human approval — use a handler task to create a [Think Workflow](https://developers.cloudflare.com/agents/harnesses/think/workflows/) run. Keep simple recurring prompts as prompt tasks, and keep one-off background turns on [submitMessages()](https://developers.cloudflare.com/agents/harnesses/think/programmatic-submissions/).

```json
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/agents/","name":"Agents"}},{"@type":"ListItem","position":3,"item":{"@id":"/agents/harnesses/","name":"Harnesses"}},{"@type":"ListItem","position":4,"item":{"@id":"/agents/harnesses/think/","name":"Think"}},{"@type":"ListItem","position":5,"item":{"@id":"/agents/harnesses/think/scheduled-tasks/","name":"Scheduled tasks"}}]}
```
