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

# Schedules

> Start a workflow at fixed times, like every hour or every Monday morning, with a cron schedule.

Some work repeats at fixed times: a report every Monday, a cleanup every night, a sync every hour. Give the workflow a schedule, and Nelkap starts a run each time it comes due. Nothing needs to run in your app in between.

## Add a schedule

Set `cron` when you register the workflow:

```ts theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
nelkap.registerWorkflows([
  {
    name: 'sendWeeklyReport',
    handler: sendWeeklyReport,
    cron: '0 9 * * 1',
  },
]);
```

This workflow starts a run every Monday at 9:00 UTC.

## Cron syntax

A schedule has five fields, separated by spaces:

| Field | Values |
| - | - |
| Minute | 0 to 59 |
| Hour | 0 to 23 |
| Day of the month | 1 to 31 |
| Month | 1 to 12 |
| Day of the week | 0 to 7, where 0 and 7 are Sunday |

In each field, `*` means every value, `1-5` a range, `1,15` a list, and `*/15` every fifteenth value. Months and days of the week can also be written as names, like `JAN` or `MON-FRI`. Times are in UTC.

| Schedule | Starts a run |
| - | - |
| `*/15 * * * *` | Every 15 minutes. |
| `0 * * * *` | Every hour, on the hour. |
| `0 2 * * *` | Every day at 2:00 UTC. |
| `0 9 * * 1-5` | Every weekday at 9:00 UTC. |
| `0 0 1 * *` | On the first day of each month, at midnight UTC. |

## Payload

A scheduled run has no payload, so the workflow takes no argument. When the work depends on the time, read it in a step, so every call of the run gets the same value:

```ts theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
export async function sendWeeklyReport() {
  const weekStart = await nelkap.step.run(
    'week start',
    () => startOfLastWeek(new Date()).toISOString(),
  );

  const report = await nelkap.step.run('report', () =>
    buildReport(weekStart),
  );

  await nelkap.step.run('email', () =>
    emailReport(report.id),
  );
}
```

## Timing

* After you add a schedule, the first run starts at the next time it matches, never right away.
* Nelkap checks schedules every minute, so a run starts within a minute of its time.
* If runs couldn't start for a while, the times missed meanwhile give a single run, not one per missed time.
* A changed schedule applies as soon as your app registers it. The next run follows the new schedule. On a serverless host, your app registers its list on its first request after a deploy. See [Workflow registration](https://docs.nelkap.com/deploy/overview.md#workflow-registration).
* Removing the schedule, or the workflow, stops the runs.

Scheduled runs go through the same [concurrency limits](https://docs.nelkap.com/guides/concurrency.md) as any other. If the previous run is still going and must finish first, give the workflow `concurrency: 1`.

## Test a schedule

To test a scheduled workflow without waiting for its time, start a run from your code with [`run`](https://docs.nelkap.com/triggers/run). It runs the same workflow, with no payload.

For a complete scheduled workflow, see the [weekly report example](https://docs.nelkap.com/examples/weekly-report.md).


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