Skip to content

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 for better observability of asynchronous operations.

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

Declarative schedules are defined in the app or block schema and automatically managed by the Flows platform.

Defining schedules»

To add scheduled operations to an app or a block, include a schedules object in your block definition:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
{
  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»

Schedules can be marked as customizable, which allows them to be configured by users:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
{
  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)»

In addition to declarative schedules, Flows provides an imperative API for setting one-time timers that trigger after a specified delay.

Setting timers»

To create a timer, use the timers.app.set and timers.block.set functions:

1
2
3
4
5
6
7
8
9
import { timers } from "@slflows/sdk/v1";

// Set a timer to trigger after 30 seconds
const 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: (block timers only) A pending event ID to associate with the timer
    • promptKey: (app timers only) A prompt key 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»

To cancel a timer before it triggers, use the timers.app.unset and timers.block.unset functions:

1
2
3
4
import { timers } from "@slflows/sdk/v1";

// Cancel a previously set timer
await timers.block.unset(timerId);

Handling timer events»

To handle app or block timer events, implement the onTimer handler on the appropriate level:

1
2
3
4
5
6
{
  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)