> ## Documentation Index
> Fetch the complete documentation index at: https://developers.zite.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Schedules

> Run workflows on a recurring cadence or once at a future time, inline on createEndpoint, or at runtime with ZiteSchedules.

Any [workflow](/framework/workflows) can run on a schedule instead of (or in addition to) being called
from an app. Scheduled runs execute in the background with **no user**: `context.user` is `null`. The
time the run was due arrives on the input instead, as `input.__cron.scheduledAt`, whenever the workflow's
inputs are a plain object. It is not part of the generated input type, so read it with a cast.

## Inline schedule

Add a `schedule` to `createEndpoint` to run it on a fixed cadence:

```typescript theme={"system"}
// apps/helpdesk/src/api/dailyDigest.ts
import { z } from 'zod';
import { createEndpoint } from 'zitejs/backend';

export default createEndpoint({
  description: 'Send the daily digest',
  inputSchema: z.object({}),
  outputSchema: z.object({ sent: z.number() }),
  schedule: {
    scheduleType: 'recurring',
    schedule: { frequency: 'daily', interval: 1, times: ['09:00'] },
    timezone: 'America/New_York',
  },
  execute: async () => {
    // …build and send the digest
    return { sent: 1 };
  },
});
```

### The schedule shape

A schedule is either `recurring` or `oneTime`:

```typescript theme={"system"}
type ZiteSchedule =
  | {
      scheduleType: 'recurring';
      schedule:
        | { frequency: 'minutely'; interval: number; activeWindow?: ActiveWindow }
        | { frequency: 'hourly';   interval: number; minute?: number; activeWindow?: ActiveWindow }
        | { frequency: 'daily';    interval: number; times: string[] }            // "HH:MM"
        | { frequency: 'weekly';   interval: number; daysOfWeek: DayOfWeek[]; times: string[] }
        | { frequency: 'monthly';  interval: 1; monthlyDay: MonthlyDay; times: string[] };
      endPolicy?: { type: 'never' } | { type: 'onDate'; endAt: string } | { type: 'afterOccurrences'; occurrences: number };
      overlapPolicy?: 'skip' | 'allow';   // default 'skip'
      timezone: string;                    // IANA, e.g. "America/New_York"
      paused?: boolean;
    }
  | { scheduleType: 'oneTime'; fireAt: string; timezone: string; paused?: boolean };
```

Each `frequency` takes a different set of fields:

| `frequency` | Required | Optional |
| - | - | - |
| `minutely` | `interval` | `activeWindow` |
| `hourly` | `interval` | `minute`, `activeWindow` |
| `daily` | `interval`, `times` | — |
| `weekly` | `interval`, `daysOfWeek`, `times` | — |
| `monthly` | `interval` (must be `1`), `monthlyDay`, `times` | — |

<Note>
  `monthly` is the one frequency that pins `interval` to `1`. Every other frequency accepts any number, so
  `{ frequency: 'hourly', interval: 6 }` runs every six hours.
</Note>

And the shared fields:

| Field | Type | Notes |
| - | - | - |
| `times` | `string[]` | `"HH:MM"` strings, in the schedule's `timezone` |
| `timezone` | `string` | Required. IANA name, e.g. `"America/New_York"` |
| `activeWindow` | `ActiveWindow` | Minutely and hourly only. Restricts firing to certain hours/days |
| `monthlyDay` | `MonthlyDay` | Monthly only. Which day to fire on, see below |
| `endPolicy` | `never` · `onDate` · `afterOccurrences` | When the recurrence stops. Omit to run indefinitely |
| `overlapPolicy` | `'skip'` · `'allow'` | Defaults to `'skip'`, which prevents a new run from starting while the previous one is still going |
| `paused` | `boolean` | Suspends the schedule without deleting it |

`monthlyDay` picks the day either by number or by weekday occurrence:

```typescript theme={"system"}
type MonthlyDay =
  | { type: 'dayOfMonth'; day: number | 'last' }              // 1–31, or the last day
  | { type: 'weekdayOccurrence';                              // e.g. the 2nd Tuesday
      weekday: DayOfWeek;
      occurrence: 1 | 2 | 3 | 4 | 'last' };
```

## Managing schedules at runtime

Import `ZiteSchedules` from `zitejs/schedules` to add or change schedules from inside your logic (for
example, scheduling a reminder when a record is created):

```typescript theme={"system"}
import { ZiteSchedules } from 'zitejs/schedules';

// Schedule a one-time reminder
const { id } = await ZiteSchedules.add({
  endpointId: 'sendReminder',
  schedule: { scheduleType: 'oneTime', fireAt: '2026-08-01T09:00:00Z', timezone: 'UTC' },
  inputs: { ticketId: 'rec_123' },
});

await ZiteSchedules.list({ endpointId: 'sendReminder' }); // { schedules: [...] }
await ZiteSchedules.update({ id, schedule: /* … */ });
await ZiteSchedules.remove({ id });
```

| Method | Arguments | Returns |
| - | - | - |
| `add` | `{ endpointId, schedule, inputs? }` | `{ id, cronJobId }` |
| `list` | `{ endpointId? }` | `{ schedules }` |
| `update` | `{ id, schedule?, inputs? }` | `{ id, updated }` |
| `remove` | `{ id }` | `{ id, deleted }` |

<Note>
  Schedule `inputs` are limited to **32 KB**. Runtime schedule changes require the app to have been
  published at least once.
</Note>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.