livqenoDocs

RTCData Messages

Data Messages

Send small messages to everyone in a call, to one topic, or to just a few people, over a reliable or a lossy channel. Web, React Native, Flutter, iOS and Android.

Data messages are small payloads, either text or bytes, that travel with the call over the same connection as the media. Use them for things that belong to this call and nowhere else:

  • a "raise hand" or an emoji reaction,
  • a cursor position on a shared whiteboard,
  • game state in a multiplayer lobby,
  • a "you're on mute" nudge sent to one person.

For a chat history that is kept, searchable and delivered to people who join later, use Chat instead.

Before you start: your app has joined a room with a token that has publishData: true (see Authentication). All code on this page runs in your app. To send from your backend, see Messages from your server.

Send and receive

await room.sendData('hello'); // string or Uint8Array: reliable, to everyone
 
await room.sendData(JSON.stringify({ emoji: '🎉' }), { topic: 'reaction' });
await room.sendData('you are muted', { destinationIdentities: ['bob'] });
await room.sendData(cursorBytes, { topic: 'cursor', reliable: false }); // lossy
 
room.on('dataReceived', (payload, participant, topic) => {
  // participant: who sent it, or undefined for a message from your server
  // topic: undefined when the sender gave none
  console.log(topic, participant?.identity, new TextDecoder().decode(payload));
});

With no options, sendData() does exactly what it always did: a reliable message to everyone in the room.

Options

OptionDefaultWhat it does
topicnoneA label for the message, up to 255 bytes of UTF-8. Receivers get it next to the payload, so one listener can route reactions, cursors and game moves apart.
destinationIdentitieseveryoneSend only to these identities, up to 64 of them. Identities that aren't in the room are skipped without an error.
reliabletruetrue: ordered and retransmitted, up to 64 KiB. false: the lossy channel, unordered and never retransmitted, up to 1,200 bytes.

When to use lossy. Use it when only the latest value matters, such as cursor positions, a player's position or a live slider. A lost update is replaced by the next one anyway, and the lossy channel never makes a newer message wait behind an older one that is being retransmitted. Anything that must arrive, such as a reaction or a command, stays reliable.

Who sent it

The sender is filled in by Livqeno's media server, not by the sending app, so a client can't pretend to be someone else.

  • A message from a participant arrives with that participant.
  • A message from your server (see below) arrives with no participant (undefined / null / nil).
  • A message from an SDK that is older than this feature also arrives with no participant and no topic.

Messages from your server

Your backend can send to a room with an API key. Recipients see it with no participant:

curl -X POST "$LIVQENO_API_URL/v1/rooms/$ROOM_ID/data" \
  -H "Authorization: Bearer $LIVQENO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "payload": "'"$(printf 'meeting ends in 5 min' | base64)"'", "topic": "system", "reliable": true }'

Or with a server SDK:

await livqeno.rooms.sendData(roomId, 'meeting ends in 5 min', { topic: 'system' });
await livqeno.rooms.sendData(roomId, cursorBytes, { destinationIdentities: ['bob'], reliable: false });

The API answers 202 { "messageId": "…" } once the message is handed on for delivery. It answers 404 RAVEN_PARTICIPANT_NOT_CONNECTED when nobody is in the room, and 400 for a payload or topic over its limit (the same limits as below). :roomId takes the room's id or its name. destinationIdentities works here too. Delivery is best-effort, like any data message, so the SDKs never retry this call. See the RTC API reference for the full request.

Limits

LimitValue
Reliable payload64 KiB
Lossy payload1,200 bytes
Topic255 bytes of UTF-8
Destinations per message64
Reliable rate per participant100 messages/s and 1 MiB/s
Lossy rate per participant300 messages/s

Every limit is checked before anything is sent. A topic or destination list over its limit, or a payload over its size limit, throws INVALID_ARGUMENT on every platform.

Over the rate limit, the server drops the extra messages and tells the sender. The SDK logs a warning and fires an event, so you can slow down:

PlatformEvent
Web / React Nativeroom.on('dataThrottled', (channel) => …), where channel is 'reliable' or 'lossy'
Flutterroom.dataThrottled.listen((e) => e.channel)
iOSRoomEvent.dataThrottled(channel:)
AndroidRoomEvent.DataThrottled(channel)

Receiving: add the listener right after joining

The SDK opens both data channels (reliable and lossy) as soon as it joins a server that supports them, so messages sent to you are delivered even before you add a listener. Still add the listener once, right after joining: messages that arrive before it are not replayed to it. (Against an older server, listening for data is what opens your channel.)

A message only reaches people who are connected at that moment. It isn't saved for people who join later.

Older servers

Topics, targeting, the lossy channel and sender identity need a media server that supports them. The SDK finds out when it joins (room.joined lists data.v2 in its capabilities). Against an older server:

  • a plain sendData(payload) works exactly as before,
  • reliable: false falls back to the reliable channel, with a warning,
  • received messages arrive with no participant and no topic.

Mixed rooms work. Someone on an SDK from before this feature still gets your messages, as the plain payload with no topic or sender, exactly as they always did. Targeting still applies: a message for bob never reaches anyone else.

Livqeno Cloud supports all of it.

Common errors

ErrorCauseFix
PERMISSION_DENIED on sendData()The token has no publishData.Set publishData: true when you mint the token.
INVALID_ARGUMENT on sendData()The payload is over 64 KiB, or over 1,200 bytes with reliable: false.Send less, split it up, or use the reliable channel.
INVALID_ARGUMENT on sendData()The topic is over 255 bytes, there are more than 64 destinations, or a destination identity is empty.Shorten the topic, or send in batches.
Messages from some people arrive with no participantThey use an SDK from before sender identity existed.Upgrade their SDK.
  • Rooms & Participants: participant metadata and attributes, for state that should stay in place rather than travel as a message.
  • Events: the full event list.
  • Chat: for messages that need history.