What Is a Message Delivery Receipts Service?
A message delivery receipts service tracks the lifecycle of every message from send to read. It powers the familiar checkmarks in WhatsApp (sent, delivered, read) and the seen indicators in iMessage. Getting receipts right is deceptively complex: you must handle offline recipients, group fan-out, race conditions, and massive write throughput — all while keeping the UI update latency under a few hundred milliseconds.
Data Model / Schema
Each message has one receipt record per recipient, capturing the state transitions:
-- Receipt state machine per (message, recipient)
CREATE TABLE receipts (
msg_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
status ENUM('sent', 'delivered', 'read'),
updated_at TIMESTAMP DEFAULT NOW(),
PRIMARY KEY (msg_id, user_id),
INDEX (user_id, updated_at)
);
-- Aggregated view for group chats (denormalized for fast reads)
CREATE TABLE message_receipt_summary (
msg_id BIGINT PRIMARY KEY,
delivered_count INT DEFAULT 0,
read_count INT DEFAULT 0,
total_recipients INT NOT NULL,
updated_at TIMESTAMP DEFAULT NOW()
);
For 1:1 chats, query the receipts table directly. For groups, use the summary table to avoid scanning hundreds of rows on every UI poll.
Core Algorithm / State Machine
Each receipt transitions through a strict one-way state machine:
sent --> delivered --> read
State transitions are append-only and monotone: you can only move forward, never backward. The service enforces this with a conditional update:
UPDATE receipts
SET status = 'delivered', updated_at = NOW()
WHERE msg_id = ? AND user_id = ?
AND status = 'sent';
Workflow
- Sent: When the Chat Server persists the message, it inserts one receipts row per recipient with status
sent. - Delivered: When the recipient's device receives the message frame over WebSocket (or pulls it from the inbox), the client sends a
deliveredACK. The server updates the row todeliveredand notifies the sender via the real-time channel. - Read: When the user opens the conversation and the message is visible on screen, the client sends a
readACK. The server updates the row toreadand notifies the sender.
Failure Handling
Offline recipients: If the recipient is offline at delivery time, the status stays sent. When they reconnect and pull their inbox, the client sends the delivered ACK as part of the sync flow. There is no need for a scheduled job because the transition is triggered by the client.
Lost ACKs: Clients retry ACKs with exponential back-off. The conditional UPDATE is idempotent — a duplicate delivered ACK on an already-delivered row updates zero rows and returns success.
Group partial delivery: In group chats, the sender sees a per-member breakdown only in the detail view. The summary table is updated atomically via an increment when any member transitions:
UPDATE message_receipt_summary
SET delivered_count = delivered_count + 1, updated_at = NOW()
WHERE msg_id = ?;
If the increment fails (e.g., crash mid-write), a reconciliation job periodically recomputes the summary from the receipts table for any row where updated_at is recent and the counts look stale.
Scalability Considerations
Write throughput: At 1 billion messages per day with average group size 5, that is ~5 billion receipt row writes per day. Batch ACKs from the client (send a list of msg_id values in one ACK frame) to reduce round trips. On the server, use bulk INSERT … ON DUPLICATE KEY UPDATE to batch database writes.
Read throughput: Senders poll for receipt updates on open conversations. Cache the summary row in Redis with a short TTL (1–2 s). Most reads hit cache; the database sees only cache-miss traffic and write-through updates.
Presence integration: Combine the presence service with receipts to optimize delivery: if a recipient is known to be online, push the message and expect a delivered ACK within 5 seconds before falling back to the inbox pull flow.
Summary
A message delivery receipts service is fundamentally a distributed state machine with high write throughput. The keys to correctness are monotone state transitions with conditional updates, idempotent ACK handling for retries, and a denormalized summary table for fast group reads. Combine client-side batching with server-side bulk writes and a Redis cache layer to absorb the write amplification that comes with large group chats.
{
“@context”: “https://schema.org”,
“@type”: “FAQPage”,
“mainEntity”: [
{
“@type”: “Question”,
“name”: “What are the different types of message delivery receipts?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “There are typically three levels of delivery receipts: Sent (the server has accepted and persisted the message), Delivered (the message has reached the recipient’s device), and Read (the recipient has opened and viewed the message). Each state transition triggers a receipt event sent back to the original sender. Some systems also add a fourth state, Downloading, for media-heavy messages that require separate asset retrieval.”
}
},
{
“@type”: “Question”,
“name”: “How do you implement read receipts at scale without overwhelming the system?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Read receipts can generate a very high volume of events. To handle this at scale, receipt events are batched on the client side (e.g., every 2-3 seconds) before being sent to the server. The server processes them asynchronously via a message queue like Kafka. The receipt state per message is stored in a fast key-value store such as Redis, with periodic flushes to the primary database. Rate limiting per user prevents abuse and reduces write amplification.”
}
},
{
“@type”: “Question”,
“name”: “How does message delivery receipt state get synchronized across multiple devices?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “When a user has multiple devices, delivery receipt state must be consistent across all of them. The server maintains a per-user, per-message receipt state. When any device marks a message as read, the server updates the canonical state and broadcasts a receipt sync event to all other active sessions for that user via their WebSocket connections. Devices that are offline receive the sync event upon reconnection through a catch-up mechanism that queries receipt state since the last sync timestamp.”
}
},
{
“@type”: “Question”,
“name”: “How do you handle delivery receipts for group messages?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “For group messages, the sender typically sees an aggregate receipt status. The server tracks per-recipient delivery and read states for each message. A message is marked as Delivered when all recipients’ devices have acknowledged it, and Read when all (or a configurable threshold of) recipients have viewed it. To avoid storing a full matrix of (message_id, user_id) receipt records at very large group sizes, some systems switch to an approximate model, displaying a count of recipients who have read the message rather than exact per-user status.”
}
}
]
}
See also: Meta Interview Guide 2026: Facebook, Instagram, WhatsApp Engineering
See also: Snap Interview Guide
See also: Scale AI Interview Guide 2026: Data Infrastructure, RLHF Pipelines, and ML Engineering