No release in over 3 years
Slack Web and RealTime API client.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Development

>= 0
>= 0
>= 0
= 0.35.0
>= 0

Runtime

 Project Readme

Slack Ruby Client

Gem Version Tests Coverage Status

A Ruby client for the Slack Web and Events APIs. Comes with a handy command-line client, too. If you are not familiar with these concepts, you might want to watch this video.

Table of Contents

  • Useful to Me?
  • Stable Release
  • Installation
  • Usage
    • Create a New Bot Integration
    • OAuth Code Grant
    • Using an API Token
    • Global Settings
    • Web Client
      • Web Client Examples
        • Test Auth
        • Send Messages
        • List Channels
        • Upload a File
        • Get Channel Info
        • Get User Info
        • Search for a User
        • Other
      • JSON Arguments
      • Web Client Options
      • Pagination Support
      • Character Encoding
      • Error Handling
        • Slack Errors
        • Rate Limiting
        • Other Errors
    • Events API
      • Configuring Slack::Events
      • Verifying the Request Signature
    • Message Handling
      • Formatting Messages
        • Date and Time Formatting
        • Channel ID Formatting
        • User ID Formatting
        • Group ID Formatting
        • URL Formatting
        • Markdown Formatting
      • Parsing Messages
        • Unescaping Message Content
        • Escaping Message Content
    • Command-Line Client
      • Authenticate with Slack
      • Send a Message
      • Get Channel Info
      • List Users
  • Sponsorship and Enterprise Support
  • History
  • Security
  • Contributing
  • Copyright and License

Useful to Me?

  • This library lets you send messages to Slack via the Web API and facilitates integration with the Events API.
  • To write a complete bot for Slack you need more than this library, and it's much easier to start with slack-ruby-bot-server-events.
  • To respond to slash commands, interactive components, or events at the lowest level, implement a web application using your favorite web framework, and use this library to call the Slack Web API, and to verify that events are coming from Slack.

Stable Release

You're reading the documentation for the next release of slack-ruby-client. Please see the documentation for the last stable release, v2.7.0 unless you're integrating with HEAD. See UPGRADING when upgrading from an older version.

Installation

Add to Gemfile.

gem 'slack-ruby-client'

Run bundle install.

Usage

Create a New Bot Integration

To integrate your bot with Slack, you must first create a new Slack App.

OAuth Code Grant

Once created, go to the app's Basic Info tab and grab the Client ID and Client Secret. You'll need these in order complete an OAuth Workflow. A working sample that starts a local web server and obtains a bot or a user token for your app using OAuth v2 is available in examples/oauth_v2.

Using an API Token

Although OAuth is recommended, you can also generate an API token for your app and use it for some interactions.

Slack.configure do |config|
  config.token = ENV['SLACK_API_TOKEN']
end

This sets a global default token. You can also pass a token into the initializer of both Slack::Web::Client and Slack::RealTime::Client or configure those separately via Slack::Web::Config.configure and Slack::RealTime::Config.configure. The instance token will be used over the client type token over the global default.

Global Settings

The following global settings are supported via Slack.configure.

setting description
token Slack API token.
logger An optional logger, defaults to ::Logger.new(STDOUT) at Logger::WARN level.

Web Client

The Slack Web API allows you to build applications that interact with Slack.

Web Client Examples

Here are some examples of how to use the web client with the Web API.

Test Auth
client = Slack::Web::Client.new
client.auth_test
Send Messages

Send messages with chat_PostMessage.

client.chat_postMessage(channel: '#general', text: 'Hello World', as_user: true)

See a fully working example in examples/hi_web.

List Channels

List channels with conversations_list.

channels = client.conversations_list.channels

general_channel = channels.detect { |c| c.name == 'general' }
Upload a File

Upload files with sequenced API calls.

This library provides a helper method files_upload_v2 that wraps the three separate API calls.

Upload a single file.

client.files_upload_v2(
  # required options
  filename: 'results.pdf', # this is used for the file title, unless a :title option is provided
  content: File.read('/users/me/results.pdf'), # the string contents of the file

  # optional options
  channels: ['C000000', 'C000001'], # channel IDs to share the file in (:channel_id, :channel, or :channels are all supported)
  initial_comment: 'Sharing the Q1 results :tada:', # the message that is included with the file share thread
  snippet_type: 'text', # the type of snippet
  title: 'Q1 Results', # sets the title of the file, overriding the filename
  thread_ts: '1738331487.481469' # specifies a thread to add this file to
)

Upload multiple files.

client.files_upload_v2(
  files: [
    { filename: 'report.pdf', content: File.read('/users/me/report.pdf'), title: 'Monthly Report' },
    { filename: 'data.csv', content: File.read('/users/me/data.csv'), title: 'Raw Data' }
  ],
  channels: ['#general'],
  initial_comment: 'Here are the monthly results!'
)

You can use a channel ID passed as channel_id, a single channel as channel, an array of channel IDs as channels, or a channel name or names (prefixed with #) in files_upload_v2. Lookup by name is not supported by the Slack API and this method called invokes conversations_list in order to locate the channel ID. This invocation can have a cost if you have many Slack channels and is only recommended when you intend to list channels anyway.

Note: This library includes a files_upload method that uses a deprecated endpoint files.upload that will no longer be supported on 3/11/2025.

client.files_upload(
  channels: '#general',
  as_user: true,
  file: Faraday::Multipart::FilePart.new('/path/to/avatar.jpg', 'image/jpeg'),
  title: 'My Avatar',
  filename: 'avatar.jpg',
  initial_comment: 'Attached a selfie.'
)
Get Channel Info

You can use a channel ID or name (prefixed with #) in all functions that take a :channel argument. Lookup by name is not supported by the Slack API and the channels_id method called invokes conversations_list in order to locate the channel ID. This invocation can have a cost if you have many Slack channels. In this scenario, we encourage you to use channel id.

client.conversations_info(channel: 'C04KB5X4D') # calls conversations_info
client.conversations_info(channel: '#general') # calls conversations_list followed by conversations_info
Get User Info

You can use a user ID or name (prefixed with @) in all functions that take a :user argument. Lookup by name is not supported by the Slack API and the users_id method called invokes users_list in order to locate the user ID.

client.users_info(user: 'U092BDCLV') # calls users_info
client.users_info(user: '@dblock') # calls users_list followed by users_info
Search for a User

Constructs an in-memory index of users and searches it. If you want to use this functionality, add the picky gem to your project's Gemfile.

client.users_search(user: 'dblock')
Other

Refer to the Slack Web API Method Reference for the list of all available functions.

JSON Arguments

The Web API expects certain arguments to be sent as JSON-encoded strings. With the client you can pass these args as ruby hashes or arrays and they will be converted automatically to JSON, or you can provide the JSON directly.

# As ruby objects
client.chat_postMessage(
  channel: 'C123456',
  text: 'Hello World',
  blocks: [{type: 'section', text: {type: 'mrkdwn', text: 'Hello World'}}]
)

# As a JSON string
client.chat_postMessage(
  channel: 'C123456',
  text: 'Hello World',
  blocks: JSON.dump([{type: 'section', text: {type: 'mrkdwn', text: 'Hello World'}}])
)
client.chat_postMessage(
  channel: 'C123456',
  text: 'Hello World',
  blocks: '[{"type":"section","text":{"type":"mrkdwn","text":"Hello World"}}]'
)

Web Client Options

You can configure the Web client either globally or via the initializer.

Slack::Web::Client.configure do |config|
  config.user_agent = 'Slack Ruby Client/3.0'
end
client = Slack::Web::Client.new(user_agent: 'Slack Ruby Client/3.0')

The following settings are supported.

setting description
token Slack API token.
user_agent User-agent, defaults to Slack Ruby Client/version.
proxy Optional HTTP proxy.
ca_path Optional SSL certificates path.
ca_file Optional SSL certificates file.
endpoint Slack endpoint, default is https://slack.com/api.
logger Optional Logger instance that logs HTTP requests.
timeout Optional open/read timeout in seconds.
open_timeout Optional connection open timeout in seconds.
default_page_size Optional page size for paginated requests, default is 100.
conversations_id_page_size Optional page size for conversations_list requests made when calculating conversation id from a conversation name, default is nil, which will use the default_page_size.
users_id_page_size Optional page size for users_list requests made when calculating user id from a user name, default is nil, which will use the default_page_size.
default_max_retries Optional number of retries for paginated requests, default is 100.
adapter Optional HTTP adapter to use, defaults to Faraday.default_adapter.

You can also pass request options, including timeout and open_timeout into individual calls.

client.conversations_list(request: { timeout: 180 })

You can control what proxy options are used by modifying the http_proxy environment variable per Net::HTTP's documentation.

Note that Docker on OSX seems to incorrectly set the proxy, causing Faraday::ConnectionFailed, ERROR -- : Failed to open TCP connection to : (getaddrinfo: Name or service not known). You might need to manually unset http_proxy in that case, eg. http_proxy="" bundle exec ruby ./my_bot.rb.

Pagination Support

The Web client natively supports cursor pagination for methods that allow it, such as users_list. Supply a block and the client will make repeated requests adjusting the value of cursor with every response. The default limit is set to 100 and can be adjusted via Slack::Web::Client.config.default_page_size or by passing it directly into the API call.

all_members = []
client.users_list(presence: true, limit: 10) do |response|
  all_members.concat(response.members)
end
all_members # many thousands of team members retrieved 10 at a time

When using cursor pagination the client will automatically pause and then retry the request if it runs into Slack rate limiting. (It will pause according to the Retry-After header in the 429 response before retrying the request.) If it receives too many rate-limited responses in a row it will give up and raise an error. The default number of retries is 100 and can be adjusted via Slack::Web::Client.config.default_max_retries or by passing it directly into the method as max_retries.

You can also proactively avoid rate limiting by adding a pause between every paginated request with the sleep_interval parameter, which is given in seconds.

all_members = []
client.users_list(presence: true, limit: 10, sleep_interval: 5, max_retries: 20) do |response|
  # pauses for 5 seconds between each request
  # gives up after 20 consecutive rate-limited responses
  all_members.concat(response.members)
end
all_members # many thousands of team members retrieved 10 at a time

Character Encoding

Note that Slack expects text to be UTF-8 encoded. If your messages appear with text such as BAD+11 in Slack, check text.encoding and .encode(Encoding::UTF_8) your messages before sending them to Slack.

text = 'characters such as "Ñ", "Á", "É"'
text.encoding
=> #<Encoding:UTF-8>
client.chat_postMessage(channel: '#general', text: text, as_user: true)
# renders 'characters such as "Ñ", "Á", "É"' in Slack

text = text.encode(Encoding::ISO_8859_1)
text.encoding
# => #<Encoding:ISO-8859-1>
client.chat_postMessage(channel: '#general', text: text, as_user: true)
# renders 'characters such as "BAD+11", "", "BAD+9"' in Slack

Error Handling

Slack Errors

If Slack returns an error for the request, then an error will be raised. The error class is specific to the type of error that Slack returns. For instance if Slack returns account_inactive then the error will be Slack::Web::Api::Errors::AccountInactive. This allows you to handle certain types of errors as needed:

rescue Slack::Web::Api::Errors::AccountInactive => e
  # deal with inactive account
end

All of these errors inherit from Slack::Web::Api::Errors::SlackError, so you can handle or silence all errors if necessary:

rescue Slack::Web::Api::Errors::SlackError => e
  # capture all Slack errors
end

If there's a new error type that is not yet known by this library, then it will raise Slack::Web::Api::Errors::SlackError. (Update the Web API if you find that errors are missing — see CONTRIBUTING.)

In all of these cases the error message contains the error code, which is also accessible with slack_error.error. In case of multiple errors, the error message contains the error codes separated by commas, or they are accessible as an array with slack_error.errors. The original response is also accessible using the response attribute. The response_metadata is accessible with slack_error.response_metadata.

Rate Limiting

If you exceed Slack’s rate limits, a Slack::Web::Api::Errors::TooManyRequestsError will be raised instead. (This does not inherit from Slack::Web::Api::Errors::SlackError.)

Other Errors

When Slack is temporarily unavailable a subclass of Slack::Web::Api::Errors::ServerError will be raised and the original Faraday::Error will be accesible via exception.cause. (Starting with 0.18.0 this is no longer a subclass of Slack::Web::Api::Errors::SlackError.)

Specifically Slack::Web::Api::Errors::ParsingError will be raised on non-json response (i.e. 200 OK with Slack unavailable HTML page) and Slack::Web::Api::Errors::HttpRequestError subclasses for connection failures (Slack::Web::Api::Errors::TimeoutError for read/open timeouts & Slack::Web::Api::Errors::UnavailableError for 5xx HTTP responses).

In any other case, a Faraday::ClientError will be raised.

Events API

This library provides limited support for the Slack Events API.

Configuring Slack::Events

You can configure Events support globally.

Slack::Events.configure do |config|
  config.signing_secret = 'secret'
end

The following settings are supported.

setting description
signing_secret Slack signing secret, defaults is ENV['SLACK_SIGNING_SECRET'].
signature_expires_in Signature expiration window in seconds, default is 300.

Verifying the Request Signature

Slack signs its requests using a secret that's unique to your app. Verify incoming HTTP requests as follows.

slack_request = Slack::Events::Request.new(http_request)
slack_request.verify!

To specify secrets on a per-request basis:

Slack::Events::Request.new(
  http_request,
  signing_secret: signing_secret,
  signature_expires_in: signature_expires_in
)

The verify! call may raise Slack::Events::Request::MissingSigningSecret, Slack::Events::Request::InvalidSignature or Slack::Events::Request::TimestampExpired errors.

Message Handling

All text in Slack uses the same system of formatting and escaping: chat messages, direct messages, file comments, etc. Slack::Messages::Formatting provides convenience methods to format and parse messages.

Formatting Messages

Slack::Messages::Formatting provides a number of methods for formatting objects that you can then embed in outgoing messages.

Date and Time Formatting

You can embed a pre-formatted date in a message as a string like any other text, but using Slack's date formatting allows you to display dates based on user preferences for dates and times, incorporating users' local time zones, and optionally using relative values like "yesterday", "today", or "tomorrow" when appropriate.

date = Time.now

# Display date as `YYYY-MM-DD HH:MM:SS`
Slack::Messages::Formatting.date(date)
  # => "<!date^1688150386^{date_num} {time_secs}|2023-06-30 18:39:46 +0000>"

# Specify a different format
# See https://api.slack.com/reference/surfaces/formatting#date-formatting for supported formats
Slack::Messages::Formatting.date(date, format: 'date_long_pretty')
  # => "<!date^1688150386^date_long_pretty|2023-06-30 18:39:46 +0000>"

# Link your timestamp to a fully qualified URL
Slack::Messages::Formatting.date(date, link: 'https://media.giphy.com/media/AcfTF7tyikWyroP0x7/giphy.gif')
  # => "<!date^1688150386^{date_num} {time_secs}^https://media.giphy.com/media/AcfTF7tyikWyroP0x7/giphy.gif|2023-06-30 18:39:46 +0000>"

# Specify custom fallback text to use if the client is unable to process the date
Slack::Messages::Formatting.date(date, text: 'party time!')
  # => "<!date^1688150386^{date_num} {time_secs}|party time!>"
Channel ID Formatting

If you already know the channel name you can just embed it in the message as #some-channel, but if you only have the ID you can embed it using special syntax which Slack will display as the channel name (while respecting channel visibility).

channel_id = 'C0000000001'
Slack::Messages::Formatting.channel_link(channel_id)
  # => "<#C0000000001>"
User ID Formatting

If you already know the user name you can just embed it in the message as @some_username, but if you only have the ID you can embed it using special syntax which Slack will display as the user name.

user_id = 'U0000000001'
Slack::Messages::Formatting.user_link(user_id)
  # => "<@U0000000001>"
Group ID Formatting

If you already know the group name you can just embed it in the message as @some_group, but if you only have the ID you can embed it using special syntax which Slack will display as the group name.

group_id = 'S0000000001'
Slack::Messages::Formatting.group_link(group_id)
  # => "<!subteam^S0000000001>"
URL Formatting

Slack will automatically parse fully qualified URLs in messages, but you need special formatting to embed a link with different text.

text = 'party time'
url = 'https://media.giphy.com/media/AcfTF7tyikWyroP0x7/giphy.gif'
Slack::Messages::Formatting.url_link(text, url)
  # => "<https://media.giphy.com/media/AcfTF7tyikWyroP0x7/giphy.gif|party time>"
Markdown Formatting

Slack uses a mishmash of regular markdown formatting with its own syntax. Some features like headings aren't supported and will be left as-is, but others like bold, strikethrough, and links are converted.

text = """
## A heading
**Bold text**
~~Strikethrough text~~
_Italic text_
[A link](https://example.com)
`code`
"""
Slack::Messages::Formatting.markdown(text)
  # => """
  # ## A heading
  # *Bold text*
  # ~Strikethrough text~
  # _Italic text_
  # <https://example.com|A link>
  # `code`
  # """

Parsing Messages

Slack::Messages::Formatting also provides ways to escape or unescape messages. This comes handy, for example, you want to treat all input to a real time bot as plain text.

Unescaping Message Content
Slack::Messages::Formatting.unescape('Hello &amp; &lt;world&gt;')
  # => 'Hello & <world>'
Slack::Messages::Formatting.unescape('Hey <@U024BE7LH|bob>, did you see my file?')
  # => 'Hey @bob, did you see my file?'
Slack::Messages::Formatting.unescape('Hey <@U02BEFY4U>')
  # => 'Hey @U02BEFY4U'
Slack::Messages::Formatting.unescape('This message contains a URL <http://foo.com/>')
  # => 'This message contains a URL http://foo.com/'
Slack::Messages::Formatting.unescape('So does this one: <http://www.foo.com|www.foo.com>')
  # => 'So does this one: www.foo.com'
Slack::Messages::Formatting.unescape('<mailto:bob@example.com|Bob>')
  # => 'Bob'
Slack::Messages::Formatting.unescape('Hello <@U123|bob>, say hi to <!everyone> in <#C1234|general>')
  # => 'Hello @bob, say hi to @everyone in #general'
Slack::Messages::Formatting.unescape('Hello <@U123|bob> &gt; file.txt')
  # => 'Hello @bob > file.txt'
Slack::Messages::Formatting.unescape('“hello”')
  # => '"hello"'
Slack::Messages::Formatting.unescape('‘hello’')
  # => "'hello'"
Escaping Message Content
Slack::Messages::Formatting.escape('Hello & <world>')
  # => 'Hello &amp; &lt;world&gt;'

Command-Line Client

The slack command-line client returns JSON data from the Slack API.

Authenticate with Slack

$ slack --slack-api-token=[token] auth test
{"ok":true,"url":"...","team":"...","user":"...","team_id":"...","user_id":"..."}

Send a Message

export SLACK_API_TOKEN=...
$ slack chat postMessage --text="hello world" --channel="#general"
{"ok":true,"channel":"...","ts":"...","message":{"text":"hello world","username":"bot","type":"message","subtype":"bot_message","ts":"..."}}

Get Channel Info

$ slack conversations info --channel=#general
{"ok":true,"channel":{"id":"C04KB5X4D","name":"general", ...}}

List Users

Combine with jq, a command-line JSON parser.

$ slack users list | jq '.members | map({(.id): .name})'
[
  {
    "U04KB5WQR": "dblock"
  },
  {
    "U07518DTL": "rubybot"
  }
]

See slack help for a complete command-line reference.

Sponsorship and Enterprise Support

This library was created and has been maintained for a decade by @dblock. Please consider a sponsorship.

Enterprise Support is available as part of a Tidelift Subscription. Click here for more details.

History

This gem is based on slack-ruby-gem, but it more clearly separates the Web and RTM APIs, is more thoroughly tested and is in active development.

Security

See SECURITY.

Contributing

See CONTRIBUTING.

Copyright and License

Copyright (c) 2015-2025, Daniel Doubrovkine, Artsy and Contributors.

This project is licensed under the MIT License.