Webhooks
Block Actions

Overview

If you send a message with BlockKit components, you can trigger a workflow when a user interacts with those components. For example, you can trigger a workflow when a user clicks a button, selects an option from a menu, or submits a modal.

The event will be the block_action event payload from Slack, which includes the actions array. You can use the actions array to determine which action was triggered, and the block_id and action_id to determine which component was interacted with.

A very simple example would be to first send a message to a channel with a button, and then trigger a workflow when the button is clicked:

import * as slack from "@trigger.dev/slack";
import { z } from "zod";

const BLOCK_ID = "employee-ping";

new Trigger({
  id: "send-message-with-button",
  name: "Send Message with Button",
  on: scheduleEvent({
    rateOf: {
      minutes: 5,
    },
  }),
  run: async (event, ctx) => {
    await slack.postMessage("Are you still there?", {
      channelName: "employee-ping",
      //text appears in Slack notifications on mobile/desktop
      text: "Are you still there?",
      blocks: [
        {
          type: "section",
          text: {
            type: "mrkdwn",
            text: "Are you still there?",
            verbatim: true,
          },
        },
        {
          type: "actions",
          block_id: BLOCK_ID, // this is the block_id that we'll use to identify the message
          elements: [
            {
              type: "button",
              action_id: "nope",
              text: {
                type: "plain_text",
                text: "I'm at lunch",
                emoji: true,
              },
              value: "lunch",
            },
            {
              type: "button",
              action_id: "youbet",
              text: {
                type: "plain_text",
                text: "Still here!",
                emoji: true,
              },
              value: "in-my-seat",
            },
          ],
        },
      ],
    });
  },
}).listen();

Passing custom data

There are three ways to pass custom data to the workflow that is triggered by the event:

  1. Use the value field of the BlockKit component. This is the easiest way to pass data, but it is limited to 2000 characters.
  2. Use the metadata option when sending the message. This is best for handling “context” data that relates to any possible action taken in a BlockKit message.

An example of using the value field:

await slack.postMessage("New Issue", {
  channelName: "github-issues",
  //text appears in Slack notifications on mobile/desktop
  text: "There is a new GitHub issue in the repo",
  blocks: [
    {
      type: "actions",
      block_id: "new.issue", // this is the block_id that we'll use to identify the message
      elements: [
        {
          type: "button",
          action_id: "reply",
          text: {
            type: "plain_text",
            text: "Reply to Issue",
          },
          value: githubIssueId,
        },
        {
          type: "button",
          action_id: "close",
          text: {
            type: "plain_text",
            text: "Close Issue",
          },
          value: githubIssueId,
        },
      ],
    },
  ],
});

And an example using the metadata option (which can be any JSON-serializable data):

await slack.postMessage("New Issue", {
  channelName: "github-issues",
  //text appears in Slack notifications on mobile/desktop
  text: "There is a new GitHub issue in the repo",
  metadata: { githubIssueId },
  blocks: [
    {
      type: "actions",
      block_id: "new.issue", // this is the block_id that we'll use to identify the message
      elements: [
        {
          type: "button",
          action_id: "reply",
          text: {
            type: "plain_text",
            text: "Reply to Issue",
          },
          value: "reply",
        },
        {
          type: "button",
          action_id: "close",
          text: {
            type: "plain_text",
            text: "Close Issue",
          },
          value: "close",
        },
      ],
    },
  ],
});

JSX Slack

You can use the JSX Slack project to build your BlockKit messages and trigger the blockActionInteraction event.

Step 1: Install and configure JSX Slack

  • npm

  • pnpm

  • yarn

npm install jsx-slack

If you aren’t already using React in your project, you will need to update your tsconfig.json file to add the compilerOptions.jsx option, and ensure to include .tsx files in your include array:

{
  "compilerOptions": {
    "jsx": "react-jsx"
  },
  "include": ["src/**/*.ts", "src/**/*.tsx"]
}

Step 2: Create your BlockKit message

/** @jsxImportSource jsx-slack */
import JSXSlack, {
  Actions,
  Blocks,
  Button,
  Section,
  Select,
  Option,
} from "jsx-slack";

export function progressBlock(blockId: string) {
  return JSXSlack(
    <Blocks>
      <Section>How is your progress today?</Section>
      <Actions blockId={blockId}>
        <Button value="blocked" actionId="status-blocked">
          I'm blocked
        </Button>
        <Button
          value="help"
          actionId="status-help"
          url="https://xkcd.com/1349/"
        >
          Get help
        </Button>
        <Select actionId="rating" placeholder="Rate it!">
          <Option value="5">5 {":star:".repeat(5)}</Option>
          <Option value="4">4 {":star:".repeat(4)}</Option>
          <Option value="3">3 {":star:".repeat(3)}</Option>
          <Option value="2">2 {":star:".repeat(2)}</Option>
          <Option value="1">1 {":star:".repeat(1)}</Option>
        </Select>
      </Actions>
    </Blocks>
  );
}

Please note, that to use jsx-slack you will need to add the jsxImportSource pragma to the top of your file. This is because jsx-slack is not a React library, but it uses the same syntax as React. The file also needs to be a .jsx or .tsx file (like when you use React).

Step 3: Use your BlockKit message in a postMessage call

import * as slack from "@trigger.dev/slack";
import { z } from "zod";
import { progressBlock } from "./messages";

const BLOCK_ID = "issue.action.block";

//1. every minute see how your employees are doing, we don't recommend this frequency 😉
new Trigger({
  id: "slack-interactivity",
  name: "Testing Slack Interactivity",
  on: scheduleEvent({
    rateOf: {
      minutes: 1,
    },
  }),
  run: async (event, ctx) => {
    await slack.postMessage("jsx-test", {
      channelName: "employee-progress",
      text: "How is your progress today?",
      blocks: progressBlock(BLOCK_ID), // Pass in the block_id which we'll trigger on in the next step
    });
  },
}).listen();

Params

blockIdrequired
string

The block_id of the BlockKit component that you want to listen for.

actionId
string | string[]

The action_id of the BlockKit component that you want to listen for. If you don’t specify an actionId, the workflow will be triggered for any action on the specified blockId. You can also specify an array of action_ids to listen for multiple actions.

Event Payload

Please see the Slack Block Actions event payload for more information.

type
string

The type of event. This will always be block_actions.

trigger_id
string

A short-lived ID that can be used to open a modal. See the openView docs

response_url
string

A short-lived URL that can be used to send a response to the user. See the postMessageResponse docs for more information.

user
user object

The user that triggered the event.

message
message object

The message that the user interacted with.

view
view object

The view that the user interacted with.

actions
Array<action>

An array of actions that the user interacted with. actions.action_id can be used to determine which action was triggered. (e.g. which button was clicked)

team
team object

The team that the user is a part of.

channel
channel object

The channel where the interaction took place.

container
object