Message Read Receipts

Most chat application users expect delivery or read receipts on the messages they sent. WhatsApp was the first to introduce this feature back in 2014, while almost all of the other chat apps in the market soon followed suit. With this Design Pattern, you’ll understand how to implement read receipts in your chat apps using Ably’s Realtime Platform.

What are ‘read receipts’?

Continuing from WhatsApp’s example, we know that when we send a message via WhatsApp, we see a single grey tick if the message is sent successfully, double grey ticks if the message is received successfully by the recipient and double blue ticks if the message is seen or read by the recipient. This is shown below in an illustration by Kuwaitiful.

Read Receipts in WhatsApp

This feature can be quite easily implemented using Ably. The implementation details are below.

Implementation details

Ably deals with messages in real time using named channels. For implementing read/delivery receipts in a chat app, you’ll need to make use of these channels to send and receive both the actual message contents that are being exchanged by different parties participating in the chat app, as well as the messages corresponding to receipt data. Since all of this needs to be done in real-time, you’ll need to use Ably’s Realtime Library.

For the read receipts, you can use a separate channel altogether, so that the messaging business logic is left undisturbed. Of course, we also need to know the presence status of the users. So, for a generic chat app between two users, let us consider three channels as shown below. This is explained further.

Read receipts design pattern using Ably


Let’s name the three channels as below:

  • “messages-channel”
  • “presence-channel”
  • “read-receipts-channel”

Note that the arrows in the illustration above depict the direction of flow of data in these channels.

The messages channel can be used by the users to send and receive messages from other users. Hence, they need to have both publish and subscribe permissions on this channel. This messages channel can be set up to use the Ably Integrations service, so you get a buffer/ shock absorber to cope with a high volume of incoming messages. Ably Integrations is a scalable service that lets you implement load balancing on your servers, trigger events or serverless functions in real-time and even stream data in real-time to other third-party streaming services.

Next, the presence channel can be used by the users to send their presence events to the server and also receive presence events from other users via the server. The users have both ‘publish’ as well as ‘subscribe’ rights on this channel.

The final one is the read-receipts channel. This channel is our main point of discussion throughout this Design Pattern. We’ll use this channel to send and receive various message delivery receipts i.e, ‘sent’, ‘delivered’ and ‘seen’. Everyone has publish as well as subscribe permissions to this channel.

Latencies are sorted but what about the order of messages?

Ably ensures that messages are delivered to persistently connected subscribers on a channel in the order they were published on that channel by any other Ably client when using one of the Realtime libraries. Read more about it in the support articles.


  • Ably currently caps peak subscriptions to presence updates at 200, hence be mindful of that. If you don’t need your users to know the presence status of all the other users, this problem is completely solved. In that case, the presence channel can be used by the clients to send their presence events to the server. In turn, the server can subscribe to this channel to be notified of this info whenever a presence event occurs. Everyone will have presence permissions on the presence channel, but not subscribe permission. This means you have the ability to query the presence on a channel or subscribe to presence events from your server, but the clients themselves won’t be able to inspect/subscribe to this channel. This avoids the classic n­-squared problem ,e.g. 10k people entering and subscribing to 10k ‘presence-enter’ events would result in 100m messages. However, 10k people entering, generating 10k presence events only for your server results in just 20k messages.
  • You have a reliable way to consume data from clients with a shock absorber (the queue), as well as to be able to simplify how you process data using workers.
  • Assuming the number of clients for such an application is generally high, you can group them into clusters based on their geographical location. You would have the three channels for each cluster implemented, with the same functionality. You can ensure you have a constant amount of data published and received regardless of cluster size or activity. Clients won’t become “overloaded” with too much data, potentially that you don’t have control over.

Live demo

You can see the full code for this demo app in the GitHub repo.

This live demo is intended to demonstrate the design pattern described above. As such, it works best with at most two chat instances.