💎 ChatSDK Ruby

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

VariableDescription
SLACK_BOT_TOKENBot User OAuth Token (starts with xoxb-)
SLACK_SIGNING_SECRETSigning Secret from your Slack app settings

Slack App Setup

  1. Go to api.slack.com/apps and create a new app.
  2. Under OAuth & Permissions, add the bot token scopes your app needs:
    • chat:write -- Post messages
    • chat:write.customize -- Post with custom username/icon
    • reactions:write -- Add/remove reactions
    • reactions:read -- Read reactions
    • files:write -- Upload files
    • im:history -- Read DM history
    • channels:history -- Read channel history
    • groups:history -- Read private channel history
  3. Install the app to your workspace and copy the Bot User OAuth Token.
  4. Under Event Subscriptions, enable events and set the Request URL to your webhook endpoint (e.g., https://your-app.com/webhooks/slack).
  5. Subscribe to bot events: app_mention, message.im, reaction_added, reaction_removed.
  6. Under Interactivity & Shortcuts, enable interactivity and set the Request URL to the same webhook endpoint.
  7. Copy the Signing Secret from Basic Information.

Webhook URL

Your webhook endpoint should be:

https://your-domain.com/webhooks/slack

Mount it in your Rack app:

map "/webhooks/slack" do
  run bot.webhooks[:slack]
end

The endpoint handles Slack's URL verification challenge automatically.

Capabilities

The Slack adapter supports all capabilities:

CapabilitySupported
edit_messagesYes
delete_messagesYes
ephemeral_messagesYes
file_uploadsYes
reactionsYes
modalsYes
typing_indicatorYes (declared, no-op)
streaming_editYes
threadsYes
direct_messagesYes
message_historyYes
scheduled_messagesYes

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)
end

Generate 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 response

Returns 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

On this page