> Discover all available pages from the documentation index: https://mastra.ai/llms.txt

# Schedules

**Added in:** `@mastra/core@1.50.0`

> **Beta:** This feature is in beta. Breaking changes may occur without a major version bump until the API is stable.

A schedule runs an agent on a cron cadence. On each fire, Mastra sends a prompt to the agent, either as a [signal](https://mastra.ai/docs/long-running-agents/signals) into a thread or as a threadless [`agent.generate()`](https://mastra.ai/reference/agents/generate) run. Use schedules for recurring agent work such as daily summaries, periodic checks, or scheduled nudges into a conversation.

Schedules are persisted, so they survive restarts and redeploys. Manage them at runtime through [`mastra.schedules`](https://mastra.ai/reference/schedules/overview), the canonical create, read, update, and delete (CRUD) surface. The same surface also manages [workflow schedules](https://mastra.ai/docs/workflows/scheduled-workflows) (pass `workflowId` instead of `agentId` to schedule a workflow).

> **Note:** Schedules require a [storage](https://mastra.ai/docs/memory/storage) adapter that implements the schedules domain. See the [`mastra.schedules` reference](https://mastra.ai/reference/schedules/overview) for supported adapters and API behavior.

## Quickstart

The following schedule runs the `pinger` agent every hour. It has no thread, so each fire is an isolated `agent.generate()` run.

```typescript
import { Mastra } from '@mastra/core'
import { Agent } from '@mastra/core/agent'
import { LibSQLStore } from '@mastra/libsql'

const pinger = new Agent({
  id: 'pinger',
  name: 'Pinger',
  instructions: 'Report the current system status in one sentence.',
  model: 'openai/gpt-5.5',
})

const mastra = new Mastra({
  agents: { pinger },
  storage: new LibSQLStore({ url: 'file:./mastra.db' }),
})

await mastra.schedules.create({
  agentId: 'pinger',
  cron: '0 * * * *',
  prompt: 'Give me a status update.',
})
```

Mastra starts the scheduler the first time a schedule is created, then fires the agent on the cron you specify.

## Cadence

Schedules fire on a cron expression. The `cron` field accepts a standard 5-, 6-, or 7-part cron expression, and it's validated when you create or update the schedule.

`croner` nicknames also work, for example `@hourly`, `@daily`, `@weekly`, `@monthly`, and `@midnight`. For day-and-time combinations, write the cron field directly:

```typescript
// Every weekday at 9am
await mastra.schedules.create({
  agentId: 'pinger',
  cron: '0 9 * * 1-5',
  prompt: 'Start-of-day check.',
})
```

Set `timezone` to an IANA timezone, for example `America/New_York`, so fire times don't depend on the host's locale. When omitted, the cron resolves against the host's local timezone.

For more readable cron construction, you can use a userland builder such as [`cron-time-generator`](https://www.npmjs.com/package/cron-time-generator) and pass its output to `cron`.

## Threadless and threaded schedules

An agent schedule fires in one of two modes, decided by whether you pass a `threadId`.

### Threadless

Without a `threadId`, each fire is an isolated `agent.generate()` run. Nothing is written to a conversation thread. This is the simplest mode and suits status checks, reports, and other work that doesn't need conversation context.

### Threaded

With a `threadId`, the schedule sends a [signal](https://mastra.ai/docs/long-running-agents/signals) into that thread, so the prompt joins the agent's conversation. Threaded schedules require a `resourceId` alongside the `threadId`.

```typescript
await mastra.schedules.create({
  agentId: 'pinger',
  cron: '0 9 * * *',
  prompt: 'Summarize anything new since yesterday.',
  threadId: 'thread-123',
  resourceId: 'user-456',
})
```

Threaded schedules accept extra fields that control how the signal behaves, including the signal type, XML tag, tag attributes, and active-or-idle delivery behavior. They mirror the options [`agent.sendSignal()`](https://mastra.ai/docs/long-running-agents/signals) accepts and stay JSON-serializable so they persist with the schedule.

These fields require a `threadId`. For the full threaded input shape, see the [agent schedule input reference](https://mastra.ai/reference/schedules/overview).

```typescript
await mastra.schedules.create({
  agentId: 'pinger',
  cron: '0 9 * * *',
  prompt: 'Summarize anything new since yesterday.',
  threadId: 'thread-123',
  resourceId: 'user-456',
  tagName: 'check-in', // renders as <check-in>…</check-in>
  attributes: { source: 'cron' },
  ifActive: { behavior: 'discard' }, // skip if the thread is mid-stream
  ifIdle: {
    behavior: 'wake', // wake the agent if the thread is idle
    streamOptions: { requestContext: { locale: 'en-US' } },
  },
})
```

`providerOptions` are merged into the signal payload on every fire and apply to both threaded and threadless schedules.

## Managing schedules

Use `mastra.schedules` for all schedule operations. The service can create, read, update, pause, resume, manually run, and delete schedules.

```typescript
const schedule = await mastra.schedules.create({
  agentId: 'pinger',
  cron: '0 * * * *',
  prompt: 'Status check.',
})

await mastra.schedules.pause(schedule.id)
await mastra.schedules.resume(schedule.id)
await mastra.schedules.run(schedule.id) // Fire once now, off-schedule
```

`pause` and `resume` are durable. `run` fires the schedule once immediately without affecting its cadence. For the complete method list, filters, and patch fields, see the [`mastra.schedules` reference](https://mastra.ai/reference/schedules/overview).

### Workflow schedules

The same service creates schedules that run a workflow instead of an agent. Pass `workflowId` and the workflow-shaped fields:

```typescript
await mastra.schedules.create({
  workflowId: 'daily-report',
  cron: '0 9 * * *',
  inputData: { userId: 'system' },
})
```

Workflow schedules created this way are independent of the declarative `schedule` field on `createWorkflow` — see [scheduled workflows](https://mastra.ai/docs/workflows/scheduled-workflows) for the declarative form and Studio views.

### Custom IDs

Pass `id` when you need a predictable handle to look up, update, or delete later.

```typescript
await mastra.schedules.create({
  id: 'nightly-summary',
  agentId: 'pinger',
  cron: '0 9 * * *',
  prompt: 'Summarize anything new since yesterday.',
})
```

See the [`create(input)` reference](https://mastra.ai/reference/schedules/overview) for ID normalization rules and duplicate ID behavior.

### From the client

The same operations are available from `@mastra/client-js` over the `/api/schedules` routes, so you can manage schedules from a separate process or a UI. See the [client-js agent schedules reference](https://mastra.ai/reference/client-js/agents) for the client method list.

## Lifecycle hooks

Hooks let you run code at key points in an agent schedule's lifecycle, for example to compute fire-time parameters or react to the outcome. Configure them on the `Mastra` constructor under `schedules`. The hooks are a single flat bundle that runs for every agent's schedules; each hook context carries the firing `agentId`, so branch on it when you need per-agent behavior. Hooks live at the `Mastra` level so they apply to both code-defined and stored agents.

```typescript
const mastra = new Mastra({
  agents: { pinger },
  storage: new LibSQLStore({ url: 'file:./mastra.db' }),
  schedules: {
    prepare: async ({ agentId, schedule, trigger }) => {
      // Return overrides, null to skip this fire, or undefined for defaults
      return { prompt: `Status as of ${trigger.firedAt.toISOString()}` }
    },
    onFinish: async ({ agentId, outcome, runId }) => {
      // Runs on any non-error, non-abort outcome
    },
    onError: async ({ agentId, phase, error }) => {
      // Runs when prepare, the signal, or the agent run threw
    },
    onAbort: async ({ agentId, runId }) => {
      // Runs when the run was aborted mid-stream
    },
  },
})
```

The hooks are:

- `prepare`: runs before the fire. Return an object to override fire-time parameters such as `prompt` or `threadId`, `null` to skip the fire, or `undefined` to use the stored defaults.
- `onFinish`: runs once per trigger that reached a non-error, non-abort terminal state.
- `onError`: runs when `prepare`, the signal, or the agent run threw.
- `onAbort`: runs when the run was aborted mid-stream.

Every hook context includes `agentId` (the agent the schedule fired for) alongside `schedule` and `trigger`.

Hook exceptions are caught and logged. They never re-route the worker or trigger another hook.

## Related

- [`mastra.schedules`](https://mastra.ai/reference/schedules/overview): API reference for creating and managing schedules.
- [Signals](https://mastra.ai/docs/long-running-agents/signals): the delivery mechanism behind threaded schedules.
- [Scheduled workflows](https://mastra.ai/docs/workflows/scheduled-workflows): declare a cron schedule on a workflow definition.