Slack developer beta concepts BETA

This page contains all the concepts that are necessary to allow you to use the next-gen Slack features in Python.

Our next-generation platform is currently in beta. Your feedback is most welcome - all feedback will help shape the future platform experience!


Manifest

Your project should contain a manifest.json file that defines your app’s manifest. This is where you’ll configure your application name and scopes, declare the functions your app will use, and more. Refer to the App Manifest and Manifest Property documentation to learn about the available manifest configurations.

Notably, the App Manifest informs Slack of the definitions for:

manifest.json is located at the top level of our example projects, along with a triggers folder containing *_trigger.json files that define the triggers for your app.

.
├── ...
├── manifest.json             # app manifest definition
├── triggers                  # folder with trigger files
│   ├── sample_trigger.json   # trigger definition
│   └── ...
└── ...


Linting, prediction & validation

Syntax and formatting checks are required to efficiently edit your manifest.json. Multiple IDEs are able to support this, namely: Visual Studio Code, Pycharm, Sublime Text (via LSP-json) and many more.

To get manifest prediction & validation in your IDE, include the following line in your manifest.json file:

1
2
3
4
{
  "$schema": "https://raw.githubusercontent.com/slackapi/manifest-schema/main/manifest.schema.json",
  ...
}

Using the Slack CLI you can validate your manifest.json against the Slack API with:

1
slack manifest validate

Refer to our template project to view a full version of manifest.json.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
{
  "$schema": "https://raw.githubusercontent.com/slackapi/manifest-schema/main/manifest.schema.json",
  "_metadata": {
    "major_version": 2,
  },
  "display_information": {
    "name": "Bolt Template App TEST"
  },
  "features": {
    "app_home": {
      "home_tab_enabled": false,
    },
    "bot_user": {
      "display_name": "Bolt Template App TEST",
      "always_online": false
    }
  },
  "oauth_config": {
    "scopes": {
      "bot": [
        "chat:write",
      ]
    }
  },
  "settings": {
    "socket_mode_enabled": true,
  },
  "functions": {},
  "types": {},
  "workflows": {},
  "outgoing_domains": []
}

Common Manifest Types

parameters
object
properties properties defines the properties
required list[string] defines the properties required by the function
properties
dictionary
key string defines the property name
value property defines the property
property
object
type string defines the property type
description string defines the property description
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"$comment": "sample parameters object"
"*_parameters":{
  "properties": {
    "property_0_name": {
      "type": "string",
      "description": "this is my first property"
    },
    "property_1_name": {
      "type": "integer",
      "description": "this is my second property"
    }
  },
  "required": [
    "property_0_name"
  ]
}

Manifest functions

Your app can invoke Functions defined and created by you (Custom Functions). In order for this to work, Slack must know they exist. Define them in your App Manifest also known as manifest.json in your project. The next time you slack run your app will inform Slack they exist.

functions
dictionary
key string defines the function's callback_id
value function defines the function
function
object
title string defines the title
description string defines the description
input_parameters parameters defines the inputs
output_parameters parameters defines the outputs

Refer to our template project to view a full version of manifest.json.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  "functions": {
    "sample_function": {
      "title": "Sample function",
      "description": "A sample function",
      "input_parameters": {
        "properties": {
          "message": {
            "type": "string",
            "description": "Message to be posted"
          }
        },
        "required": [
          "message"
        ]
      },
      "output_parameters": {
        "properties": {
          "updatedMsg": {
            "type": "string",
            "description": "Updated message to be posted"
          }
        },
        "required": [
          "updatedMsg"
        ]
      }
    }
  }

Manifest workflows

Your app can use Functions by referencing them in Workflows. Your Custom Functions and the Built-in Functions can be used as steps in Workflow definitions.

Workflows are invoked by Triggers. You will need to set up a Trigger in order to use your defined workflows. Triggers, Workflows, and Functions work together in the following way:

Trigger → Workflow → Workflow Step → Function

Your App Manifest, found at manifest.json, is where you will define your workflows.

workflows
dictionary
key string defines the workflow's id
value workflow defines the function
workflow
object
title string defines the title
description string defines the description
input_parameters parameters defines the inputs
steps list[parameters] defines the steps
step
object
id string defines the order of the steps
function_id string identifies the function to evoke
inputs dict[string:string] defines the inputs to provide to the function

Refer to our template project to view a full version of manifest.json.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
  "workflows": {
    "sample_workflow": {
      "title": "Sample workflow",
      "description": "A sample workflow",
      "input_parameters": {
        "properties": {
          "channel": {
            "type": "slack#/types/channel_id"
          }
        },
        "required": [
          "channel"
        ]
      },
      "steps": [
        {
          "id": "0",
          "function_id": "#/functions/sample_function",
          "inputs": {
            "message": "{{inputs.channel}}"
          }
        },
        {
          "id": "1",
          "function_id": "slack#/functions/send_message",
          "inputs": {
            "channel_id": "{{inputs.channel}}",
            "message": "{{steps.0.updatedMsg}}"
          }
        }
      ]
    }
  }

Built-in functions

Slack provides built-in functions that can be used by a Workflow to accomplish simple tasks. You can add these functions to your workflow steps in order to use them.

Refer to the built-in functions document to learn about the available built-in functions.

1
2
3
4
5
6
7
8
9
10
11
    "$comment": "A step to post the user name to a channel"
    "steps": [
      {
        "id": "0",
        "function_id": "slack#/functions/send_message",
        "inputs": {
          "channel_id": "{{inputs.channel}}",
          "message": "{{inputs.user_name}}"
        }
      }
    ]

Listening & responding to functions

Your app can use the function() method to listen to incoming function requests. The method requires a function callback_id of type str. This callback_id must also be defined in your Function definition. Functions must eventually be completed with the complete() function to inform Slack that your app has processed the function request. complete() requires one of two keyword arguments: outputs or error. There are two ways to complete a Function with complete():

  • outputs of type dict completes your function successfully and provides a dictionary containing the outputs of your function as defined in the app’s manifest.
  • error of type str completes your function unsuccessfully and provides a message containing information regarding why your function was not successful.

Refer to the module document to learn the available listener arguments.

1
2
3
4
5
6
7
8
9
10
11
12
13
# The sample function simply outputs an input
@app.function("sample_function")
def sample_func(event: dict, complete: Complete):
    try:
        message = event["inputs"]["message"]
        complete(
            outputs={
                "updatedMsg": f":wave: You submitted the following message: \n\n>{message}"
            }
        )
    except Exception as e:
        complete(error=f"Cannot submit the message: {e}")
        raise e

Function Interactivity

The function() method returns a SlackFunction decorator object. This object can be used by your app to set up interactive listeners such as actions and views. These listeners listen to events created during the handling of your function event. Additionally, they will only be called when a user interacts with a block element that has the following attributes:

  • It was created during the handling of a function event.
  • The action_id matches the interactive listeners action_id.

These listeners behave similarly to the ones assigned directly to your app. The notable difference is that complete() must be called once your function is completed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# Your listener will be called when your function "sample_function" is triggered from a workflow
# When triggered a message containing a button with an action_id "approve_button" is posted
@app.function("sample_function")
def sample_func(event: dict, complete: Complete):
    try:
        client.chat_postMessage(
            channel="a-channel-id",
            text="A new button appears",
            blocks=[
                {
                    "type": "actions",
                    "block_id": "approve-button",
                    "elements": [
                        {
                            "type": "button",
                            "text": {
                                "type": "plain_text",
                                "text": "Click",
                            },
                            "action_id": "sample_action",
                            "style": "primary",
                        },
                    ],
                },
            ],
        )
    except Exception as e:
        complete(error=f"Cannot post the message: {e}")
        raise e

# Your listener will be called when a block element
#   - Created by your "sample_func"
#   - With the action_id "sample_action"
# is triggered
@sample_func.action("sample_action")
def update_message(ack, body, client, complete):
    try:
        ack()
        if "container" in body and "message_ts" in body["container"]:
            client.reactions_add(
                name="white_check_mark",
                channel=body["channel"]["id"],
                timestamp=body["container"]["message_ts"],
            )
        complete()
    except Exception as e:
        logger.error(e)
        complete(error=f"Cannot react to message: {e}")
        raise e