Why architecture matters here

The architecture matters because a status indicator is a promise, and a broken promise here erodes trust in the whole product. When a user sees a 'delivered' check, they believe the message reached the other person's device; when they see 'read,' they believe it was seen. If those indicators are wrong — 'delivered' on a message lost in transit, or perpetually 'sent' on one already read — the user makes real decisions on false information: they assume they were ignored, or they assume they were heard when they were not. The receipt system is not decorative; it is a claim about the state of a conversation, and its value is entirely in its accuracy. An architecture that cannot guarantee the indicator matches reality is worse than no indicator at all.

It matters, too, because the facts being reported are genuinely distributed and each hop can fail independently. 'Sent' is observed at the server when it persists the message. 'Delivered' is observed on the recipient's device and must travel back. 'Read' is observed later, on the same device, and must also travel back. Every one of these observations, and every acknowledgement of it, crosses an unreliable network on a connection that can drop mid-flight. Without an architecture that assigns stable identities, tracks state per recipient, and reconciles on reconnect, receipts drift out of sync with reality the first time a connection blips — which, at scale, is constantly. The system exists precisely to make an inherently unreliable, multi-party observation reliable end to end.

The architecture also matters because the state it tracks must be monotonic, and preserving that under an out-of-order network is non-trivial. A message progresses sent then delivered then read, and it must never appear to go backward — a user must never see a message flip from 'read' back to 'delivered' because a delayed delivered-ack arrived after the read receipt. Networks reorder; receipts arrive late. The status store has to enforce that state only advances, discarding any receipt that would move it backward. Get this wrong and the UI flickers between states, which reads as a bug even when every individual receipt was technically correct. Monotonicity is a property the architecture must impose because the network will not provide it.

Finally, it matters because receipts multiply in groups and touch privacy in a way one-to-one messaging does not. In a group of two hundred people, one message generates up to two hundred delivered acks and two hundred read receipts, and the sender's UI needs an aggregate ('read by 143') rather than a storm of individual events. And read receipts specifically are a privacy-sensitive signal — many users want to read without broadcasting that they read — so the architecture must support turning read receipts off, symmetrically, without breaking delivery tracking. A receipt system that ignores fan-in cardinality melts down in large groups, and one that ignores the privacy setting ships a feature users actively distrust.

Advertisement

The architecture: every piece explained

Top row: identity and the two acknowledgements. A sender composes a message and tags it with a client id — a locally-generated unique id that will let the system deduplicate retries. The server persists the message and assigns it a stable server id and a per-conversation sequence number; this server id is the identity every receipt will refer to, so that acks are unambiguous even if the client retried and generated the message twice. As the message reaches the recipient, two acknowledgements originate on the recipient's side at two different times: a delivered ack the moment the recipient's device receives and persists the message, and a read receipt later, when the user actually opens the conversation and the message is displayed. These are distinct events with distinct meanings, and the architecture keeps them separate all the way back to the sender.

Middle row: the return path and the state. Both acknowledgements travel back over the ack channel — the same bidirectional connection that delivered the message, now carrying receipts the other direction. The server records them in a status store that holds, per message and per recipient, the current state (sent, delivered, read) and the timestamp of each transition. For a group message, a fan-in step aggregates the many recipients' acks into the summary the sender's UI needs — counts and, where shown, the set of who has read. And because receipts, like any message, can be retried and duplicated, idempotent dedup keyed on the message's server id and the recipient collapses repeated receipts for the same (message, recipient, state) into one, so a retried read receipt does not double-count.

Right column and the group case: the fan-in is where scale concentrates. A message to a large group can generate one acknowledgement per recipient per state, and pushing every one of those to the sender individually would flood the sender's connection and UI. Instead the server aggregates: it tracks each recipient's state in the status store and emits to the sender a rolled-up view — 'delivered to all, read by 143 of 200' — updating it as receipts arrive, rather than relaying two hundred separate events. The idempotent dedup ensures that recipients whose devices retried their acks are counted once.

Bottom rows: offline handling and privacy. A recipient who is offline cannot ack in real time, so their delivered and read receipts are queued and delivered when they reconnect — and, symmetrically, receipts destined for an offline sender are buffered until the sender returns, so no status update is lost to a disconnection. Read receipts are gated by a privacy setting: a user who has turned read receipts off does not emit them, and typically also does not see others' — the setting is usually symmetric — while delivered acks and the underlying delivery still function. The ops strip names the properties that keep receipts trustworthy: a receipt-latency SLO (how fast a status reflects reality), enforced status monotonicity, backfill of missed receipts on reconnect, and attention to fan-in cardinality so large groups do not overwhelm the sender.

Delivery + read receipts — per-message acknowledgement over a duplex channelsent, delivered, read: three states the sender can trustSendermessage + client idServer persistassign server id + seqDelivered ackdevice received itRead receiptuser actually saw itAck channelreceipts flow backStatus storeper-recipient stateFan-in (group)N recipients aggregateIdempotent dedupclient id collapses retriesOffline: queue receiptsdeliver on reconnectPrivacy: read-receipt opt-outrespect settingOps — receipt latency SLO + status monotonicity + backfill on resume + fan-in cardinalitysenddeliveropenawaitrecordaggregatecollapsebuffergateoperateoperate
Delivery and read receipts: the server persists each message with a server id and sequence, the recipient's device sends a delivered ack and later a read receipt back over the same duplex channel, a per-recipient status store tracks monotonic state, group messages fan in acks across recipients, and receipts queue for offline recipients and respect read-receipt privacy settings.
Advertisement

End-to-end flow

Trace a message and its receipts through a one-to-one chat, then note how a group changes it.

Send and persist: Ana types a message to Ben. Her client tags it with a client id and pushes it over the WebSocket. The server persists it, assigns a server id and the next sequence number in the Ana-Ben conversation, and immediately acks back to Ana's client that the message is stored — her UI shows the single 'sent' check. The server id is now the stable handle every later receipt will reference.

Delivery: Ben is online, so the server pushes the message down his WebSocket. His device receives it, persists it locally, and sends a delivered ack referencing the server id back up the channel. The server records Ben's state for this message as 'delivered' in the status store and pushes a delivered notification to Ana; her UI advances to the double check. Elapsed time is sub-second, bounded by the receipt-latency SLO. If Ben had been offline, the message and the eventual delivered ack would have queued, and the double check would appear when he reconnected.

Read: a minute later Ben opens the conversation and the message is displayed. His client emits a read receipt for the server id. The server records 'read,' checks Ben's privacy setting (read receipts are on), and pushes the read status to Ana, whose UI shows 'seen.' The status store now holds sent, delivered, and read timestamps for this message. Had Ben turned read receipts off, this step would be suppressed: the message would sit at 'delivered' from Ana's view even though Ben had read it — the privacy trade the setting exists to make.

Monotonicity under reorder: suppose the network delayed Ben's delivered ack so it arrived at the server after his read receipt. The status store, seeing 'read' already recorded, discards the late delivered ack rather than moving the state backward — Ana never sees the status regress from 'seen' to 'delivered.' The store enforces that state only advances, imposing the order the network failed to preserve.

The group case: now Ana posts to a 200-person group. The server fans the message out and tracks each recipient's state independently in the status store. Rather than relaying 200 delivered acks and 200 read receipts to Ana as separate events, it aggregates: her UI shows 'delivered to all' once every device has acked, and 'read by 143' updating as people open it. Recipients whose devices retried their acks are deduplicated by (server id, recipient, state), so the count is exact. Offline members' receipts backfill when they reconnect, advancing the aggregate. The sender gets a single, monotonic, accurate rollup instead of a storm — which is the whole reason the fan-in exists.