Using Slack threads as a two-way syncing and notification mechanism

How the Spoke app lets users resolve issues without ever leaving Slack

Kyle Peterson
Slack Platform Blog
5 min readApr 30, 2019

--

Spoke is a modern take on the ticketing system, letting teams like HR and IT serve their fellow colleagues better while letting employees get their questions answered and their issues resolved without ever having to leave Slack (or their favorite platform).

Until recently, employees using Spoke could get notified about updates to their requests in Slack but had to leave Slack and go to the Spoke web app if they wanted to respond to a question. If we could make this functionality available from Slack, we knew that requesters could get most of their questions resolved without ever having to leave Slack and losing their current context. This has been a common ask from most of our customers, and one that we wanted to solve thoughtfully.

Syncing threads and followers across platforms

We came up with various ideas, right from the #z-channel approach all the way to dialogs, but finally settled on an approach using threading. (More about why we choose threading here.)

The screenshot below shows how our requests look on Slack side-by-side with the same request in the Spoke web app. All updates to the request are posted as responses in a thread. The top level message (referred to as the summary message) contains information about the request and why you are getting notified about it (e.g., you could have followed it yourself, you might have made the request, or you could be in charge of resolving it).

Messages created in Slack are automatically added as additional information to the request in Spoke.

With this approach, each Spoke request creates a Slack thread in the direct messages for each user following the request. The hard part of this problem is keeping the two systems in sync. Additionally, there are multiple threads in Slack (one per follower) for each request in Spoke. Since each thread is unique to a user, a request with 3 followers will have 3 threads which all need to be kept in sync. This meant we would need a system that was scalable and performant. Additionally, if we notified each user about every update to a request, this would quickly become noisy, especially if we sent notifications to users about their own messages that they had posted in the Spoke web app. Because of this, we knew that we needed a solution that allowed us to send different updates to each user’s thread.

Technical Overview

Every thread that we create in Slack is represented as an instance of the conversation model. A conversation consists of a combination of two things: a request and a user. The job of a conversation is to maintain a mapping between the update on Spoke and its corresponding Slack message on a thread for the given user.

Note that we maintain the Slack timestamp for each update we post for a request. This lets us do the following things

  • Know what messages have already been sent to Slack and not send a message twice.
  • Delete a message from a Slack thread if it was deleted on Spoke. (We’ve all sent something sensitive on the wrong chat before!)
  • Update messages on Slack if needed.

There are a few kinds of events that a conversation needs to handle:

  • New request created — send a summary message to all followers
  • Update posted to a request via web — send the message to all followers (except the poster)
  • Update posted via Slack — post the message to the Spoke request, and to all followers on Slack
  • Message deleted on Spoke — delete it from all threads on Slack
  • Message deleted on Slack — delete it from Spoke, and all threads it is on, in Slack

To make the system scalable, and keep it fast, we throw every event on a queue. There is one queue which handles tasks that need to be done really fast, and another for maintenance operations which are not as time-sensitive (e.g. updating the summary message to reflect the current status of the request on Spoke. This is not as important as updating a thread with the latest message since users usually have chat like expectations for new messages, but this is not the case with updating a request summary).

In order to make extending this system easier, the conversation model has some methods which handle all interactions with various Slack threads like posting a message on threads for each user, updating request summaries, or deleting a message from each thread. As we continue to add features to Spoke, this design makes it easy to manage our Slack conversations by simply relying on the conversations module.

When we started out our goal was to make simple interactions with Spoke easier without ever having to leave Slack. Users sometimes add screenshots or other images to requests and we knew that this was something that we could and should support. Dealing with files is not hard, but dealing with big files quickly is tricky.

We scoped down the problem and decided to only handle images uploaded via a Slack message in this initial version. Every time a message with a file is added to a Spoke thread, we fetch a copy from Slack and store it in our datastore. Once we have a copy of the file we add the user caption as the message along with the file to the Spoke request. In the case that a user sends an attachment Spoke doesn’t support, Spoke replies with an error message asking the user to use the web app for all non image attachments. We’re currently working on supporting bigger files / non images files using an asynchronous mechanism.

Building a two-way syncing and notification mechanism from Spoke to Slack was a problem we wanted to solve in a fashion that made it easy for our developers to understand and use while delivering a delightful experience to our users. We also wanted to build something that would scale as the number of users on Spoke increased. We think the above approach has brought us to a place with a scalable solution which also lets us continue to iterate quickly.

Find the Spoke app for Slack in the Slack App Directory to learn more. Questions, comments, or feedback? Drop us a note on Twitter @SlackAPI or send a message to devsupport@Slack.com.

--

--