Go to Slack

Supporting and developing apps in Enterprise Grid

The rundown
Read this if:You're still experimenting, prototyping, and exploring.
Read next: Enterprise Migration

For a brief introduction to Enterprise Grid, start here.

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 workspaces.

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 Slack apps for your own Enterprise Grid workspace
  • you're building Slack apps for other Enterprise Grid workspaces 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.

Identifying Enterprise Grid workspaces

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

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

There are two ways to determine whether the workspace 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 workspace 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 workspaces you service, as workspaces 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 workspace first, it's best to associate Enterprise Grid workspace data with its enterprise_id.

Use enterprise_id prodigiously in your queries. 

If your app is installed on multiple workspaces 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 workspace without exploding at the speed of light.

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 workspace or not.

Though you yourself should know whether the workspace 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.

Support Strategies for Enterprise Grid

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

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

API Support strategies
All Slack apps
  • Support global user IDs
  • Support users from other workspaces within the same Enterprise Grid using your application features
RTM
Events API
Web API
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 global user IDs
  • Slash commands are only invoked by users belonging to the workspace it is installed in
Message buttons
  • Handle action invocations by users from other workspaces
  • Support user ID fields containing global user IDs

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 workspace, and it might be related to another workspace that has or will install your app. You'll want to store it along side other data about its workspaces.
  2. Prepare for your integration or bot to be used in shared channels: you'll encounter messages authored by users not belonging to the workspace your app is installed in. Use users.info to look up information about user IDs belonging to a different workspace on the Enterprise Grid, ad hoc as needed.
  3. Anticipate receiving & handling semi-duplicate messages when connected on behalf of multiple workspaces within the same Enterprise Grid. If your bot is installed on 3 workspaces in an Enterprise you're invited to a shared channel with members from all 3 workspaces, you'll receive a message three times, once for each workspace. 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 workspaces. 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 workspaces in the payloads of these methods.

To find users from another workspace 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 workspaces, but also the artifacts of their existence and workspace 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 workspaces.
  • If your messages contain interactive elements, you may receive invoked actions from these users, and should handle them just as you would a "native" workspace user
  • If you provide a slash command, you'll want to handle mentions of members from other workspace'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 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.

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 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. While simpler to implement, it 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 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.

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 workspaces 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 workspaces:

{
    "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 workspaces 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.

When workspaces migrate to Enterprise Grid

In most cases, an Enterprise Grid is formed by combining multiple independent Slack workspaces 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.

If your app has disabled the translation layer, use migration.exchange to receive global user IDs for any of your stored local user IDs.

User object changes

You'll find user objects on Enterprise Grid workspaces 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 (workspace) IDs within the containing enterprise organization that this user belongs to

User IDs

When a pre-existing workspace becomes part of an Enterprise Grid, each existing user is assigned a new "global" or cross-workspace user ID representing that user across all workspaces in the grid. This ID will begin with the letter W. If a workspace existed before it became part of an Enterprise Grid, users created in that workspace before the 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 workspace before the days of Enterprise Grid.

You can turn this translation layer off and on. By turning it off, the API will serve your app only global user IDs.

We recommend disabling the translation layer and using migration.exchange to convert any stored local user IDs into global user IDs, so your app can transact exclusively with global user IDs for all Enterprise Grid organizations.

You'll still encounter users with IDs beginning with W: these users were created after a workspace became part of an Enterprise. They will not have a legacy ID.

How global and local IDs differ

In typical workspaces, users have a single user ID local to that workspace.

When workspaces bind together with others to form an Enterprise Grid, the existing users are granted a global user ID to complement their local user ID.

From then on, every new user is granted only a global user ID.

All users in an enterprise grid have a global user ID, a string beginning with W. Global user IDs are a unique, consistent identifier for a single user across multiple workspaces in an Enterprise Grid. It travels with users.

Local user IDs are unique only to a single workspace and begin with U. With translation turned off, your app will no longer receive local user IDs. Users created after an enterprise migration do not have local user IDs.

Toggling the translation layer

Disable the translation layer by navigating to application management for your app and finding the User ID Translation panel.

The translation layer's toggle

If you need to update local user IDs you've stored in a database or in other ways, first use migration.exchange with the relevant user or bot tokens to receive global user IDs in bulk.

Building internal integrations

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

Find out more in our internal integrations documentation.

Requesting a sandbox

Building properly for shared channels or enterprise grid requires experiencing the unique constraints and opportunities yourself.

If you don't already have access to a grid of workspaces, please complete the form below to request a sandbox. We'll get back to you as soon as possible.

Request a sandbox