Go to Slack

Developing for Enterprise Grid

New to building on Slack? We're happy you're here. The first step is to create an app — it'll only take a moment. Get started.

Slack Enterprise Grid allows large organizations to collaborate across many teams. While many apps, integrations, and bots will work as expected on Enterprise, there are enough new behaviors, conditions, nuances, and opportunity to warrant reviewing your app for full compatibility.

An Enterprise organization

What is Enterprise Grid?

Enterprise Grid is a "network" of two or more Slack team instances. Each Slack team has its own team ID, its own directory of team members, its own channels, conversations, files, and zeitgeist. For the most part, each team behaves and acts functions as you're used to.

The biggest delta for your app, integration, bot, or mind to handle is that some channels and conversations can be shared between multiple teams within the same Enterprise organization.

As with typical Slack teams, teams on an Enterprise Grid have their very own application installations and custom integrations.

What if I do nothing?

A Slack team is a Slack team is a Slack team. And a team is a workspace. A place where work happens. It's all very serious.

If your app or integration is never installed on an enterprise organization's team, you won't encounter any of the particularities detailed in this document.

However, if you have the good fortune to be installed by one or more Enterprise Grid teams, or a team you're already installed on becomes part of one:

🙃 Your app might not know what to do with messages and users originating from shared channels. It's novel.

🗿 Your app may have trouble dealing with object IDs beginning with atypical characters, like user IDs starting with W.

👯 Your bot could blindly reply multiple times to messages, not recognizing a unique message delivery scenario introduced by shared channels.

🐋 Teams on Enterprise Grid are often large and some API responses, like channel memberships, can grow immense. Your app might need to change how it digests information.

You never know when a team on Enterprise Grid will install your app. Without making a few tweaks, your new users may notice these (and other) quirks in your app's behavior.

Surface area

Working with Enterprise Grid means that no matter what your app does or which APIs it uses, it's more likely to encounter an enterprise-related quirk.

Bot users are more likely than other integration points to require additional work for exemplary performance on Enterprise Grid teams.

We recommend reviewing your app's current assumptions, operations, and configuration. Maybe even if you aren't planning to work with Enterprise Grid.

Read on if:

  • you are building custom integrations or Slack apps for your own Enterprise Grid team
  • you're building Slack apps for other Enterprise Grid teams to collaborate with
  • you're readying Enterprise Grid support for tools, libraries, or frameworks

Really, if you're using any aspect of the platform at all and want to support Enterprise Grid — this guide is for you.

Enterprise Grid containment grid

Here's one way of looking at the kind of work involved in supporting Enterprise Grid teams.

"If there's a steady paycheck in it, I'll believe anything you say."

API Support strategies
All slack apps & custom integrations
  • Support user IDs beginning with W
  • Support users from other teams within the same Enterprise Grid using your application features
RTM
  • Support events & messages containing user IDs beginning with W
  • Support users from other teams in shared channels
  • Support duplicate message deliveries in shared channels when installed on multiple teams
Events API
  • Support events & messages containing user IDs beginning with W
  • Support users from other teams in shared channels
Web API
  • Support user ID fields containing user IDs beginning with W
  • Support users from other teams in shared channels
  • Use users.info to retrieve additional information on cross-team user IDs not found in users.list
Incoming webhooks
  • When using webhooks exclusively, no special Enterprise Grid considerations should be necessary.
Slash commands
  • Turn on entity resolution for mentioned users, allowing you to identify them by ID
  • Support user ID fields containing user IDs beginning with W
  • Slash commands are only invoked by users belonging to the team it is installed in
Message buttons
  • Handle action invocations by users from other teams
  • Support user ID fields containing user IDs beginning with W

Identifying Enterprise Grid teams

Teams don't always start out part of an Enterprise. Thanks to mergers, acquisitions, and simple evolution a team may become part of an Enterprise Grid.

And just as a user that's part of a team becomes part of its team's Enterprise Grid, that user who was once a duplicative singleton at team A and team B, with no way to reconcile shared identity, becomes a cross-referenced entity you can recognize consistently across teams.

There are two ways to determine whether the team you're working with is part of an Enterprise.

Look for enterprise IDs

The easiest way to determine whether your app is dealing with a team that's part of an enterprise organization is by observing enterprise_id attributes scattered through object types in Web API methods & events.

Look for enterprise_id in auth.test, the most conspicuous and eternally useful location it surfaces in.

We recommend using this method after acquiring an OAuth token. You'll want to poll it regularly for all teams and workspaces you service, as teams migrate to Enterprise Grid.

An enterprise_id is constant and unique.

Other places to find enterprise_id (often paired with enterprise_name):

If you store data, you'll want to warehouse enterprise_id. Just as you'd never store information about a user or their data without tying it to a team first, it's best to associate Enterprise Grid workspace data with its enterprise_id.

Use enterprise_id prodigiously in your queries. 

Interpret other arcane signals

If you're working with a custom integration instead of a Slack app, you may have a more difficult time understanding whether you're working with an Enterprise Grid team or not.

Though you yourself should know whether the team you're part of and building an integration for is on Enterprise Grid, there are fewer programmatic signals to interpret and off-the-shelf code may not know how to handle certain situations like shared channels.

How do I become Enterprise-ready?

The first step is to recognize that you're working with an Enterprise Grid workspace. Then you just need to teach your app a few new tricks:

  1. Look for enterprise_id in auth.test and channels.info. Consider it a strong signal you may encounter shared channels, novel IDs, and different API responses when working with this team, and it might be related to another team that has or will install your app. You'll want to store it along side other data about its teams.
  2. Prepare for your integration or bot to be used in shared channels: you'll encounter messages authored by users not belonging to the team your app is installed in. Use users.info to look up information about user IDs belonging to a different team on the Enterprise Grid, ad hoc as needed.
  3. Anticipate receiving & handling semi-duplicate messages when connected on behalf of multiple teams within the same Enterprise Grid. If your bot is installed on 3 teams in an Enterprise you're invited to a shared channel with members from all 3 teams, you'll receive a message three times, once for each team. Your bot should only respond to one of the message deliveries.
  4. If your app is part of the directory, you'll want to let us know that you've prepared for Enterprise Grid teams. We'll do some additional testing and give you feedback on anything that could be improved.

Handling unknown users gracefully

If your app or integration is used as part of a shared channel, it will encounter users it is not aware of.

Typically, apps build their "known universe" of users by consuming the users collection in rtm.start or by using users.list. You won't find users from other teams in the payloads of these methods.

To find users from another team on an Enterprise Grid, use users.info and query by their "foreign" user ID.

Of course, many apps need only a user's ID and don't need additional information to operate.

Messages, interactions, & slash command invocations involving unknown users

Not only will you encounter user IDs belonging to other teams, but also the artifacts of their existence and team cross-pollination:

  • Messages will be authored by these users, and if they fall within your bot or app's functionality, you should respond as you normally would. Messages may also mention users on other teams.
  • If your messages contain interactive elements, you may receive invoked actions from these users, and should handle them just as you would a "native" team user
  • If you provide a slash command, you'll want to handle mentions of members from other team's. We strongly recommend turning on entity resolution so you can resolve mentioned users.

Careful with those messages, bot

If your application is installed by multiple teams 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 teams 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 team the message originates from.

RTM considerations

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

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

One way to handle duplicate messages is to make one of the teams 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.join message when your bot is added to a shared channel. The metadata included in this event will tell you which teams are part of the shared channel.

Of the teams 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 team's RTM should respond. Alternatively, you can ignore all messages coming from a team that is not the same as the team your app is installed on. While simpler to implement, it will prevent users on teams 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 teams 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 team across the 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 team within the Enterprise Grid that the message originates from.

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

Sweet relief delivered by the Events API

Congratulations! If you're using the Events API, you do not have to worry about duplicate messages from shared channels. We'll only send one event regardless of how many teams within the organization your app is installed on.

Look for the authed_teams node that arrives as part of the "wrapper" around delivered events. It contains a collection of team IDs within an organization that this delivery is on behalf of.

Here's an example payload of a shared channel's message being delivered on behalf of multiple teams:

{
    "token": "ecto1EZslimer",
    "team_id": "T3MQV36V7",
    "api_app_id": "A3MERH269",
    "event": {
        "type": "message",
        "user": "W3M73E5LM",
        "text": "Now _we_ are the real ghostbusters!",
        "team": "T3MQV36V7",
        "source_team": "T3MQV36V7",
        "user_team": "T3MQV36V7",
        "user_profile": {
            "avatar_hash": "g56821b98743",
            "image_72": "https://...png",
            "first_name": "Patty",
            "real_name": "Patty Tolan",
            "name": "patty.tolan"
        },
        "ts": "1485371714.000002",
        "channel": "C3W4KDKCL",
        "event_ts": "1485371714.000002"
    },
    "type": "event_callback",
    "authed_users": [
        "W3MBP5799"
    ],
    "authed_teams": [
        "T3NHPD4J0",
        "T3MQV36V7"
    ]
}

In this example, the application is installed on two teams in an Enterprise and is party to a conversation in a shared channel.

The message originates from team T3MQV36V7 and is delivered as a single event for both teams, including T3NHPD4J0.

Shared channels

Teams on an Enterprise Grid may contain channels shared between multiple teams in an organization. This means that it may contain members from teams your app is not installed on and that you don't have direct access to data around.

Determine whether you're working with a shared channel by looking for the is_org_shared attribute and whether it is set to true. You'll find this field on channel objects returned by channels.list and channels.info.

If you're only installed on one of the teams involved, all channel members belonging to the other team are "foreign" to your app.

Use users.info to look up user information about a remote team's shared user.

Crossing the streams

If your app is installed on multiple teams within an enterprise organization, you may want to utilize that position effectively by grouping data by enterprise_id and using the new enterprise user IDs as your primary source of truth.

This way you'll easily cross work streams between more than one team without exploding at the speed of light.

A translation layer for the Enterprise Grid

We really wanted to offer an in-place translation layer, complete with ever-present fields mapping legacy user IDs to cross-team IDs. Unfortunately, this feat was not technically possible to provide, though we documented it here and many of you noticed it didn't actually exist.

Ideally we'd like you to start using the new W-based user IDs as soon as possible. For this reason, we suggest doing a one-time data migration by getting the new ID for every user currently existing in your database and merge any duplicate users you discover.

In the future, we'll provide additional tooling to bulk convert user IDs you have stored with their cross-team counterparts. For now, you'll need to use users.info to convert between U-based and W-based IDs.

So if you ever hear about this mysterious user_id_mapping_old_to_new field but wonder what is it, where is it, and why is it not here? — now you know.

User object changes

You'll find user objects on Enterprise Grid teams now have an enterprise_user attribute, containing a hash of additional context about the user in the larger Enterprise Grid organization.

You'll find:

  • id - this user's ID, which might start with U or W
  • enterprise_id - the unique ID for this particular Enterprise Grid organization
  • enterprise_name - the name of this umbrella organization
  • is_admin - a boolean value indicating whether this user administers this Enterprise
  • is_owner - a boolean value indicating whether this user is the owner of this Enterprise
  • teams - an array of team IDs within the containing enterprise organization that this user belongs to

User IDs

Within an Enterprise Grid, all users have an underlying cross-team ID beginning with the letter W. If a team existed before the team became part of an Enterprise Grid, users created before that transition also have a more local team-specific ID beginning with U.

We'll automatically convert references to a user's Enterprise ID to their legacy team-specific ID. This allows your app to remain backwards compatible for users you've identified on a team before the days of Enterprise Grid.

You'll still encounter users with IDs beginning with W: these users were created after a team became part of an Enterprise and have no legacy ID.

Building internal integrations

It's easy and secure to build Slack apps for your teams only. As with all Slack apps, installation and ownership of apps is on a workspace/team-basis. Internal integrations that are locked to a single team cannot be installed across an Enterprise Grid.

Find out more in our internal integrations documentation.

Developing for, but not necessarily against, Enterprise

The easiest way to test your app or integration against an Enterprise Grid environment is for an Enterprise Grid team to install it.

Of course, not every app will be installed on an Enterprise, though we hope most strive to be.

Implement the additional logic outlined in this document to prepare your app for Enterprise Grid customers. When you've finished, indicate your readiness as part of the the Slack App Directory review process.

Do you need a test instance?

After combing through your code and database structures, you've made some tweaks here and there, and you want to test if everything actually works against Enterprise Grid. Fair enough, that's reasonable.

We hope following the recommendations of this documentation makes you confident your app will work in Enterprise environments.

We have a limited number of test instances available for complex apps and bots targeting Enterprise Grid teams that are concerned about the impact on a large number of users. If that sounds like you, please fill out this form to signal your interest to us.

It shouldn't be necessary to use a test instance if your app makes minimal use of user data or utilizes only simple incoming webhooks.

When teams migrate to Enterprise Grid

In most cases, an Enterprise Grid is formed by combining multiple independent Slack teams together. During this period of time where data is migrated and made compatible with Grid's organization structures, Web API calls, RTM API connections, Events API, and other platform interactions may be unavailable, both for users and your applications.

On the Web API, you may encounter the team_added_to_org error during this time.

Migration time varies depending team-to-team and organization-to-organization. Practice an exponential backoff strategy when encountering errors to help manage these periods by attempting connections at incrementally increasing rates: 1 second to 3 seconds to 10 seconds to 30 seconds to 1 minute, and so on.

To best plan for migrations, subscribe to grid-related app events as part of the Events API. Using these events, your app can pause and resume activity as appropriate.

Glossary

Though for the most part Enterprise Grid Slack teams are business as usual, there are enough new concepts introduced to warrant brief review:

  • Enterprise organization - An entity introduced with Enterprise Grid to house multiple Slack team workspaces. When a customer is using Enterprise Grid, all users and their direct messages are stored at the organization level.
  • Enterprise organization user - A Enterprise Grid user. They have the same identity and profile across all team workspaces within an organization.
  • Enterprise user ID - A user ID beginning with W that represents a user across one or more teams within an Enterprise.
  • Legacy user ID - Also known as a "team user ID", these are the User IDs you've come to know and love in Slack and are unique only to a team. They begin with U.
  • Shared channel - A channel shared between two or more teams within an organization.
  • Translation layer - A translating service that transparently converts new organization-based user IDs to legacy team user IDs, allowing apps to migrate data.
  • Workspace - Where a Slack team works, especially in context to other teams within an Enterprise Grid. Each team has a workspace. For many of you, the team is the workspace.

Related reading:

  • Announcement about user IDs - Back in August 2016, we announced that user IDs may sometimes begin with the letter W and that it vaguely had something to do with our upcoming enterprise product. W is here to stay.