livqenoDocs

ChatDelivery & Read Receipts

Delivery & Read Receipts

Track how far each user has read, and show unread counts.

Read receipts record how far each user has read in a conversation. Use them for "Seen by" avatars under a message, an unread badge on a conversation list ("3 new"), or a "the agent has seen your message" hint in support chat.

Where this fits: your app is connected to a conversation and shows messages. Your app tells Livqeno when the user has read something; Livqeno stores it and tells the other members.

Use it

In your app:

await chat.markAsRead('msg_3xR…');
 
chat.on('read', (event) => {
  console.log(`${event.userId} has read up to ${event.messageId}`);
});
 
const state = await chat.getReadState();
// { lastReadMessageId: 'msg_…', lastReadAt: '...', unreadCount: 3 }

Marking one message as read also marks everything before it. When a user scrolls to the bottom, make one call with the newest message.

When to call markAsRead:

  • When the chat screen is open and visible, and a new message arrives.
  • When the user scrolls down to the newest message.
  • Not when the tab is in the background. The user hasn't actually seen it.

A position, not a log

Livqeno stores one marker per user per conversation: the newest message they've read. It doesn't store every single read.

  • Opening a chat with 500 unread messages is one small update.
  • The unread count is simply: messages after my marker, not sent by me.

The marker only moves forward

If two tabs are open at different scroll positions, the marker never moves back. An old tab can't "un-read" messages.

Delivery semantics: three different things

StateMeansGuaranteed?
StoredThe message is saved.Yes. This is what the send confirmation means.
DeliveredA connected device received it.Only reported as a total count, not per person.
ReadThe user marked it read.Yes, and saved. Your app triggers it.

Why "delivered" isn't a per-recipient flag

A device receiving a message doesn't mean a person saw it. A background tab or a phone in a pocket receives everything. A "delivered ✓" per person would look strong but mean very little.

If you need something like "delivered", build it on read, which needs a real user action.

Everyone's position

To draw "Seen by" avatars, load everyone's marker once when the chat opens, then keep it updated with read events:

const receipts = await chat.getReadReceipts();
// [{ userId: 'bob', lastReadMessageId: 'msg_…', lastReadAt: '...' }, ...]

Returns at most 500 rows, even in very large channels.

Common errors

ProblemCauseFix
Unread count doesn't drop after reading older messagesThe marker only moves forward.Mark the newest visible message as read.
No read event for your own readYou don't get your own events back.Use the value getReadState() returns.
Messages show as "seen" when the user wasn't lookingYour app marks messages read while in the background.Only call markAsRead when the chat is visible.

Next steps