Detect Direct Messages / Datapackets

I’m curious how one would leverage the destinationSID/Identities to implement a direct send feature. Or, how you can detect direct messages with the server SDK (Go specifically).

I am not sure what you are asking. What are you trying to do? What is your usecase?

Like an IRC whisper. The issue is that message callbacks for broadcast messages, vs direct messages.

Have a look at data packets with `destinationIdentities`

We are. But once the direct send has occurred, that information is stripped, so the callback can’t tell that it was a direct message.

Even a boolean of isDirectwould solve this problem.

As a workaround, could you include the isDirect param in the message payload?

We are talking about that, at that point we’re requiring a data envelope, which is undesirable to us in some ways. It just seems logical that the LiveKit Server would be able to inform the receiver that it was a direct message.

The issue is the same as Telegram had originally…

With current LiveKit APIs, the basic pattern is:

  1. Send a targeted data packet by identity
  2. Treat “direct message” as app-level semantics
  3. Detect it on receive using your own topic/payload metadata

In v2, LiveKit moved away from recipient SIDs for data publishing. The current pattern is to target ‘participant identities’ instead. Go uses PublishDataPacket(..., WithDataPublishDestination([]string{"alice","bob"})), the Go SDK will then write those identities into dataPacket.DestinationIdentities before publishing.

A direct-send in Go looks like this:

payload := []byte(`{"type":"dm","to":"alice","body":"hello"}`)

err := room.LocalParticipant.PublishDataPacket(
    lksdk.UserData(payload),
    lksdk.WithDataPublishReliable(true),          // usually what you want for chat/DMs
    lksdk.WithDataPublishTopic("dm"),             // app-level topic
    lksdk.WithDataPublishDestination([]string{"alice"}), // target identity
)
if err != nil {
    // handle error
}

That will cause LiveKit to route the packet only to the participant(s) whose ‘identity’ matches the destination list. If no destination is set, the message is broadcast to the room.

The important limitation is on the receive side. In the Go SDK, OnDataReceived now gives you DataReceiveParams, but that struct exposes only the sender, ‘sender identity’, and topic. It does not expose the destination identities or an isDirectMessage flag.

So, from the server SDK callback alone, you generally cannot know whether an incoming packet was originally a room-wide broadcast or a targeted/direct packet (unless you had already defined the meaning yourself)

In practice, the usual approach I’d use is like this:

cb := lksdk.NewRoomCallback()
cb.OnDataReceived = func(data []byte, params lksdk.DataReceiveParams) {
    // params.SenderIdentity
    // params.Topic

    if params.Topic == "dm" {
        // treat as a direct message by convention
    }

    // optionally decode your JSON and inspect fields like:
    // { "type": "dm", "to": "alice", "body": "..." }
}

A solid DM design would be:

  • Use a dedicated topic such as "dm"
  • Include explicit fields in the payload like type, to, from, messageId
  • Optionally include your own conversation/thread ID
  • Use reliable=true for DM/chat behavior rather than loss delivery.

One more nuance if by “server SDK” you mean a backend process that is not actually joined to the room as a participant, it will not naturally receive WebRTC data-channel messages via OnDataReceived. That callback belongs to the realtime room client side of server-sdk-go (NewRoom, room callbacks, local participant publishing). So for the server to observe or participate in DMs, it typically needs to join as a participant/bot/agent in the room.

So the answer I believe is:

  • Use destinationIdentities to implement direct send.
  • Do not rely on the receive callback to tell you it was direct.
  • And mark DMs yourself with the topic/payload metadata yourself.

Hope it helps.