Legacy: Real Time Messaging API

The legacy Real Time Messaging API is a WebSocket-based API that allows you to receive events from Slack in real time and send messages as users, including bot users. It's sometimes referred to as the "RTM API".

It was once the basis for all Slack clients and once commonly used with bot users to create helper bots for your workspace.

We recommend having events pushed to you instead, using the HTTP-based Events API. Most of the RTM API's supported event types are also supported by the Events API. If you really like WebSockets, Socket Mode for apps delivers event subscriptions over WebSockets instead.

Many workspace administrators will not allow apps and integrations using the RTM API due to the overly permissive permission scopes required for their operation. Slack apps allow you to subscribe to events and request permissions for only the data your app truly needs to operate.

Notices

This API is ancient and the ways to access it have grown more limited over time. Please excuse this litany of warnings and calls to action below.

The platform has evolved for nearly a decade and we're looking to consolidate some of our oldest ways to integrate and extend Slack.

Slack apps may not use any Real Time Messaging API method. Create a classic app and use the V1 Oauth flow to use RTM.

For most applications, Socket Mode is a better way to communicate with Slack.

Basics

To begin a RTM session make an authenticated call to the rtm.connect API method. This provides an initial set of workspace metadata and a message server WebSocket URL. Once you have connected to the message server it will provide a stream of events, including both messages and updates to the current state of the workspace. This allows a client to easily maintain a synchronized local copy of all workspace data and messages.

The Websocket URLs provided by rtm.connect are single-use and are only valid for 30 seconds, so make sure to connect quickly. If you connect successfully the first event received will be a hello:

{
	"type": "hello"
}

This will be followed by any events that occurred between the call to rtm.connect and the connection to the message server. If you're reconnecting after a network problem this initial set of events may include a response to the last message sent on a previous connection (with a reply_to) so a client can confirm that message was received.

If there was a problem connecting an error will be returned, including a descriptive error message:

{
	"type": "error",
	"error": {
		"code": 1,
		"msg": "Socket URL has expired"
	}
}

Events

Almost everything that happens in Slack will result in an event being sent to all connected clients. A common event is a message sent from a user:

{
	"type": "message",
	"ts": "1358878749.000002",
	"user": "U123ABC456",
	"text": "Hello"
}

Every event has a type property which describes the type of event. Our servers currently send the following event types:

EventDescriptionWorks with
accounts_changed

The list of accounts a user is signed into has changed

RTM
app_deleted

User has deleted an app

Events
app_home_opened

User clicked into your App Home

Events
app_installed

User has installed an app

Events
app_mention

Subscribe to only the message events that mention your app or bot

Events
app_rate_limited

Indicates your app's event subscriptions are being rate limited

Events
app_requested

User requested an app

Events
app_uninstalled

Your Slack app was uninstalled.

Events
app_uninstalled_team

User has uninstalled an app

Events
assistant_thread_context_changed

The context changed while an AI assistant thread was visible

Events
assistant_thread_started

An AI assistant thread was started

Events
bot_added

A bot user was added

RTM
bot_changed

A bot user was changed

RTM
call_rejected

A Call was rejected

Events
channel_archive

A channel was archived

RTMEvents
channel_created

A channel was created

RTMEvents
channel_deleted

A channel was deleted

RTMEvents
channel_history_changed

Bulk updates were made to a channel's history

RTMEvents
channel_id_changed

A channel ID changed

Events
channel_joined

You joined a channel

RTM
channel_left

You left a channel

RTMEvents
channel_marked

Your channel read marker was updated

RTM
channel_rename

A channel was renamed

RTMEvents
channel_shared

A channel has been shared with an external workspace

Events
channel_unarchive

A channel was unarchived

RTMEvents
channel_unshared

A channel has been unshared with an external workspace

Events
commands_changed

A slash command has been added or changed

RTM
dnd_updated

Do not Disturb settings changed for the current user

RTMEvents
dnd_updated_user

Do not Disturb settings changed for a member

RTMEvents
email_domain_changed

The workspace email domain has changed

RTMEvents
emoji_changed

A custom emoji has been added or changed

RTMEvents
external_org_migration_finished

An enterprise grid migration has finished on an external workspace.

RTM
external_org_migration_started

An enterprise grid migration has started on an external workspace.

RTM
file_change

A file was changed

RTMEvents
file_comment_added

A file comment was added

RTMEvents
file_comment_deleted

A file comment was deleted

RTMEvents
file_comment_edited

A file comment was edited

RTMEvents
file_created

A file was created

RTMEvents
file_deleted

A file was deleted

RTMEvents
file_public

A file was made public

RTMEvents
file_shared

A file was shared

RTMEvents
file_unshared

A file was unshared

RTMEvents
function_executed

Your app function is executed as a step in a workflow

Events
goodbye

The server intends to close the connection soon.

RTM
grid_migration_finished

An enterprise grid migration has finished on this workspace.

Events
grid_migration_started

An enterprise grid migration has started on this workspace.

Events
group_archive

A private channel was archived

RTMEvents
group_close

You closed a private channel

RTMEvents
group_deleted

A private channel was deleted

RTMEvents
group_history_changed

Bulk updates were made to a private channel's history

RTMEvents
group_joined

You joined a private channel

RTM
group_left

You left a private channel

RTMEvents
group_marked

A private channel read marker was updated

RTM
group_open

You created a group DM

RTMEvents
group_rename

A private channel was renamed

RTMEvents
group_unarchive

A private channel was unarchived

RTMEvents
hello

The client has successfully connected to the server

RTM
im_close

You closed a DM

RTMEvents
im_created

A DM was created

RTMEvents
im_history_changed

Bulk updates were made to a DM's history

RTMEvents
im_marked

A direct message read marker was updated

RTM
im_open

You opened a DM

RTMEvents
invite_requested

User requested an invite

Events
link_shared

A message was posted containing one or more links relevant to your application

Events
manual_presence_change

You manually updated your presence

RTM
member_joined_channel

A user joined a public channel, private channel or MPDM.

RTMEvents
member_left_channel

A user left a public or private channel

RTMEvents
message

A message was sent to a channel

RTMEvents
message.app_home

A user sent a message to your Slack app

Events
message.channels

A message was posted to a channel

Events
message.groups

A message was posted to a private channel

Events
message.im

A message was posted in a direct message channel

Events
message.mpim

A message was posted in a multiparty direct message channel

Events
message_metadata_deleted

Message metadata was deleted

Events
message_metadata_posted

Message metadata was posted

Events
message_metadata_updated

Message metadata was updated

Events
pin_added

A pin was added to a channel

RTMEvents
pin_removed

A pin was removed from a channel

RTMEvents
pref_change

You have updated your preferences

RTM
presence_change

A member's presence changed

RTM
presence_query

Determine the current presence status for a list of users

RTM
presence_sub

Subscribe to presence events for the specified users

RTM
reaction_added

A member has added an emoji reaction to an item

RTMEvents
reaction_removed

A member removed an emoji reaction

RTMEvents
reconnect_url

Experimental

RTM
resources_added

Access to a set of resources was granted for your app

Events
resources_removed

Access to a set of resources was removed for your app

Events
scope_denied

OAuth scopes were denied to your app

Events
scope_granted

OAuth scopes were granted to your app

Events
shared_channel_invite_accepted

A shared channel invite was accepted

Events
shared_channel_invite_approved

A shared channel invite was approved

Events
shared_channel_invite_declined

A shared channel invite was declined

Events
shared_channel_invite_received

A shared channel invite was sent to a Slack user

RTMEvents
shared_channel_invite_requested

A shared channel invite was requested

Events
star_added

A member has saved an item for later or starred an item

RTMEvents
star_removed

A member has removed an item saved for later or starred an item

RTMEvents
subteam_created

A User Group has been added to the workspace

RTMEvents
subteam_members_changed

The membership of an existing User Group has changed

RTMEvents
subteam_self_added

You have been added to a User Group

RTMEvents
subteam_self_removed

You have been removed from a User Group

RTMEvents
subteam_updated

An existing User Group has been updated or its members changed

RTMEvents
team_access_granted

Access to a set of teams was granted to your org app

Events
team_access_revoked

Access to a set of teams was revoked from your org app

Events
team_domain_change

The workspace domain has changed

RTMEvents
team_join

A new member has joined

RTMEvents
team_migration_started

The workspace is being migrated between servers

RTM
team_plan_change

The account billing plan has changed

RTM
team_pref_change

A preference has been updated

RTM
team_profile_change

The workspace profile fields have been updated

RTM
team_profile_delete

The workspace profile fields have been deleted

RTM
team_profile_reorder

The workspace profile fields have been reordered

RTM
team_rename

The workspace name has changed

RTMEvents
tokens_revoked

API tokens for your app were revoked.

Events
url_verification

Verifies ownership of an Events API Request URL

Events
user_change

A member's data has changed

RTMEvents
user_huddle_changed

A user's huddle status has changed

RTMEvents
user_profile_changed

A user's profile data has changed

RTMEvents
user_resource_denied

User resource was denied to your app

Events
user_resource_granted

User resource was granted to your app

Events
user_resource_removed

User resource was removed from your app

Events
user_status_changed

A user's status has changed

RTMEvents
user_typing

A channel member is typing a message

RTM
workflow_deleted

A workflow that contains a step supported by your app was deleted

Events
workflow_published

A workflow that contains a step supported by your app was published

Events
workflow_step_deleted

A workflow step supported by your app was removed from a workflow

Events
workflow_step_execute

A workflow step supported by your app should execute

Events
workflow_unpublished

A workflow that contains a step supported by your app was unpublished

Events

New event types will be added in the future, clients should be able to handle unexpected event types.

Sending messages

You can send a message to Slack by sending JSON over the WebSocket connection.

Every event should have a unique (for that connection) positive integer ID. All replies to that message will include this ID allowing the client to correlate responses with the messages sent; replies may be "out of order" due to the asynchronous nature of the message servers.

Also, as with events sent from the server, each event sent by the client has a string type specifying what the message does — chat messages are of type message.

Channel selection

So to post the text "Hello world" to a channel, you can send this JSON:

{
	"id": 1,
	"type": "message",
	"channel": "C123ABC456",
	"text": "Hello world"
}

You can send a message to a private group or direct message channel in the same way, but using a group ID (C123ABC456) or direct message channel ID (D0123ABC456).

To send a message both as and to the authenticating user, use the correct direct message channel ID for that user. The direct message ID can be found as part of the response to rtm.connect, or by consulting conversations.list.

Formatting messages

The RTM API only supports posting basic messages formatted using our default message formatting mode. It does not support attachments or other message formatting modes. To post a more complex message as a user clients can call the chat.postMessage Web API method with as_user set to true.

User mentions over RTM should use the User ID-based <@U123ABC> syntax:

{
	"id": 2,
	"type": "message",
	"channel": "C123ABC456",
	"text": "Hello <@U123ABC>"
}

Handling responses

Once the JSON has been sent to the server visual clients should immediately display the text in the channel, grayed out or otherwise marked to indicate that it is "pending". At some point after that, usually a few milliseconds later, the server will send a confirmation that the message was received:

{
	"ok": true,
	"reply_to": 1,
	"ts": "1355517523.000005",
	"text": "Hello world"
}

Replies to messages sent by clients will always contain two properties: a boolean ok indicating whether they succeeded and an integer reply_to indicating which message they are in response to.

In the case of a reply to a chat message, if successful, the reply will contain the canonical recorded timestamp of the message. All messages within a single channel are guaranteed to have a unique timestamp which is ASCII sortable. Given the precision of the timestamp, clients should treat these timestamps as strings, not floats/doubles. Once a successful reply has been returned, the message in the chat log should no longer be grayed out - it has now been delivered.

Chat message replies also contain the message text, which may vary from the sent message due to URL detection.

Errors

If there is an error processing an event the message server will reply with an error. For example:

{
	"ok": false,
	"reply_to": 1,
	"error": {
		"code": 2,
		"msg": "message text is missing"
	}
}

Typing indicators

Clients can send a typing indicator to indicate that the user is currently writing a message to send to a channel:

{
	"id": 1,
	"type": "typing",
	"channel": "C123ABC456"
}

This can be sent on every key press in the chat input unless one has been sent in the last three seconds. Unless there is an error the server will not send a reply, but it will send a "user_typing" event to all workspace members in the channel.

Presence

User and bot user presence on the RTM API is complicated enough to warrant an entire document. Learn all about presence subscriptions and batch presence events here.

RTM API Presence is now only available via subscription.
As of January 2018, presence_change events are not dispatched without presence subscriptions established with presence_sub. Relatedly, current user presence status is no longer communicated in rtm.start. Learn more.

Ping and Pong

Clients should try to quickly detect disconnections, even in idle periods, so that users can easily tell the difference between being disconnected and everyone being quiet. Not all web browsers support the WebSocket ping spec, so the RTM protocol also supports ping/pong messages. When there is no other activity clients should send a ping every few seconds. To send a ping, send the following JSON:

{
	"id": 1234, // ID, see "sending messages" above
	"type": "ping",
	...
}

You can supply any number of extra "flat" arguments (that is: only scalar values, no arrays or objects). These will be included in the pong message that is sent back. For example, a client could include a local timestamp in the ping message so it can calculate round-trip latency:

{
	"id": 1234,
	"type": "ping",
	"time": 1403299273342
}

This will be included in the reply from the server:

{
	"reply_to": 1234,
	"type": "pong",
	"time": 1403299273342
}

Limits

The message server will disconnect any client that sends a message longer than 16 kilobytes. This includes all parts of the message, including JSON syntax, not just the message text. Clients should limit messages sent to channels to 4000 characters, which will always be under 16k bytes even with a message comprised solely of non-BMP Unicode characters at 4 bytes each. If the message is longer a client should prompt to split the message into multiple messages, create a snippet or create a post.

As with all Slack APIs, the RTM API is subject to rate limits. Clients should not send more than one message per second sustained. If you do you may receive an error message or be disconnected.

What's a WebSocket?

WebSockets are a standard way to open a long-lived bi-directional communication channel with a server over TCP. It's the protocol used when connecting to our RTM API. Many contributions from our community support the particulars of connecting to Slack via a WebSocket.

Connecting with rtm.connect vs. rtm.start

There are currently two ways to reserve websocket connections.

rtm.connect concerns itself only with getting your app connected to the RTM API, and only includes limited information about the connecting user and housing workspace.

rtm.start includes not only an entire kitchen sink but an entire kitchen filled with information about the user, its workspace, its channels, its current state in the universe. rtm.start is naturally more difficult to use with Enterprise Grid and other large workspaces.

We strongly recommend using rtm.connect to reserve your websocket connections and use the Web API in tandem to collect all the state information your app needs.

Events API vs. RTM API

Both APIs are useful for different reasons, despite them both serving the same kind of data. Find out why to choose one or the other (or both!) in the Events API FAQ.

Create a classic app

Since Slack apps don't connect to rtm.connect, you'll need to have a classic app to get started.

Discontinuing new classic Slack app creation

You won't be able to create new classic apps or legacy custom integration bot users anymore after June 4, 2024. Learn how this may impact you and your team.

Create a classic app

Using the RTM API on Enterprise Grid

There are additional support actions you'll need to take for the RTM API to properly work with Enterprise Grid.

RTM:

Be careful with messages

If your application is installed by multiple workspaces of an Enterprise Grid and then used in a shared channel, it's possible that your bot will receive multiple RTM events for the same message: one for each of the workspaces you're installed on.

If your bot doesn't de-duplicate the messages by looking at the ts value of each message, you might interpret each one independently and reply to them, adding noise a conversation.

Look for the source_team message field to identify the Enterprise workspace the message originates from.

To help you understand the different scenarios in which you'll receive multiple messages, let's imagine the following situation:

  • We have 3 workspaces in an organization
  • Of the 3 workspaces, your app is installed on Workspace 1 and Workspace 2
  • Your app is not installed on Workspace 3
  • Your bot has been invited to join a shared channel that exists between users from all 3 workspaces.
  • Your app has opened websocket connections for both Workspace 1 and Workspace 2
Condition Result
User from Workspace 1 sends message
  • RTM websocket for Workspace 1 will receive the message as normal.
  • RTM websocket for Workspace 2 will receive the same message with some additional metadata about Workspace 1's user.
User from Workspace 2 sends message
  • RTM websocket for Workspace 2 will receive the message as normal.
  • RTM websocket for Workspace 1 will receive the same message with some additional metadata about Workspace 2's user.
User from Workspace 3 sends message
  • RTM websocket for Workspace 1 will receive the message with some additional metadata.
  • RTM websocket for Workspace 2 will also receive the same message with the same additional metadata.

One way to handle duplicate messages is to make one of the workspaces in the shared channel (that your app is installed on) responsible for handling all messages coming from that shared channel.

To do this, you'll need to listen to the channel_joined event when your bot is added to a shared channel. The metadata included in this event will tell you which workspaces are part of the shared channel.

Of the workspaces in the channel that your app is installed on, you'll want to pick one and save both the channel ID and team ID in your database. From that point on, you can look up the channel ID for every message you need to respond to and determine which workspace's RTM should respond.

Alternatively, you can ignore all messages coming from a workspace that is not the same as the workspace your app is installed on. This will prevent users on workspaces that haven't installed your app from being able to interact with your bot.

Working with direct messages

Direct messages work much like channels: private conversations between two or more individuals spanning multiple workspaces within an Enterprise Grid result in RTM API streaming one message for each of your open websocket connections.

Your app can be the target of a direct message from another workspace across the Enterprise Grid. You never know when a user might want to collaborate with your bot.

These messages will also contain a source_team attribute when perspectively appropriate. The source_team attribute contains the workspace within the Enterprise Grid that the message originates from.

As with channels, when connected to multiple websocket connections on behalf of workspaces in the Enterprise Grid, you can receive redundant message deliveries. They will have the same ts value.

Bits and bobs for the Deno developer
These TypeScript code snippets help you work with canvases, authentication, forms, connectors, and more.