Interactivity
When someone clicks a button, picks an option, changes a date or submits a form in something your app posted, Workstation sends your app a request — the same payloads Slack sends. This page covers clicks and changes (block_actions), answering them, and live option lists (block_suggestion). Forms are covered in Modals.
Set the interactivity URL
Section titled “Set the interactivity URL”Open Settings → Integrations & Developers → Developer Apps → Bots, expand your bot and fill in Interactivity. Workstation sends:
block_actions— a control in a message, ephemeral message, modal or Home tab was usedview_submissionandview_closed— see Modalsblock_suggestion— someone typed into an external select
Only the app that posted a message hears about its controls. Every request is signed — verify it.
The block_actions payload
Section titled “The block_actions payload”{ "type": "block_actions", "team": { "id": "1bf87154-…" }, "user": { "id": "42bc76d7-…", "username": "[email protected]", "name": "Dana Reader", "team_id": "1bf87154-…" }, "api_app_id": "75385d0c-…", "container": { "type": "message", "message_ts": "3a77c808-…", "channel_id": "e0fd46e9-…", "is_ephemeral": false }, "trigger_id": "c0b6f3d1-…", "channel": { "id": "e0fd46e9-…", "name": "refunds" }, "message": { "type": "message", "ts": "3a77c808-…", "bot_id": "75385d0c-…", "blocks": [ … ] }, "state": { "values": { "pick": { "reason": { "type": "static_select", "selected_option": { "text": { "type": "plain_text", "text": "Damaged" }, "value": "damaged" } } } } }, "response_url": "https://workstation.repzo.com/api/bot/responses/eyJ…", "actions": [ { "type": "button", "action_id": "approve", "block_id": "decision", "text": { "type": "plain_text", "text": "Approve" }, "value": "refund-42", "style": "primary", "action_ts": "1791799555.321000" } ]}actions[0]is what was used. Its value field depends on the element —valuefor buttons,selected_optionfor menus and radio buttons,selected_date,selected_time,selected_date_time,selected_user(s),selected_conversation(s)… Each element page lists its own.state.valuesholds the current value of every other control in the message, keyed byblock_idthenaction_id, so a Submit button can read the menus next to it.- People can only pick what your blocks offer: option text in the payload comes from your message, never from the person’s browser.
- In a modal or Home tab,
containeris{ "type": "view", "view_id": "…" }, the payload carriesviewinstead ofmessage, and there is noresponse_url.
Answer with any 2xx. Clicks are delivered from a queue, so unlike Slack you don’t have to answer within three seconds — but a failed delivery isn’t retried, so keep the endpoint up. Each delivery is logged, and a subscription that fails five times in a row is turned off: fix the URL and turn it back on in the bot’s settings.
Answer with response_url
Section titled “Answer with response_url”Each payload from a message has a response_url. POST JSON to it to change what the person sees — no token needed. It works five times within 30 minutes.
| Body | Effect |
|---|---|
{ "replace_original": true, "text": "…", "blocks": [ … ] } |
Replace the message the control was in |
{ "delete_original": true } |
Delete that message |
{ "response_type": "in_channel", "text": "…", "blocks": [ … ] } |
Post a new message in the same conversation (and thread) |
{ "text": "…", "blocks": [ … ] } |
Post an ephemeral message only the person who clicked sees (Slack’s default) |
curl -X POST "$RESPONSE_URL" -H "Content-Type: application/json" \ -d '{ "replace_original": true, "text": "Refund approved ✅" }'attachments are accepted in every case. You can also edit the message any time with chat.update.
Open a modal with trigger_id
Section titled “Open a modal with trigger_id”Every interaction payload has a trigger_id. Pass it to views.open within 60 seconds to open a modal for that person; each trigger works once. See Modals.
Confirmation dialogs
Section titled “Confirmation dialogs”Add a confirm object to a button, menu or picker to ask “Are you sure?” before the action is sent.
Live options: block_suggestion
Section titled “Live options: block_suggestion”An external_select asks your app for options as the person types. Once they type at least min_query_length characters (default 3), Workstation POSTs, signed:
{ "type": "block_suggestion", "user": { "id": "42bc76d7-…", "name": "Dana Reader" }, "api_app_id": "75385d0c-…", "container": { "type": "message", "message_ts": "3a77c808-…", "channel_id": "e0fd46e9-…" }, "block_id": "lookup", "action_id": "customer", "value": "lin"}Answer within 3 seconds with up to 100 options or option groups:
{ "options": [ { "text": { "type": "plain_text", "text": "Lina Haddad" }, "value": "cust-1" }, { "text": { "type": "plain_text", "text": "Linda Saleh" }, "value": "cust-2" } ]}The person can only pick an option you returned (or the element’s initial_option); anything else is refused before it reaches you.
Other events
Section titled “Other events”To hear about Home tab opens, subscribe to app_home_opened — it arrives as a regular event ({ "type": "event_callback", "event": { … } }). See App Home.