Slack ā
slack wraps the Slack Web API. One instance carries one set of Slack credentials (bot token + optional user OAuth) and exposes read + write ops over channels, threads, users, messages, and reactions.
This is the outbound Slack surface ā what a workflow or LLM calls to do something on Slack. The inbound surface (events arriving in real time) is the Slack channel; the two are separate modules but normally configured together. The connector row also stores per-instance OAuth app credentials for the Connect Account user-token flow.
| Source | internal/connectors/slack/ |
| Key | slack |
| Icon | š¬ |
| Tier | builtin (every wick app) |
| Health check | ā
ā Test Integration button on the row runs every API the connector depends on |
| OAuth | ā ā global app credentials live on this row |
Configs ā
The Slack row holds credentials for both the connector ops and the Slack channel. The exact field set is form-rendered from the Configs struct ā all fields are always visible in the admin form:
| Field | Type | Purpose |
|---|---|---|
AuthMode | dropdown | bot_token (default) or user_token ā selects which token the runtime reads when making API calls. |
BotToken | secret | xoxb-⦠token used by every connector op when AuthMode=bot_token. |
UserToken | secret | xoxp-⦠user OAuth token, used when AuthMode=user_token. Set after the operator clicks Connect Account when ClientID is configured, or paste manually. |
ClientID | string | Slack OAuth App Client ID. Required to activate the Connect Account button for the user-token OAuth flow. Lives on this instance row, not in a shared server setting. |
ClientSecret | secret | Slack OAuth App Client Secret. Required for the token exchange step of the Connect Account flow. Lives on this instance row. |
OAuth app credentials (ClientID / ClientSecret) are now per-instance ā different Slack connector rows can use different Slack apps. Enable the Connect Account flow by setting both fields and enabling EnableSSO in the Access Policy section.
The Test Integration button at the top of the row runs each API the connector needs in parallel (~5s budget) and reports only failures ā auth.test, users.list, conversations.list, chat.postMessage dry-run, etc. See Channels ā¶ Integration health check for the equivalent on the channel side.
Operations (read) ā
| Op | Input | What it does |
|---|---|---|
list_channels | types, exclude_archived, name_contains, limit, cursor | List channels visible to the bot. Paginated via cursor. |
search_channels | query, limit | Substring search by channel name (case-insensitive). |
get_channel_info | channel | Metadata for one channel ā topic, purpose, creator, created. |
get_channel_history | channel, limit, oldest, latest, cursor | Recent messages. Top-level only ā use get_thread_replies for threaded replies. |
get_thread_replies | channel, ts, limit, cursor | Parent + every reply under a thread. |
list_users | limit, cursor | Workspace members. Email requires users:read.email scope. |
get_user_info | user | Profile for one user ID. |
get_user_by_email | email | Resolve a workspace user by email. Pair with channel:slack.open_dm to DM them. |
get_permalink | channel, ts | Permalink URL for a message ts. |
All read ops are connector.Op (non-destructive).
Operations (write ā destructive, opt-in per row) ā
| Op | Input | What it does |
|---|---|---|
send_message | channel, text, blocks, thread_ts, reply_broadcast, unfurl_links, mrkdwn | Post a message to a channel / DM / thread. |
send_ephemeral | channel, user, text, blocks, thread_ts | Visible only to user. |
update_message | channel, ts, text, blocks | Edit an existing message. |
delete_message | channel, ts | Delete by ts. |
add_reaction | channel, ts, name | Emoji reaction (name without colons). |
remove_reaction | channel, ts, name | Remove a reaction. |
Every write op is connector.OpDestructive ā enabled by default on every new row. Admins can disable individual ops per (row, op) at /manager/connectors/slack/{id}. The MCP layer appends a destructive warning to these ops' descriptions so the LLM confirms before calling.
Quirks worth knowing ā
channelaccepts a channel ID (Cā¦), DM ID (Dā¦), user ID (Uā¦ā auto-opens DM), or#name(only resolves when the bot is already a member).thread_tsis always the parent message ts ā replying to a reply still uses the root ts.get_channel_historyreturns only top-level messages. Walk thread replies withget_thread_repliesagainst each parentts.oldest/latestare Slack ts strings ("1700000000.000100"), not RFC3339.- Pagination uses
cursorfromresponse_metadata.next_cursor;limitcaps the per-call page (max 1000), not the total. - Email lookup requires the
users:read.emailscope; without it theprofile.emailfield is empty inlist_usersoutput. - Rate limit: 1 msg/sec per channel for
send_message. Bursts get queued then 429. blocksoverridestextfor rendering, but Slack still wants non-emptytextfor the notification preview ā always set both.
Workflow integration ā
Slack ops are a common right-hand side of a workflow connector node:
- id: notify
type: connector
module: slack
op: send_message
arg_modes:
text: expression
args:
channel: "#alerts"
text: "New ticket from {{.Node.trigger.payload.user}}: {{.Node.trigger.payload.text}}"Channel-node actions ā
For Slack actions that aren't 1:1 with a plain Web API call ā modals, ephemerals, App Home, slash-command replies ā use a channel node (channel: slack) and pick the action with op. These are wired to the live Slack API and complement the connector ops above.
op | Destructive | Inputs | Returns |
|---|---|---|---|
send_message | no | channel, text, thread_ts? | ts, channel |
reply_thread | no | channel, thread, text | ts |
send_dm | no | user, text | ts, channel |
send_ephemeral | no | channel, user, text | ts |
update_message | yes | channel, ts, text | ts |
react | no | channel, message_ts, emoji | ok |
open_modal | no | trigger_id, view | view_id, view_hash |
update_modal | no | view_id, view, view_hash? | view_id |
push_modal | no | trigger_id, view | view_id, view_hash |
open_dm | no | user | channel |
publish_home | no | user_id, view | view_id |
respond_url | no | response_url, text?, replace_original?, delete_original?, response_type? | ok |
- id: ask
type: channel
channel: slack
op: open_modal
args:
trigger_id: "{{.Node.trigger.payload.trigger_id}}"
view: "{{.Node.build_view.result}}"Notes:
viewis a Slack Block Kit view JSON (string or object). Build it with atransformnode upstream.open_modal/push_modalneed a freshtrigger_idā Slack expires it ~3s after the interaction, so the path from inbound event to the modal op must be short.reactis idempotent ā re-adding an existing emoji is a no-op, not an error.
See Workflows ā¶ channel node.
See also ā
- Channels ā¶ Slack ā inbound side (events, access control, picker, hot-reload).
- Workflows ā using these ops + channel actions in a DAG.
- HTTP / REST ā fallback for any Slack Web API call wick hasn't typed yet.