# Interactivity

> Receive block_actions when people use your app's controls, answer with response_url, open modals with trigger_id, and load options for external selects.

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](https://workstation.repzo.com/docs/developers/chat-apps/modals).

## 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](https://workstation.repzo.com/docs/developers/chat-apps/ephemeral-messages), modal or [Home tab](https://workstation.repzo.com/docs/developers/chat-apps/app-home) was used
- `view_submission` and `view_closed` — see [Modals](https://workstation.repzo.com/docs/developers/chat-apps/modals)
- `block_suggestion` — someone typed into an [external select](https://workstation.repzo.com/docs/developers/block-kit/elements/select-menu-element)

Only the app that posted a message hears about its controls. Every request is signed — [verify it](https://workstation.repzo.com/docs/developers/chat-apps#verify-requests-from-repzo).

> **Tip**
>
> The same setting is available through the API: an event subscription whose `eventTypes` include `block_actions` (and optionally `block_suggestion` for a separate options URL) — `POST /api/v1/events/subscriptions`.

## The `block_actions` payload

```json
{
  "type": "block_actions",
  "team": { "id": "1bf87154-…" },
  "user": { "id": "42bc76d7-…", "username": "dana@example.com", "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 — `value` for buttons, `selected_option` for menus and radio buttons, `selected_date`, `selected_time`, `selected_date_time`, `selected_user(s)`, `selected_conversation(s)`… Each [element page](https://workstation.repzo.com/docs/developers/block-kit/elements) lists its own.
- `state.values` holds the current value of every other control in the message, keyed by `block_id` then `action_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, `container` is `{ "type": "view", "view_id": "…" }`, the payload carries `view` instead of `message`, and there is no `response_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`

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](https://workstation.repzo.com/docs/developers/chat-apps/ephemeral-messages) only the person who clicked sees (Slack's default) |

```bash
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`

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](https://workstation.repzo.com/docs/developers/chat-apps/modals).

## Confirmation dialogs

Add a [`confirm` object](https://workstation.repzo.com/docs/developers/block-kit/objects/confirmation-dialog-object) to a button, menu or picker to ask "Are you sure?" before the action is sent.

## Live options: `block_suggestion`

An [`external_select`](https://workstation.repzo.com/docs/developers/block-kit/elements/select-menu-element) asks your app for options as the person types. Once they type at least `min_query_length` characters (default 3), Workstation `POST`s, signed:

```json
{
  "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:

```json
{
  "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

To hear about Home tab opens, subscribe to `app_home_opened` — it arrives as a regular event (`{ "type": "event_callback", "event": { … } }`). See [App Home](https://workstation.repzo.com/docs/developers/chat-apps/app-home).

## Related

- [Modals](https://workstation.repzo.com/docs/developers/chat-apps/modals)
- [Block elements](https://workstation.repzo.com/docs/developers/block-kit/elements)
- [Ephemeral messages](https://workstation.repzo.com/docs/developers/chat-apps/ephemeral-messages)
