> ## Documentation Index
> Fetch the complete documentation index at: https://docs.meshagent.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Messaging

## Overview

The `MessagingClient` is the Room API for participant-to-participant messages inside a room.
Use it for direct JSON messages, room-wide broadcasts, and participant presence.

If you need incremental or long-lived streaming, use **streaming toolkits** instead. Messaging stream APIs have been removed.
If you need process-backed agent turn lifecycle such as `turn.start`, `turn.started`, `turn.ended`, steering, or interruption, see [Agent Turns](../agents/process/agent_turns).

## Why use the Messaging API?

* Send structured JSON messages between room participants.
* Broadcast announcements to everyone in a room.
* Track which remote participants currently have messaging enabled.
* Attach raw bytes to a normal message when needed.

## Core concepts

### Participant-based delivery

Messaging targets `Participant` objects. That gives you one delivery model for users, agents, and services in the same room.

### Typed JSON payloads

Each message has a `type` plus a JSON payload. Use that to build chat flows, control messages, coordination events, or lightweight app protocols.

### Optional binary attachments

Messages may include an attachment for cases where you need to send bytes along with the JSON body.

### Streaming toolkits for streaming work

Messaging no longer exposes stream open/accept/chunk/close APIs. If your use case needs incremental results or chunked transfer, use a streaming toolkit instead of `room.messaging`.

### Agent turns are a separate layer

Process-backed agents use room messaging as transport, but the turn protocol is a separate runtime concept.

Use `MessagingClient` when you want:

* participant-to-participant messages
* broadcasts
* attachments
* participant discovery

Use [Agent Turns](../agents/process/agent_turns) when you want:

* process-backed agent execution
* turn lifecycle events
* steering or interruption
* thread-aware runtime control

## CLI commands

Use `meshagent room messaging` when you want to inspect messaging-enabled participants or send one-off messages from the terminal:

```bash bash theme={null}
meshagent room messaging --help
meshagent room messaging list --room myroom
meshagent room messaging send \
  --room myroom \
  --to-participant-id PARTICIPANT_ID \
  --type chat \
  --data '{"text":"Hello from the CLI"}'
meshagent room messaging broadcast \
  --room myroom \
  --data '{"content":"Hello everyone"}'
```

## Getting Started

<CodeGroup>
  ```python Python theme={null}
  # Suppose you already have a RoomClient instance named `room`

  await room.messaging.enable()

  participant = room.messaging.get_participant_by_name("other-user")
  if participant is not None:
      await room.messaging.send_message(
          to=participant,
          type="chat",
          message={"text": "Hello from Python!"},
      )

  await room.messaging.broadcast_message(
      type="announcement",
      message={"content": "Hello everyone!"},
  )
  ```

  ```ts TypeScript theme={null}
  // Suppose you already have a RoomClient instance named `room`

  await room.messaging.enable();

  const participant = room.messaging.getParticipantByName("other-user");
  if (participant) {
    await room.messaging.sendMessage({
      to: participant,
      type: "chat",
      message: { text: "Hello from TypeScript!" },
    });
  }

  await room.messaging.broadcastMessage({
    type: "announcement",
    message: { content: "Hello everyone!" },
  });
  ```

  ```dart Dart theme={null}
  // Suppose you already have a RoomClient instance named `room`

  await room.messaging.enable();

  final participant = room.messaging.remoteParticipants.isNotEmpty
      ? room.messaging.remoteParticipants.first
      : null;
  if (participant != null) {
    await room.messaging.sendMessage(
      to: participant,
      type: "chat",
      message: {"text": "Hello from Dart!"},
    );
  }

  await room.messaging.broadcastMessage(
    type: "announcement",
    message: {"content": "Hello everyone!"},
  );
  ```

  ```dotnet C# theme={null}
  // Suppose you already have a RoomClient instance named `room`

  room.Messaging.Enable();

  var participants = room.Messaging.GetParticipants();
  if (participants.Count > 0)
  {
      await room.Messaging.SendMessage(
          to: participants[0],
          type: "chat",
          message: new Dictionary<string, object?> { ["text"] = "Hello from C#!" }
      );
  }

  await room.Messaging.BroadcastMessage(
      type: "announcement",
      message: new Dictionary<string, object?> { ["content"] = "Hello everyone!" }
  );
  ```
</CodeGroup>

## API Methods

### enable()

Enables messaging for the current participant and starts participant discovery for the messaging subsystem.

<CodeGroup>
  ```python Python theme={null}
  await room.messaging.enable()
  ```

  ```ts TypeScript theme={null}
  await room.messaging.enable();
  ```

  ```dart Dart theme={null}
  await room.messaging.enable();
  ```

  ```dotnet C# theme={null}
  room.Messaging.Enable();
  ```
</CodeGroup>

### disable()

Disables messaging for the current participant.

<CodeGroup>
  ```python Python theme={null}
  await room.messaging.disable()
  ```

  ```ts TypeScript theme={null}
  await room.messaging.disable();
  ```

  ```dart Dart theme={null}
  await room.messaging.disable();
  ```

  ```dotnet C# theme={null}
  room.Messaging.Disable();
  ```
</CodeGroup>

### sendMessage(...)

Sends a direct message to one participant.

<CodeGroup>
  ```python Python theme={null}
  await room.messaging.send_message(
      to=participant,
      type="chat",
      message={"text": "Hey!"},
      attachment=b"\x01\x02\x03",
  )
  ```

  ```ts TypeScript theme={null}
  await room.messaging.sendMessage({
    to: participant,
    type: "chat",
    message: { text: "Hey!" },
    attachment: new Uint8Array([1, 2, 3]),
  });
  ```

  ```dart Dart theme={null}
  await room.messaging.sendMessage(
    to: participant,
    type: "chat",
    message: {"text": "Hey!"},
    attachment: Uint8List.fromList([1, 2, 3]),
  );
  ```

  ```dotnet C# theme={null}
  await room.Messaging.SendMessage(
      to: participant,
      type: "chat",
      message: new Dictionary<string, object?> { ["text"] = "Hey!" },
      attachment: new byte[] { 1, 2, 3 }
  );
  ```
</CodeGroup>

### broadcastMessage(...)

Broadcasts a message to every participant with messaging enabled.

<CodeGroup>
  ```python Python theme={null}
  await room.messaging.broadcast_message(
      type="announcement",
      message={"title": "Maintenance", "content": "We will be down tonight."},
  )
  ```

  ```ts TypeScript theme={null}
  await room.messaging.broadcastMessage({
    type: "announcement",
    message: { title: "Maintenance", content: "We will be down tonight." },
  });
  ```

  ```dart Dart theme={null}
  await room.messaging.broadcastMessage(
    type: "announcement",
    message: {"title": "Maintenance", "content": "We will be down tonight."},
  );
  ```

  ```dotnet C# theme={null}
  await room.Messaging.BroadcastMessage(
      type: "announcement",
      message: new Dictionary<string, object?>
      {
          ["title"] = "Maintenance",
          ["content"] = "We will be down tonight."
      }
  );
  ```
</CodeGroup>

### remoteParticipants / getParticipants()

Returns the currently known remote participants with messaging enabled.

### getParticipant(id) / getParticipantByName(name)

Looks up a known remote participant by id or by the `name` attribute.

## Notes

* Messaging is intended for discrete participant messages, not incremental response streams.
* For process-backed agent turn lifecycle, see [Agent Turns](../agents/process/agent_turns).
* For streaming responses, chunked transfer, or bidirectional streaming, use a streaming toolkit.
* Participant presence is only available after `enable()` completes.

## Related guides

* [Room API Overview](./overview)
* [Queues API](./queue)
* [Sync API](./sync)
