Slack Adapter
Full integration with Slack's Web API and Block Kit via the chat_sdk-slack gem.
The Slack adapter (chat_sdk-slack) provides full integration with Slack's Web API and Block Kit.
Installation
# Gemfile
gem "chat_sdk"
gem "chat_sdk-slack"Configuration
require "chat_sdk"
require "chat_sdk/slack"
slack = ChatSDK::Slack::Adapter.new(
bot_token: ENV["SLACK_BOT_TOKEN"], # or set SLACK_BOT_TOKEN env var
signing_secret: ENV["SLACK_SIGNING_SECRET"] # or set SLACK_SIGNING_SECRET env var
)Both parameters fall back to environment variables if not provided.
Environment Variables
| Variable | Description |
|---|---|
SLACK_BOT_TOKEN | Bot User OAuth Token (starts with xoxb-) |
SLACK_SIGNING_SECRET | Signing Secret from your Slack app settings |
Slack App Setup
- Go to api.slack.com/apps and create a new app.
- Under OAuth & Permissions, add the bot token scopes your app needs:
chat:write-- Post messageschat:write.customize-- Post with custom username/iconreactions:write-- Add/remove reactionsreactions:read-- Read reactionsfiles:write-- Upload filesim:history-- Read DM historychannels:history-- Read channel historygroups:history-- Read private channel history
- Install the app to your workspace and copy the Bot User OAuth Token.
- Under Event Subscriptions, enable events and set the Request URL to your webhook endpoint (e.g.,
https://your-app.com/webhooks/slack). - Subscribe to bot events:
app_mention,message.im,reaction_added,reaction_removed. - Under Interactivity & Shortcuts, enable interactivity and set the Request URL to the same webhook endpoint.
- Copy the Signing Secret from Basic Information.
Webhook URL
Your webhook endpoint should be:
https://your-domain.com/webhooks/slackMount it in your Rack app:
map "/webhooks/slack" do
run bot.webhooks[:slack]
endThe endpoint handles Slack's URL verification challenge automatically.
Capabilities
The Slack adapter supports all capabilities:
| Capability | Supported |
|---|---|
edit_messages | Yes |
delete_messages | Yes |
ephemeral_messages | Yes |
file_uploads | Yes |
reactions | Yes |
modals | Yes |
typing_indicator | Yes (declared, no-op) |
streaming_edit | Yes |
threads | Yes |
direct_messages | Yes |
message_history | Yes |
scheduled_messages | Yes |
Direct Client Access
Access the underlying Slack::Web::Client for platform-specific API calls:
slack_adapter = bot.adapter(:slack)
client = slack_adapter.client
# Any slack-ruby-client method
client.users_info(user: "U12345")
client.conversations_list(types: "public_channel")Cards Rendering
Cards built with ChatSDK.card are rendered as Slack Block Kit JSON. The BlockKitRenderer handles the translation automatically.
Mention Formatting
The adapter formats user mentions as Slack-native <@USER_ID> syntax:
slack.mention("U12345") # => "<@U12345>"Message IDs
Slack uses timestamps (ts) as message IDs. The Message#id returned from post and other methods is the ts value (e.g., "1234567890.123456").
Multi-Workspace OAuth
For apps installed across multiple Slack workspaces, pass client_id and client_secret instead of a single bot_token. Use set_state to provide a state store, then implement installation lifecycle callbacks:
slack = ChatSDK::Slack::Adapter.new(
client_id: ENV["SLACK_CLIENT_ID"],
client_secret: ENV["SLACK_CLIENT_SECRET"],
signing_secret: ENV["SLACK_SIGNING_SECRET"]
)
# Wire up a state store (e.g., backed by Redis or your DB)
slack.set_state(my_state_store)
# Persist installation data after OAuth completes
slack.set_installation(team_id) { |token_data| save_to_db(team_id, token_data) }
# Retrieve installation data when handling events from a workspace
slack.get_installation(team_id) { load_from_db(team_id) }
# Remove installation data on uninstall
slack.delete_installation(team_id) { delete_from_db(team_id) }
# Handle the OAuth callback in your Rack app
slack.handle_oauth_callback(rack_request)The adapter resolves the correct per-team Slack::Web::Client automatically when parsing inbound events.
Socket Mode
For local development or firewall-restricted environments, use Socket Mode instead of HTTP webhooks. Requires the optional faye-websocket gem.
# Gemfile
gem "faye-websocket"
# Start listening over WebSocket
slack.start_socket_mode(app_token: ENV["SLACK_APP_TOKEN"]) do |event|
# event is a ChatSDK::Events::Mention, DirectMessage, etc.
handle(event)
endGenerate an app-level token (starting with xapp-) in your Slack app under Basic Information > App-Level Tokens with the connections:write scope.
Assistants API
Control the Slack Assistants thread experience with suggested prompts, status indicators, and titles:
slack.set_suggested_prompts(channel_id: channel, thread_ts: ts,
prompts: [{title: "Summarize", message: "Summarize this thread"}])
slack.set_assistant_status(channel_id: channel, thread_ts: ts,
status: "is thinking...")
slack.set_assistant_title(channel_id: channel, thread_ts: ts,
title: "Support Bot")Home Tab
Publish a Home tab view for a user using Block Kit JSON:
slack.publish_home_view(user_id: "U12345", view: {
type: "home",
blocks: [{ type: "section", text: { type: "mrkdwn", text: "Welcome!" } }]
})Requires the views:publish scope on your Slack app.
Scheduled Messages
Schedule a message to be posted at a future time:
slack.schedule_message(
channel_id: "C12345",
message: "Reminder: standup in 5 minutes!",
post_at: Time.now.to_i + 300
)post_at is a Unix timestamp. The returned Message#id is the scheduled_message_id.
Get User
Look up a Slack user by ID:
author = slack.get_user("U12345")
author.name # => "alice"
author.bot? # => false
author.raw # => full Slack API responseReturns a ChatSDK::Author or nil if the user is not found.
Update Modal
Update an already-open modal view by ID:
slack.update_modal(view_id: "V123ABC", modal: updated_modal)Response URL
Post to a Slack response URL from slash commands or interactive payloads:
slack.send_to_response_url(
response_url: "https://hooks.slack.com/actions/T123/456/respond",
message: "Updated!"
)Fetch Thread Info
Get channel/conversation metadata:
info = slack.fetch_thread(channel_id: "C12345")
info["name"] # => "general"Stop Socket Mode
Cleanly shut down a Socket Mode connection:
slack.stop_socket_mode