# Scheduling

The scheduling service in Flows provides ways to execute code at specified times, either on a recurring schedule or after a specific delay. This enables blocks to perform time-based operations without requiring external triggers or manual intervention.

Timers can be linked with [pending events](./events#pending-events) for better observability of asynchronous operations.

## Overview
[Section titled “Overview”](#overview)
Scheduling in Flows comes in two forms:

 1. **Declarative scheduling** - Predefined cron or frequency-based schedules specified in app or block definitions that run on a regular basis
 1. **Imperative scheduling (timers)** - Dynamically created one-time timers that trigger after a specified delay

These capabilities enable important patterns such as:

 - Polling external APIs for new data
 - Performing regular maintenance tasks like refreshing auth tokens
 - Implementing delays and timeouts
 - Retrying operations after failures
 - Checking the status of long-running processes

## Declarative scheduling
[Section titled “Declarative scheduling”](#declarative-scheduling)
Declarative schedules are defined in the app or block schema and automatically managed by the Flows platform.

### Defining schedules
[Section titled “Defining schedules”](#defining-schedules)
To add scheduled operations to an app or a block, include a `schedules` object in your block definition:


**

```
{  schedules: {    generateDailyReport: {      description: "Generates the daily summary report",      // Run at 8:00 AM every weekday      definition: {        type: "cron",        cron: {          expression: "0 8 * * 1-5",          location: "America/New_York",        },      },      onTrigger: async (input) => {        // Your report generation logic here...      },    },  },},
```

The `onTrigger` handler

When a schedule fires, the corresponding `onTrigger` handler is invoked.

### Customizable schedules
[Section titled “Customizable schedules”](#customizable-schedules)
Schedules can be marked as customizable, which allows them to be configured by users:


**

```
{  schedules: {    dataSync: {      description: "Synchronizes data from the external API",      customizable: true, // Users can customize this schedule      definition: {        type: "frequency",        frequency: {          interval: 1,          unit: "hours" // Default: every hour        }      },      onTrigger: async (input) => {        // Your data synchronization logic here...      },    },  },}
```

When a schedule is marked as customizable:

 - The default schedule serves as the initial value
 - Users can modify the schedule through the block configuration UI
 - The schedule becomes part of the block’s configuration

## Imperative scheduling (timers)
[Section titled “Imperative scheduling (timers)”](#imperative-scheduling-timers)
In addition to declarative schedules, Flows provides an imperative API for setting one-time timers that trigger after a specified delay.

### Setting timers
[Section titled “Setting timers”](#setting-timers)
To create a timer, use the `timers.app.set` and `timers.block.set` functions:


**

```
import { timers } from "@slflows/sdk/v1";
// Set a timer to trigger after 30 secondsconst timerId = await timers.app.set(30, {  inputPayload: { message: "Don't forget to check the status!" },  description: "Reminder to check operation status",});
console.log(`Timer set with ID: ${timerId}`);
```

The timer set functions take these arguments:

 - `delaySeconds`: How long to wait before triggering (in seconds)
 - `options`: Optional configuration object with:
 - `inputPayload`: Data to pass to the handler when the timer triggers
 - `description`: A human-readable description for the UI
 - `pendingEventId`: Optional pending event ID to associate with the timer
 - `promptId`: Optional prompt ID to associate with the timer

The function returns a unique ID for the timer, which can be used to unset it if needed.

### Unsetting timers
[Section titled “Unsetting timers”](#unsetting-timers)
To cancel a timer before it triggers, use the `timers.app.unset` and `timers.block.unset` functions:


**

```
import { timers } from "@slflows/sdk/v1";
// Cancel a previously set timerawait timers.block.unset(timerId);
```

### Handling timer events
[Section titled “Handling timer events”](#handling-timer-events)
To handle app or block timer events, implement the `onTimer` handler on the appropriate level:


**

```
{  onTimer: async (input) => {    const { timer } = input;    console.log("Timer triggered with payload:", timer.payload);  },}
```

In addition to `app` and/or `block` contexts, the `onTimer` handler receives a `timer` containing information about the timer that fired, including:

 - `timer.payload`: The payload data provided when the timer was set
 - `timer.pendingEvent`: Optional information about an associated pending event, containing:
 - `id`: The pending event ID
 - `body`: The current event body/payload (if any)
 - `status`: Current status description (if any)