Skip to main content

Sending Messages

Sending messages is a core part of an agent's functionality. With all activity handlers, a send method is provided which allows your handlers to send a message back to the user to the relevant conversation.

app.OnMessage(async (context, cancellationToken) =>
{
await context.Send($"you said: {context.activity.Text}", cancellationToken);
});

In the above example, the handler gets a message activity, and uses the send method to send a reply to the user.

  app.OnVerifyState(async (context, cancellationToken) =>
{
await context.Send("You have successfully signed in!", cancellationToken);
});

You are not restricted to only replying to message activities. In the above example, the handler is listening to SignIn.VerifyState events, which are sent when a user successfully signs in.

tip

This shows an example of sending a text message. Additionally, you are able to send back things like adaptive cards by using the same send method. Look at the adaptive card section for more details.

Streaming​

You may also stream messages to the user which can be useful for long messages, or AI generated messages. The SDK makes this simple for you by providing a stream function which you can use to send messages in chunks.

app.OnMessage(async (context, cancellationToken) =>
{
context.Stream.Emit("hello");
context.Stream.Emit(", ");
context.Stream.Emit("world!");
// result message: "hello, world!"
return Task.CompletedTask;
});
note

Streaming is currently only supported in 1:1 conversations, not group chats or channels

Animated image showing agent response text incrementally appearing in the chat window.

@Mention​

Sending a message at @mentions a user is as simple including the details of the user using the AddMention method

app.OnMessage(async (context, cancellationToken) =>
{
await context.Send(new MessageActivity("hi!").AddMention(activity.From), cancellationToken);
});

Targeted Messages​

Coming Soon

Targeted messages are coming soon in May 2026.

Targeted messages, also known as ephemeral messages, are delivered to a specific user in a shared conversation. From a single user's perspective, they appear as regular inline messages in a conversation. Other participants won't see these messages, making them useful for authentication flows, help or error responses, personal reminders, or sharing contextual information without cluttering the group conversation.

To send a targeted message when responding to an incoming activity, use the WithRecipient method with the recipient account and set the targeting flag to true.

app.OnMessage(async (context, cancellationToken) =>
{
// Using WithRecipient with isTargeted=true explicitly targets the specified recipient
await context.Send(
new MessageActivity("This message is only visible to you!")
.WithRecipient(context.Activity.From, isTargeted: true),
cancellationToken
);
});
.NET

In .NET, targeted message APIs are marked with [Experimental("ExperimentalTeamsTargeted")] and will produce a compiler error until you opt in. Suppress the diagnostic inline with #pragma warning disable ExperimentalTeamsTargeted or project-wide in your .csproj:

<PropertyGroup>
<NoWarn>$(NoWarn);ExperimentalTeamsTargeted</NoWarn>
</PropertyGroup>

Reactions​

Reactions allow your agent to add or remove emoji reactions on messages in a conversation, and to receive reactions added by users. See the Message Reactions guide for full coverage.

Threading​

In Teams channels, messages can be organized into threads. The SDK provides helpers to simplify working with threads.

Reactive Threading (Within a Handler)​

When your agent receives a message in a thread, the conversation context already carries the thread ID. Use Send() to send a message in the same thread without quoting, or Reply() to send with a visual quote of the inbound message.

app.OnMessage(async (context, cancellationToken) =>
{
// Send in the same thread, no quote
await context.Send("Acknowledged", cancellationToken);

// Send in the same thread with a visual quote of the inbound message
await context.Reply("Got it!", cancellationToken);
});

For proactive threading (sending to a thread outside of a handler), see Proactive Messaging.

Quoted Replies​

Coming Soon

Quoted replies are coming soon in May 2026.

Quoted replies let your agent reference a previous message in the conversation. When a user sends a message that quotes another message, your agent receives structured metadata about the quoted content. Your agent can also send messages that quote previous messages.

Receiving Quoted Replies​

When a user quotes a message and sends it to your agent, the quoted reply metadata is available on the inbound activity. Use the GetQuotedMessages() method to access all quoted reply entities.

app.OnMessage(async context =>
{
var quotes = context.Activity.GetQuotedMessages();

if (quotes.Count > 0)
{
var quote = quotes[0].QuotedReply;
await context.Reply(
$"You quoted message {quote.MessageId} from {quote.SenderName}: \"{quote.Preview}\"");
}
});

Each quoted reply entity contains the quoted message's ID, sender information, a preview of the quoted text, and whether the quoted message has been deleted.

Sending a Quoted Reply​

When your agent calls Reply(), the SDK automatically stamps a quoted reply entity referencing the inbound message. The reply will appear as a quoted reply in Teams.

app.OnMessage(async context =>
{
// Reply() automatically quotes the inbound message
await context.Reply("Got it!");
});

To quote a different message in the same conversation (not the inbound message), use the Quote() method with the message ID you want to quote.

app.OnMessage(async context =>
{
// Quote a specific message by its ID
var parentMessageId = "1772050244572";
await context.Quote(parentMessageId, "Referencing an earlier message");
});

Building Quoted Replies for Proactive Send​

For proactive scenarios (using app.Send()) or when quoting multiple messages, use the AddQuote() method on a message activity. Pass the message ID and an optional response text.

var parentMessageId = "1772050244572";
var firstMessageId = "1772050244573";
var secondMessageId = "1772050244574";

// Single quote with response below it
var msg = new MessageActivity()
.AddQuote(parentMessageId, "Here is my response");
await app.Send(conversationId, msg);

// Multiple quotes with interleaved responses
msg = new MessageActivity()
.AddQuote(firstMessageId, "response to first")
.AddQuote(secondMessageId, "response to second");
await app.Send(conversationId, msg);

// Grouped quotes — omit response to group quotes together
msg = new MessageActivity("see below for previous messages")
.AddQuote(firstMessageId)
.AddQuote(secondMessageId, "response to both");
await app.Send(conversationId, msg);
.NET

In .NET, quoted reply APIs are marked with [Experimental("ExperimentalTeamsQuotedReplies")] and will produce a compiler error until you opt in. Suppress the diagnostic inline with #pragma warning disable ExperimentalTeamsQuotedReplies or project-wide in your .csproj:

<PropertyGroup>
<NoWarn>$(NoWarn);ExperimentalTeamsQuotedReplies</NoWarn>
</PropertyGroup>