Developing automations requires a paid plan. Don't have one? Join the Developer Program and provision a sandbox with access to all Slack features for free.
Invoke a workflow at specific time intervals
Scheduled triggers are an automatic type of trigger. This means that once the trigger is created, they do not require any user input.
Use a scheduled trigger if you need a workflow to kick off after a delay or on an hourly, daily, weekly, or annual cadence.
Triggers can be added to workflows in two ways:
You can add triggers with the CLI. These static triggers are created only once. You create them with the Slack CLI, attach them to your app's workflow, and that's that. The trigger is defined within a trigger file.
You can add triggers at runtime. These dynamic triggers are created at any step of a workflow so they can incorporate data acquired from other workflow steps. The trigger is defined within a function file.
Slack CLI built-in documentation
Use slack trigger --help
to easily access information on the trigger
command's flags and subcommands.
The triggers you create when running locally (with the slack run
command) will not work when you deploy your app in production (with the slack deploy
command). You'll need to create
any triggers again with the CLI.
To create a scheduled trigger with the CLI, you'll need to create a trigger file. The trigger file contains the payload you used to define your trigger.
Create a TypeScript trigger file within your app's folder with the following form:
import { Trigger } from "deno-slack-api/types.ts";
import { ExampleWorkflow } from "../workflows/example_workflow.ts";
import { TriggerTypes } from "deno-slack-api/mod.ts";
const trigger: Trigger<typeof ExampleWorkflow.definition> = {
// your TypeScript payload
};
export default trigger;
Your TypeScript payload consists of the parameters needed for your own use case. Below is the trigger file from the Message Translator app:
// triggers/daily_maintenance_job.ts
import { Trigger } from "deno-slack-sdk/types.ts";
import workflowDef from "../workflows/maintenance_job.ts";
import { TriggerTypes } from "deno-slack-api/mod.ts";
/**
* A trigger that periodically starts the "maintenance-job" workflow.
*/
const trigger: Trigger<typeof workflowDef.definition> = {
type: TriggerTypes.Scheduled,
name: "Trigger a scheduled maintenance job",
workflow: `#/workflows/${workflowDef.definition.callback_id}`,
inputs: {},
schedule: {
// Schedule the first execution 60 seconds from when the trigger is created
start_time: new Date(new Date().getTime() + 60000).toISOString(),
end_time: "2037-12-31T23:59:59Z",
frequency: { type: "daily", repeats_every: 1 },
},
};
export default trigger;
trigger create
commandOnce you have created a trigger file, use the following command to create the scheduled trigger:
slack trigger create --trigger-def "path/to/trigger.ts"
If you have not used the slack triggers create
command to create a trigger prior to running the slack run
command, you will receive a prompt in the Slack CLI to do so.
Your app needs to have the triggers:write
scope to use a trigger at runtime. Include the scope within your app's manifest.
The logic of a runtime trigger lies within a function's TypeScript code. Within your functions
folder, you'll have the functions that are the steps making up your workflow. Within this folder is where you can create a trigger within the relevant <function>.ts
file.
When you create a runtime trigger, you can leverage inputs
acquired from functions within the workflow. Provide the workflow definition to get additional typing for the workflow and inputs fields.
Create a scheduled trigger at runtime using the client.workflows.triggers.create
method within the relevant function
file.
const triggerResponse = await client.workflows.triggers.create<typeof ExampleWorkflow.definition>({
// your TypeScript payload
});
Your TypeScript payload consists of the parameters needed for your own use case. Below is the function file with a TypeScript payload for a scheduled trigger from the Daily Channel Topic app:
// functions/create_scheduled_trigger.ts
import { SlackAPI } from "deno-slack-api/mod.ts";
import { DefineFunction, Schema, SlackFunction } from "deno-slack-sdk/mod.ts";
import { TriggerTypes } from "deno-slack-api/mod.ts";
export const CreateScheduledTrigger = DefineFunction({
title: "Create a scheduled trigger",
callback_id: "create_scheduled_trigger",
source_file: "functions/create_scheduled_trigger.ts",
input_parameters: {
properties: {
channel_id: {
description: "The ID of the Channel to create a schedule for",
type: Schema.slack.types.channel_id,
},
},
required: ["channel_id"],
},
output_parameters: {
properties: {
trigger_id: {
description: "The ID of the trigger created by the Slack API",
type: Schema.types.string,
},
},
required: ["trigger_id"],
},
});
export default SlackFunction(
CreateScheduledTrigger,
async ({ inputs, token }) => {
console.log(`Creating scheduled trigger to update daily topic`);
const client = SlackAPI(token, {});
const scheduleDate = new Date();
// Start schedule 1 minute in the future. Start_time must always be in the future.
scheduleDate.setMinutes(scheduleDate.getMinutes() + 1);
// triggers/sample_scheduled_update_topic.txt has a JSON example of the payload
const scheduledTrigger = await client.workflows.triggers.create({
name: `Channel ${inputs.channel_id} Schedule`,
workflow: "#/workflows/scheduled_update_topic",
type: TriggerTypes.Scheduled,
inputs: {
channel_id: { value: inputs.channel_id },
},
schedule: {
start_time: scheduleDate.toUTCString(),
frequency: {
type: "daily",
repeats_every: 1,
},
},
});
if (!scheduledTrigger.trigger) {
return {
error: "Trigger could not be created",
};
}
console.log("scheduledTrigger has been created");
return {
outputs: { trigger_id: scheduledTrigger.trigger.id },
};
},
);
Field | Description | Required? |
---|---|---|
type |
The type of trigger: TriggerTypes.Scheduled |
Yes |
name |
The name of the trigger | Yes |
workflow |
Path to workflow that the trigger initiates | Yes |
schedule |
When and how often the trigger will activate. See The schedule object below |
Yes |
description |
The description of the trigger | No |
inputs |
The inputs provided to the workflow. See the inputs object below |
No |
Scheduled triggers are not interactive. Use a link trigger to take advantage of interactivity.
inputs
object The inputs
of a trigger map to the inputs of a workflow. You can pass any value as an input.
There is also a specific input value that contains information about the trigger. Pass this value to provide trigger information to your workflows!
Field | How to reference | Type | Description |
---|---|---|---|
data.user_id |
TriggerContextData.Scheduled.user_id |
string | A unique identifier for the user who created the trigger. |
event_timestamp |
TriggerContextData.Scheduled.event_timestamp |
timestamp | A Unix timestamp in seconds indicating when this event was dispatched. |
The following snippet shows a user_id
input being set with a value of TriggerContextData.Scheduled.user_id
, representing the user who created the trigger.
...
inputs: {
user_id: {
value: TriggerContextData.Scheduled.user_id
}
},
...
schedule
object Field | Description | Required? |
---|---|---|
start_time |
ISO date string of the first scheduled trigger | Yes |
timezone |
Timezone string to use for scheduling | No |
frequency |
Details on what cadence trigger will activate. See The frequency object below |
No |
occurrence_count |
The maximum number of times trigger will run | No |
end_time |
If set, this trigger will not run past the provided ISO date string | No |
frequency
object Field | Description | Required? |
---|---|---|
type |
How often the trigger will activate: once |
Yes |
repeats_every |
How often the trigger will repeat, respective to frequency.type |
No |
on_week_num |
The nth week of the month the trigger will repeat | No |
import { TriggerTypes } from "deno-slack-api/mod.ts";
import { ScheduledTrigger } from "deno-slack-api/typed-method-types/workflows/triggers/scheduled.ts";
import { ExampleWorkflow } from "../workflows/example_workflow.ts";
const schedule: ScheduledTrigger<typeof ExampleWorkflow.definition> = {
name: "Sample",
type: TriggerTypes.Scheduled,
workflow: `#/workflows/${ExampleWorkflow.definition.callback_id}`,
inputs: {},
schedule: {
// Starts 60 seconds after creation
start_time: new Date(new Date().getTime() + 60000).toISOString(),
timezone: "asia/kolkata",
frequency: {
type: "once",
},
},
};
export default schedule;
Field | Description | Required? |
---|---|---|
type |
How often the trigger will activate: hourly |
Yes |
repeats_every |
How often the trigger will repeat, respective to frequency.type |
Yes |
on_week_num |
The nth week of the month the trigger will repeat | No |
import { TriggerTypes } from "deno-slack-api/mod.ts";
import { ScheduledTrigger } from "deno-slack-api/typed-method-types/workflows/triggers/scheduled.ts";
import { ExampleWorkflow } from "../workflows/example_workflow.ts";
const schedule: ScheduledTrigger<typeof ExampleWorkflow.definition> = {
name: "Sample",
type: TriggerTypes.Scheduled,
workflow: `#/workflows/${ExampleWorkflow.definition.callback_id}`,
inputs: {},
schedule: {
// Starts 60 seconds after creation
start_time: new Date(new Date().getTime() + 60000).toISOString(),
end_time: "2040-05-01T14:00:00Z",
frequency: {
type: "hourly",
repeats_every: 2,
},
},
};
export default schedule;
Field | Description | Required? |
---|---|---|
type |
How often the trigger will activate: daily |
Yes |
repeats_every |
How often the trigger will repeat, respective to frequency.type |
Yes |
on_week_num |
The nth week of the month the trigger will repeat | No |
import { TriggerTypes } from "deno-slack-api/mod.ts";
import { ScheduledTrigger } from "deno-slack-api/typed-method-types/workflows/triggers/scheduled.ts";
import { ExampleWorkflow } from "../workflows/example_workflow.ts";
const schedule: ScheduledTrigger<typeof ExampleWorkflow.definition> = {
name: "Sample",
type: TriggerTypes.Scheduled,
workflow: `#/workflows/${ExampleWorkflow.definition.callback_id}`,
inputs: {},
schedule: {
// Starts 60 seconds after creation
start_time: new Date(new Date().getTime() + 60000).toISOString(),
end_time: "2040-05-01T14:00:00Z",
occurrence_count: 3,
frequency: { type: "daily" },
},
};
export default schedule;
Field | Description | Required? |
---|---|---|
type |
How often the trigger will activate: weekly |
Yes |
on_days |
The days of the week the trigger should activate on | Yes |
repeats_every |
How often the trigger will repeat, respective to frequency.type |
Yes |
on_week_num |
The nth week of the month the trigger will repeat | No |
import { TriggerTypes } from "deno-slack-api/mod.ts";
import { ScheduledTrigger } from "deno-slack-api/typed-method-types/workflows/triggers/scheduled.ts";
import { ExampleWorkflow } from "../workflows/example_workflow.ts";
const schedule: ScheduledTrigger<typeof ExampleWorkflow.definition> = {
name: "Sample",
type: TriggerTypes.Scheduled,
workflow: `#/workflows/${ExampleWorkflow.definition.callback_id}`,
inputs: {},
schedule: {
// Starts 60 seconds after creation
start_time: new Date(new Date().getTime() + 60000).toISOString(),
frequency: {
type: "weekly",
repeats_every: 3,
on_days: ["Friday", "Monday"],
},
},
};
export default schedule;
Field | Description | Required? |
---|---|---|
type |
How often the trigger will activate: monthly |
Yes |
on_days |
The day of the week the trigger should activate on. Provide the on_week_num value along with this field. |
Yes |
repeats_every |
How often the trigger will repeat, respective to frequency.type |
Yes |
on_week_num |
The nth week of the month the trigger will repeat | No |
import { TriggerTypes } from "deno-slack-api/mod.ts";
import { ScheduledTrigger } from "deno-slack-api/typed-method-types/workflows/triggers/scheduled.ts";
import { ExampleWorkflow } from "../workflows/example_workflow.ts";
const schedule: ScheduledTrigger<typeof ExampleWorkflow.definition> = {
name: "Sample",
type: TriggerTypes.Scheduled,
workflow: `#/workflows/${ExampleWorkflow.definition.callback_id}`,
inputs: {},
schedule: {
// Starts 60 seconds after creation
start_time: new Date(new Date().getTime() + 60000).toISOString(),
frequency: {
type: "monthly",
repeats_every: 3,
on_days: ["Friday"],
on_week_num: 1,
},
},
};
export default schedule;
Field | Description | Required? |
---|---|---|
type |
How often the trigger will activate: yearly |
Yes |
repeats_every |
How often the trigger will repeat, respective to frequency.type |
Yes |
on_week_num |
The nth week of the month the trigger will repeat | No |
import { TriggerTypes } from "deno-slack-api/mod.ts";
import { ScheduledTrigger } from "deno-slack-api/typed-method-types/workflows/triggers/scheduled.ts";
import { ExampleWorkflow } from "../workflows/example_workflow.ts";
const schedule: ScheduledTrigger<typeof ExampleWorkflow.definition> = {
name: "Sample",
type: TriggerTypes.Scheduled,
workflow: `#/workflows/${ExampleWorkflow.definition.callback_id}`,
inputs: {},
schedule: {
// Starts 60 seconds after creation
start_time: new Date(new Date().getTime() + 60000).toISOString(),
frequency: {
type: "yearly",
repeats_every: 2,
},
},
};
export default schedule;
In the Deno Triage Rotation sample app, you can create a trigger that will trip on a specific time and day, which is collected from the user via a form. An example code snippet showing the handler.ts
function in this sample app is provided below. You can view all of the code for the sample app here.
import { SlackFunction } from "deno-slack-sdk/mod.ts";
import { DatastoreItem } from "deno-slack-api/types.ts";
import { ScheduledTrigger } from "deno-slack-api/typed-method-types/workflows/triggers/scheduled.ts";
import { CreateRotationFunctionDefinition } from "./definition.ts";
import RotationDatastore from "../../datastores/rotations.ts";
import AdvanceRotation from "../../workflows/advance_rotation.ts";
import { dateFromTimeInSec } from "../open_form/blocks.ts";
export const WEEKDAY: string[] = [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
];
// This is the handling code for the CreateRotationFunction. It will:
// 1. Fetch the existing rotation for this channel
// 2. Create a new scheduled trigger to trigger our AdvanceRotation function
// 3. Update the existing rotation with the updated rotation details and
// the new scheduled trigger id
// 4. Cleanup/delete any existing triggers based on the function inputs rotation
export default SlackFunction(
CreateRotationFunctionDefinition,
async ({ inputs, client, env }) => {
const debugMode = env["DEBUG"] === "true";
const channelGetResp = await client.apps.datastore.get<
typeof RotationDatastore.definition
>({
datastore: RotationDatastore.name,
id: inputs.channel,
});
if (!channelGetResp.ok) {
return {
error: channelGetResp.error ??
"Failed to query for existing triggers",
};
}
const newScheduledTrigger: ScheduledTrigger<
typeof AdvanceRotation.definition
> = {
type: "scheduled",
name: "Scheduled trigger for the rotation workflow",
description: "A scheduled trigger for the rotation workflow",
workflow: "#/workflows/advance_rotation",
inputs: {
channel: {
value: inputs.channel,
},
},
schedule: getTriggerSchedule(debugMode, inputs),
};
const newScheduledTriggerResp = await client.workflows.triggers.create<
typeof AdvanceRotation.definition
>(
newScheduledTrigger,
);
if (!newScheduledTriggerResp.ok) {
console.log(
"There was an error scheduling the trigger",
newScheduledTriggerResp.error,
newScheduledTriggerResp.response_metadata,
);
return {
error: newScheduledTriggerResp.error ??
"Failed to create scheduled trigger",
};
}
const newRotation: DatastoreItem<typeof RotationDatastore.definition> = {
rotation_trigger_id: newScheduledTriggerResp.trigger.id,
order: inputs.order,
channel: inputs.channel,
message: inputs.message,
start_time: inputs.start_time,
repeats_every: inputs.repeats_every,
repeats_every_number: inputs.repeats_every_number,
last_advance_time: inputs.start_time,
next_advance_time: getNextAdvanceTimeInSec(
inputs.start_time,
inputs.repeats_every_number,
inputs.repeats_every,
),
};
const putResp = await client.apps.datastore.put<
typeof RotationDatastore.definition
>({
datastore: RotationDatastore.name,
item: newRotation,
});
if (!putResp.ok) {
return { error: putResp.error ?? "Failed to update with rotation" };
}
if (Object.keys(channelGetResp.item).length > 0) {
const deleteTriggerResp = await client.workflows.triggers.delete({
trigger_id: channelGetResp.item.rotation_trigger_id,
});
if (!deleteTriggerResp.ok) {
return {
error: deleteTriggerResp.error ??
"Failed to delete past scheduled trigger",
};
}
}
return { outputs: {} };
},
);
// This function takes a start time (in seconds) and returns a time
// that reflects start time + delta (in seconds). Delta is calculated using the
// repeatsEveryNumber and repeatsEvery options saved alongside the rotation.
// @param startTimeInSec = A number representing UNIX time in seconds
// @param repeatsEveryNumber = frequency of repetition, i.e., 1 day or 3 days
// @param repeatsEvery = day or week
// @returns
export function getNextAdvanceTimeInSec(
startTimeInSec: number,
repeatsEveryNumber: number,
repeatsEvery: string,
): number {
const DAYS_PER_WEEK = 7;
const SEC_PER_DAY = 86400;
const SEC_PER_WEEK: number = SEC_PER_DAY * DAYS_PER_WEEK;
const deltaInSec: number = (repeatsEvery === "week")
? (SEC_PER_WEEK * repeatsEveryNumber)
: (SEC_PER_DAY * repeatsEveryNumber);
const nextAdvanceTimeInSec: number = startTimeInSec + deltaInSec;
return nextAdvanceTimeInSec;
}
// This helper function returns a schedule object for a scheduled workflow trigger.
export function getTriggerSchedule(debugMode: boolean, inputs: any) {
const DELAY_SEC = 10;
const { start_time } = inputs;
// When debugMode === true, the start_time of the scheduled trigger will be based on
// the current time + a set delay from after the function is executed, ignoring any
// start time supplied in the inputs to the function.
// This allows for testing the behavior of the workflow
// attached to the scheduled trigger, as the trigger should
// trip after an interval determined by the DELAY_SEC const.
// Set DEBUG to true in your local .env file to toggle this behavior in development
// To set this value for production apps, use the `slack env var add`.
if (debugMode) {
console.log("DEBUG MODE IS ON");
const now = new Date();
const delayInSeconds = now.getSeconds() + DELAY_SEC;
const startTime = new Date(now.setSeconds(delayInSeconds));
return {
start_time: startTime.toISOString(),
};
} else {
const startTime: Date = dateFromTimeInSec(start_time);
return {
start_time: startTime.toISOString(),
timezone: "UTC",
frequency: getTriggerScheduleFrequency(inputs),
};
}
}
// This helper function returns a frequency object, which is required to
// create a scheduled workflow trigger.
function getTriggerScheduleFrequency(inputs: any) {
const { start_time, repeats_every, repeats_every_number } = inputs;
const startDate = new Date(start_time * 1000);
if (repeats_every === "day") {
return {
type: "daily",
repeats_every: repeats_every_number,
};
} else {
return {
type: "weekly",
repeats_every: repeats_every_number,
on_days: [WEEKDAY[startDate.getDay()]],
};
}
}
The response will have a property called ok
. If true
, then the trigger was created, and the trigger
property will be populated.
Your response will include a trigger.id
; be sure to store it! You use that to update
or delete
the trigger if need be. See trigger management.
➡️ With your trigger created, you can now test your app by running your app locally.
✨ Once your app is active, see trigger management for info on managing your triggers in your workspace.
Have 2 minutes to provide some feedback?
We'd love to hear about your experience building Slack automations. Please complete our short survey so we can use your feedback to improve.