Our future generation platform is in closed beta. Please request to participate. During the beta, expect some rough edges, broken windows overlooking blue sky vistas, and regularly scheduled changes.

Getting started with our beta platform

Our next-generation platform empowers everyone to take control of their flow and supercharge their productivity.

In the following guide, we'll get your machine and workspace ready to build, then walk through the starter code that comes with every newly scaffolded Slack app. By the end, we'll have created a new app, customized it a bit, and installed it into your workspace—but that's just the beginning!

A handful of guided tutorials await you at the end of this guide, and we hope that you're as excited as we are to see how the whole slack ecosystem helps you architect amazing automations and apps.

Ready? Let's go!

Step 1Prerequisites

  • Install Deno

    Deno is currently a pre-requisite for using the CLI. If you do not have Deno installed, please do that first.

    Deno is very similar to Node, with a few important differences:

    • There's an embedded package manager. No package.json, .npmrc, node_modules, or NPM at all.
    • External libraries are loaded using the standard ES 6 import statement; you import fully qualified URLs to specify dependencies (e.g., import { serve } from "https://deno.land/std@0.101.1/http/server.ts";; import './lib.ts').
    • index.js/index.ts have no special meaning. Instead, mod.ts is used for importing/exporting a module.
    • Secure by default. Access to the filesystem, network, or environment must be explicitly enabled).

    For more about Deno and some of its features, visit deno.land.

  • OptionalInstall IDE Plugin (if using VS Code)

    Your development experience will be greatly enhanced if your IDE includes support for Deno. If you're using Visual Studio Code, here's a plugin to make working with Deno a breeze.

  • OptionalFamiliarise yourself with TypeScript

    Our next-generation framework is based on Deno and TypeScript, and includes an SDK with type definitions which will help you use auto-completion, linting, and tests in VSCode.

    The Deno runtime supports TypeScript automatically; there's no additional setup or configuration required. If you have written JavaScript using the Node.js runtime, writing TypeScript with Deno should feel familiar.

    The TypeScript handbook maintains sections for migrating from JavaScript, Java/C#, and functional languages. Check it out!

  • Enable beta features in your workspace

    We're almost ready!

    The final prerequisite is to enable Beta features in your workspace. This will allow slack to fully communicate with your workspace as you build and deploy, and must be completed by a Workspace Admin.

    To enable beta features:

    1. Go to your Workspace Settings. To do that, click your workspace name, select settings & administration from the menu, then click Workspace settings.
    2. Scroll down until you see the Slack Platform & Hosting Beta section. Expand it, then click the checkbox to agree to the Slack Platform Beta Services Terms.
    3. When you do that, you’ll be presented with an option to select who specifically can create Slack-hosted apps in Beta. If you aren't sure what to select, keep the default option, Any owner or admin, then click the Save button.


    Now that all the prerequisites are finished, we can head to the next section and install the Slack CLI. Let's go!

Step complete!

Step 2Install & setup the Slack CLI

  • Download the CLI

    Download the latest version of the CLI for your platform. Decompress the downloaded files.

  • Add the binary to your path

    On macOS, copy the slack binary to a destination in your path (we recommend /usr/local/bin).

    For Windows, copy the .exe file into any location accessible in your path.

    Test that the app is properly in your path by running slack version, and verifying that the version matches the one you downloaded.

Step complete!

Step 3Authenticate the CLI

  • Generate and copy an auth ticket

    Before using the CLI, it needs to be authenticated. This will associate it with a Slack workspace and your user account in that workspace. Authentication happens in two steps:

    • first, we'll tell the CLI to generate an authentication ticket (in this step);
    • second, we'll copy and paste that ticket into any channel of the workspace where you will install the app (in the next step).


    Generate an authentication ticket by running the command slack login.

    This produces some output in your terminal, which says that slack is waiting for authentication. We'll do that now.

    To begin authentication, find the authentication ticket from the terminal output—which looks something like this:

    /slackauthticket ABC123defABC123defABC123defABC123defXYZ

    Copy that whole string (starting with /slackauthticket and including the entire alphanumeric string next to it), and proceed to the next step to finish authentication.

  • Paste the auth ticket into Slack

    With the auth ticket in your clipboard from the previous step, go to your Slack workspace.

    In any channel, paste the auth ticket into a new message, then send the message.

    A modal entitled "Grant Permission" will pop-up, asking you to confirm the permissions that you must grant to your apps built and installed with slack. Go ahead and click the Confirm button.


    If Slackbot tells you that /slackauthticket is not a valid command, your workspace isn't configured for beta access yet. Head back to Step 1, Prerequisites, to Enable beta features in your workspace.


    Slackbot will reply with, "Lovely! You may return to your terminal."

    You can verify that your CLI is correctly authenticated by going back to your terminal and running slack auth info, which will emit information you and the workspace you've authorized the CLI to work in.

    Let's recap what we've got so far:

    • Deno is installed and our editor is ready for Deno and Typescript
    • Our workspace has beta features enabled
    • The Slack CLI is authorized for our workspace


    We are now ready to start building our first app on the next-generation platform!

Step complete!

Step 4Create an app using the Slack CLI

  • Scaffold a new app

    In the following sections, we're going to create a new app with some starter code ready to deploy. When installed, our app will provide users with a shortcut named Reverse, which will take one argument—a string—and return the same string in reverse. This starter code also helps illustrate how triggers call workflows and workflows call functions.

    So let's create a new app!

    First, navigate to a directory where you have write access and want your app to live.

    Next, we'll run the CLI's create command.

    There are two ways to run create:

    • slack create, which will create a new project for you with a random name, or
    • slack create my-app, where my-app is a name that you choose.


    For this guide, let's create an app with a name that matches the starter code we'll see in the next section. Go ahead and run the following:

    $ slack create reverse-string

    You'll see some output confirming that reverse-string was successfully created, which includes a new folder reverse-string.

    Head in to that directory:

    $ cd reverse-string

    Great! We've created a new Slack app. In the next section, we'll take a look at the file and folder structure to get a feel for where things go and how they all work together.

Step complete!

Step 5Understand the app structure

  • The project's parent folder

    The structure of next-generation Slack platform apps aims to favor convention over configuration.

    $ ls

    As is (hopefully) obvious, directories are named logically and the files within those directories map to specific functionality within the app.

    There are four relevant areas to focus on at this point: the functions, triggers, and workflows folders, and the project.ts file.

    • functions/: the Functions you define that can be invoked by Workflows will each be in a separate file here.

    • triggers/: the Triggers that can invoke Workflows, each in a separate file.

    • workflows/: the Workflows that can be invoked by Triggers, also in separate files.

    • project.ts: the project definition. Here you will specify information about your app, including the specific triggers and tables that your app will be using.


    The other items in the project include:

    • tables/: table definitions that enable your app to store and retrieve data hosted on Slack's infrastructure. Tables are covered in-depth in their own tutorial.

    • .slack/: a home for internal configuration files, scripts hooks, and the app SDK. This directory must be checked in to your version control.

    • import_map.json: a helper file for Deno that specifies where modules should be imported from.

    • assets/: a place to store assets related with the project. This is a great place to store the icon that your app will display when users interacts with it.


    Each Slack app that you create will start with this same structure, and follow the same conventions for adding functionality:

    • New functions will be added to the functions/ folder
    • New workflows will be added to the workflows/ folder
    • New triggers will be added to the triggers/ folder


    At this point we have a new Slack app created with a fresh perspective on the app's project structure.

    In the next section, we'll look at how the app's components fit together as we open up the included function, workflow, and trigger, then we'll deploy this app to our workspace.

  • The example function

    Inside the functions/ folder, you'll find two files:

    • reverse.ts, which contains the function definition, and
    • reverse_test.ts, which contains (yep, you guessed it) an example test for said function.


    In reverse.ts, you can see the example function ReverseString and its definition.

    Notice how ReverseString can take input_parameters that are described in the properties property—in this case, a single variable named stringToReverse of type Schema.types.string—and also define output_parameters—which, here, is another Schema.types.string named reverseString.

    At the end of the file you'll see how ReverseString performs its job (which, of course, is to reverse a string) and subsequently returns a value:

        const reverseString = inputs.stringToReverse.split("").reverse().join("");
        return await {
          outputs: { reverseString },

    Notice how it's using the stringToReverse input parameter (inputs.stringToReverse), and that the resultant transformed string stored in reverseString is declared in outputs: { reverseString } as the Function's return value.

    ReverseString is a kind of custom function. The SDK that comes with every new Slack app (and that is imported at the top of the file) also provides definitions for many built-in functions that give you access to many routine Slack features, like sending messages and creating channels.

    All these are important for the next step, when we look at how the Workflow calls this function and uses its output.

  • The example workflow

    We saw the Function that we want to use, so now let's take a look at the Workflow that will invoke that function.

    Let's head in to the workflows/ folder.

    Inside the file reverse_echo.ts, you'll find the Workflow definition for the example Workflow ReverseEchoString.

    There are a few important things to note here:

    First, Functions executed by the Workflow must be imported. Here, we are performing a local import of ReverseString using its relative path:

    import { ReverseString } from "../functions/reverse.ts";

    Second, Workflow definitions look a lot like Function definitions, but while you can define both input_parameters and output_parameters for Functions, Workflows only accept input_parameters.

    After we define ReverseEchoString with DefineWorkflow, the execution steps of the Workflow are enumerated via the .addStep() method.

    Let's look at what's going on piece by piece.


    const reverseStep = ReverseEchoString.addStep(ReverseString, {
      stringToReverse: ReverseEchoString.inputs.stringToReverse,

    In the above code, we define a variable reverseStep, and set its value to be the output of the ReverseString function. To do that, we pass along the Workflow's input variable—stringToReverse—as the input to our Function, ReverseString.

    When this is finished executing, reverseStep will be equal to whatever ReverseString declared as its outputs value.


    ReverseEchoString.addStep(Schema.slack.functions.SendMessage, {
      channel_id: ReverseEchoString.inputs.channel,
      message: `Your string in reverse is *${reverseStep.outputs.reverseString}*`,

    In this next code block, a second step is added to the ReverseEchoString Workflow that is a built-in function—Schema.slack.functions.SendMessage, which sends a message to a channel based on that channel's channel_id.

    Notice that channel_id comes from one of the Workflow's inputs, and message contains the output value of the ReverseString function.

    When this Workflow is invoked, both the steps will execute in the order they are written; the first step will take the Workflows input and reverse it, and the second step will send a message with the input reversed.

    But where does the Workflow input come from?

    We'll find out in the next part when we look at the final piece of the puzzle—the Trigger!

  • The example trigger

    We've seen how to declare a Function that has inputs and outputs.

    We've seen how a Workflow takes input and sends it to a Function.

    Now let's take a look at how a Trigger invokes a Workflow and how Workflows can collect input.

    In the triggers/ folder, open the file reverse_echo_shortcut.ts. In here, you'll find the definition for the ReverseEchoShortcut trigger.

    There are several key pieces here, so let's look at it piece by piece.

    Let's start from the top.


    import { DefineTrigger, TriggerTypes } from "slack-cloud-sdk/mod.ts";
    import { ReverseEchoString } from "../workflows/reverse_echo.ts";

    First, we import the parts of the SDK that we need to define a Trigger, along with the Workflow we want to this Trigger to invoke.


    export const ReverseEchoShortcut = DefineTrigger("reverse_echo_shortcut", {
      type: TriggerTypes.Shortcut,
      name: "Reverse",
      description: "Reverses a string and echoes it in-channel",

    Next, we use DefineTrigger (which we imported from the SDK in the first line) to define a Trigger named ReverseEchoShortcut, specifying three things: type, which also comes from the SDK, name, and description. Remember these because they're going to come up again very soon!


    What is that string in the first argument for DefineTrigger?
    You may have noticed a string representation of the variable name as the first argument for DefineFunction, DefineWorkflow, and DefineTrigger. This is a unique string name that can be anything; it's only used behind the scenes, like when slack emits helpful information to your command line window during development.

    Notice that the Trigger type here is Shortcut, which means that this Trigger is a Global shortcut Trigger. When we deploy our app to our workspace in the next section, we'll invoke this Trigger via the Shortcuts menu.


    .withInputs((ctx) => ({
        channel: ctx.data.channel_id,

    In the final parts of the file, we're using method chaining to tell the Trigger which Workflow to invoke via the .runs() method, and also passing contextual information about the Trigger's invocation via the .withInputs() method. In this case, we're passing along the channel_id of the channel we invoked the Trigger from.


    So how does this all come together?

    Once invoked, the Workflow will open a modal inside Slack.

    The name and description keys defined in the Trigger are used as the name and description of the modal.

    With our Trigger set to invoke our Workflow and our Workflow set to invoke our Function, we'll look at one more file—project.ts—then deploy this app to our workspace.

    See you in the next section!

  • The project.ts file

    Before we deploy our app, take a quick look at the project.ts file in the root project folder.

    There are two things to note here about our app's Triggers:

    • each are imported at the top of the file, and
    • each are declared in the Project we instantiate.


    We are also enumerating our app's permissions in botScopes.

    Finally, you can see where we set the app's icon and name, both of which will be visible by your users in your workspace. If you would like, go ahead and change the name now before we run the app in development mode. Perhaps something like:

    name: "String Reverser 9000",


    We've come a long way! It's time to head back to the command prompt and, in our next and final section, deploy this app to our workspace. 🚀

Step complete!

Step 6Deploy and run your app

There are two ways to deploy and run your app: Local development mode ("dev"), and App Hosted mode ("prod").

  • Local development mode

    While developing your app, you can run it on your machine and sync changes in real-time using the local development server built in to the slack CLI.

    1. Navigate to your project's root folder at the command prompt. Then, execute slack run.
    2. You'll be prompted to choose a workspace to install your dev app. Select the workspace from which we authorized the CLI back in Step 3, Authorize your CLI. A local development server will start up and begin listening for activity.
    3. Jump in to Slack and look for your app. You should be able to find a version of your app with (dev) appended to it's name. This is your "development" app.
    4. To see the local development server in action, go back to project.ts and update the name of your app. When you save changes, the local development server will notice the changed file and update the name of your app accordingly.


    You can now test your app by using the Shortcut menu and locating the Trigger that we defined named Reverse. To pull up the Global Shortcut menu quickly, go to your workspace's #general chat and type this message: /reverse.

    Notice that your app's Shortcut Trigger appears in the menu as "Reverse with String Reverser 9000 (dev)." If you named your app something else, then that name will appear next to the Shortcut name.

    Click on that shortcut, and you'll see the modal from earlier appear. Go ahead and try it out.

    When you want to turn off the local development server, use Ctrl+c in the command prompt.

  • App Hosted mode

    When your app is production-ready and you want to deploy it, use slack deploy to get it installed into the workspace of your choice.

    Navigate to your project's root folder at the command prompt, then execute slack deploy.

    You'll be prompted to choose a workspace to install your app. Select the workspace from which we authorized the CLI back in Step 3, Authorize your CLI. Notice that slack will package up and deploy your app, then put you right back at the command prompt; there's no local development server when deploying this way.

    Jump in to Slack and look for your production app. You'll know it's the production version of your app because it does not have (dev) appended to its name.

    At this point, your app has been deployed to your workspace. It is important to note that Local and App Hosted modes create different apps on the configured workspace—even though both apps are using the exact same codebase.

    What's the difference?

    When you use slack run, you spin up a local development server and can see your changes propagated to the dev version of your workspace app in real-time; when you use slack deploy, you are packaging that app up for production hosting on our infrastructure.

    Some other important differences between slack run and slack deploy include:

    • Environment variables (with slack var) only works on hosted deployments (e.g., slack deploy)
    • Local environment variables (with slack run) are defined in .env
    • Icons are not supported in Local Development mode
    • After executing slack run, a dev version of your app is installed to the workspace you specify. If you then exit out of the development server, you can still invoke the app's triggers but they will simply timeout; a dev app will only work when the development server is running.

    Where to go from here?

    This marks the end of our Get Started guide, but only the start of your next-generation Slack development journey. Continue onward and keep exploring; we're excited to see what you build!

Step complete!

Keep exploring

Learn more about developing functions that can be triggered by users via commands and workflows, or by subscribing to fascinating metadata attached to messages by your app and others.

Our tutorials below continue exploring how our future-generation platform can enable novel workflows.

Create an incident

Create a new incident, spin up a new channel, and post details about the incident.

Using Slack Built-in functions

Leveraging built-in functions to create a new project workflow.

How to store and retrieve data from Slack hosted tables

Storing and retrieving data from tables hosted by Slack

Send and receive metadata events

Learn how to send messages with metadata and create triggers that listen for events with custom metadata.

Create tests for your functions

Learn how to compose tests for your new functions

Managing app deployment and Admin Approved Apps

Navigating app deployment when the Admin Approved Apps setting is enabled on a workspace.

Build multiple versions of your app from a single code base

Deploy multiple versions of your app using environment variables