Understanding Activity Protocol

Activity Protocol is a standard communication protocol used across Microsoft in many Microsoft SDKs, services, and clients. Activity Protocol is used by Microsoft 365 Copilot, Microsoft Copilot Studio, and the Microsoft 365 Agents SDK. Activity Protocol defines the structure of an Activity and how messages, events, and interactions flow from a channel to your code and everywhere else in between. Agents can connect to one or more channels to interact with users and work with other agents. Activity Protocol standardizes the communication protocol with any client you're working with, including Microsoft and non-Microsoft clients, so that you don't have to create custom logic for each channel.

What is an Activity?

An Activity is a structured JSON object that represents any interaction between a user and your agent. Activities aren't limited to text-based messages. They can include various types of interaction, such as events like a user joining or leaving for clients that support multiple users, typing indicators, file uploads, card actions, and custom events that developers design.

Every activity includes metadata about:

  • Who sent it (from)
  • Who should receive it (recipient)
  • The conversation context
  • The channel it originated from
  • The type of interaction
  • The payload data

Activity schema - key properties

This specification defines Activity Protocol: Activity Protocol - Activity. Some of the key properties defined in Activity Protocol are:

Property Description
Id Typically generated by the channel if originating from a channel
Type The type controls the meaning of an activity, for example message type
ChannelID The ChannelID references the channel that the activity originated from. For example: msteams.
From The sender of the activity (which can be a user or agent)
Recipient The intended recipient of the activity
Text The text content of the message
Attachment Rich content like cards, images of files

Access activity data

To complete actions from the TurnContext object, developers need to access the data within the activity.

You can find a TurnContext class in each language version of the Microsoft 365 Agents SDK:

Note

The code snippets in this article use C#. The syntax and API structure for the JavaScript and Python versions are similar.

The TurnContext is an important object that's used in every conversation turn in the Microsoft 365 Agents SDK. It provides access to the incoming activity, methods for sending responses, conversation state management, and the context needed to handle a single turn of conversation. Use it to maintain context, send appropriate responses, and interact with your users in their client or channel effectively. Every time your agent receives a new activity from a channel, the Agents SDK creates a new TurnContext instance and passes it to your registered handlers or methods. This context object exists during the single turn and is then disposed of once the turn ends.

A turn is defined as the round trip of a message sent from the client and making the journey to your code. Your code handles that data and can optionally send a response back to complete the turn. That round trip can be broken up into the following steps:

  1. Incoming activity: The user sends a message or performs an action that creates an activity.

  2. Your code receives the activity and the agent processes it using TurnContext.

  3. Your agent sends one or more activities back.

  4. The turn ends and the TurnContext is disposed of.

Access data from the TurnContext, such as:

var messageText = turnContext.Activity.Text;
var channelID = turnContext.Activity.ChannelId;

This code snippet shows an example of a complete turn:

agent.OnActivity(ActivityTypes.Message, async (turnContext, turnState, cancellationToken) =>
{
    var userMessage = turnContext.Activity.Text;
    var response = $"you said: {userMessage}";
    await turnContext.SendActivityAsync(MessageFactory.Text(response), cancellationToken);
});

Inside the TurnContext class, commonly used key information includes:

  • Activity: The main way to get information from the activity
  • Adapter: The channel adapter that created the activity
  • TurnState: The state for the turn

Activity types

The type of an activity defines what the rest of the activity requires or expects between clients, users, and agents.

These include:

  • Message
  • ConversationUpdate
  • Event
  • Invoke
  • Typing

Message

A common type of activity is the Message type of Activity. This Activity type can include text, attachments, and suggested actions.

agent.OnActivity(ActivityTypes.Message, async (turnContext, turnState, cancellationToken) =>
{
    var userMessage = turnContext.Activity.Text;
    var response = $"you said: {userMessage}";
    await turnContext.SendActivityAsync(MessageFactory.Text(response), cancellationToken);
});

ConversationUpdate

The ConversationUpdate type of Activity notifies your agent when members join or leave a conversation. Not all clients support this notification, but Microsoft Teams does.

The following code snippet greets new members in a conversation:

agent.OnActivity(ActivityTypes.ConversationUpdate, async (turnContext turnState, cancellationToken) =>
{
    var membersAdded = turnContext.Activity.MembersAdded
    if (membersAdded != null)
    {
        foreach (var member in membersAdded)
        {
            if (member.Id != turnContext.Activity.Recipient.Id)
            {
                await turnContext.SendActivityAsync(MessageFactory.Text($"Welcome {member.Name}!"), cancellationToken);
            }
        }
    }
})

Events

The Event type of Activity is a custom event that channels or clients use to send structured data to your agent. This data isn't predefined in the Activity payload structure.

You need to create a method or route handler for the specific Event type. Then, manage the desired logic based on the:

  • Name: The event name or identifier from the client
  • Value: Event payload that is typically a JSON object
agent.OnActivity(ActivityTypes.Event, async (turnContext turnState, cancellationToken) =>
{
    var eventName = turnContext.Activity.Name;
    var eventValue = turnContext.Activity.Value;

    // custom event (E.g. a switch on eventName)
});

Invoke

An Invoke type of Activity is a specific type of activity that a client calls into an agent to perform a command or operation. It's not just a message. Examples of these types of activities are common in Microsoft Teams for task/fetch and task/submit. Not all channels support these types of activities.

Typing

A Typing type of Activity is a classification of activity to indicate someone is typing in a conversation. This activity is commonly seen between human to human conversations in Microsoft Teams client, for example. Typing activities aren't supported in every client. Notably, Microsoft 365 Copilot doesn't support typing activities.

await turnContext.SendActivityAsync(new Activity { Type = ActivityTypes.Typing }, cancellationToken); 
await Task.Delay(2000);
await turnContext.SendActivityAsync(MessageFactory.Text("Here is your answer..."), cancellationToken);

Create and send activities

To send responses, the TurnContext provides multiple methods for sending responses back to the user.

agent.OnActivity(ActivityTypes.Message, async (turnContext, turnState, cancellationToken))
{
    await turnContext.SendActivityAsync("hello!", cancellationToken: CancellationToken); // uses string directly
    await turnContext.SendActivityAsync(MessageFactory.Text("Hello"), cancellationToken); // uses Message Factory
    await turnContext.SendActivitiesAsync(activities, cancellationToken); // send multiple activities in an Activity array
}

Work with attachments

Agents often work with attachments that users (or even other agents) submit. The client sends a Message activity that includes an attachment (it's not a specific type of activity). Your code needs to handle receiving the message with the attachment, read the metadata, and securely fetch the file from the URL that the client provided. Typically, you move the file to your own storage.

To receive an attachment

The following code shows how to receive an attachment.

agent.OnActivity(ActivityTypes.Message, async(turnContext, turnState, cancellationToken)) =>
{
    var activity = turnContext.Activity;
    if (activity.Attachments != null && activity.Attachments.Count > 0)
    {
        foreach (var attachment in activity.Attachments)
        {
            // get metadata as required e.g. attachment.ContextType or attachment.ContentUrl
            // use the URL to securely download the attachment and complete your business logic
        };
    }
}

Typically, to receive the document for the attachment, the client sends an authenticated GET request to retrieve the actual contents. Each adapter has its own way to get that data. For example, Teams, OneDrive, and so on. It's also important to know that those URLs are typically short lived, and so don't assume the URLs stay valid for long. This limitation is why moving to your own storage is important if you need to refer to the contents later.

Citations

It's important to know that Attachment and Citation aren't the same object type. Clients, like Microsoft Teams, handle citations in their own ways. They use the Entities property of the Activity. You can add citations with activity.Entities.Add and add a new Entity object that has the specific Citation definition based on your client. It gets serialized as a JSON object that the client then deserializes based on how it renders in the client. Fundamentally, Attachments are messages, and Citations can reference attachments and are another object sent in Entities of the Activity payload.

Channel specific considerations

The Microsoft 365 Agents SDK is built as a 'Hub' that developers use to create agents that can work with any client, including the clients we support. It provides the tools for developers to build their own channel adapter using the same framework. This architecture gives developers breadth when it comes to agents, and provides extensibility to clients to connect to that hub, which can be one or more clients like Microsoft Teams, Slack, and more.

Different channels have different capabilities and limitations.

You can check the channel you received the activity from by inspecting the channelId property in the Activity.

Channels include specific data that doesn't conform to the generic Activity payload across all channels. You can access this data from the TurnContext.[Activity.ChannelData](/dotnet/api/microsoft.agents.core.models.activity.channeldata) property by casting it to variables for use in your code.

The following sections summarize considerations when working with common clients.

Microsoft Teams

  • Supports rich Adaptive Cards with advanced features.
  • Supports message updates and deletions.
  • Has specific channel data for Teams features, such as mentions and meeting info.
  • Supports invoke activities for task modules.

Microsoft 365 Copilot

  • Primarily focused on message activities.
  • Supports citations and references in responses.
  • Requires streaming responses.
  • Limited support for rich cards and adaptive cards.

Web Chat/DirectLine

Web Chat is an HTTP protocol that agents can use to communicate over HTTPS.

  • Full support for all activity types.
  • Supports custom channel data.

Non-Microsoft channels

These channels include Slack, Facebook, and more.

  • Might have limited support for certain activity types.
  • Card rendering might be different or unsupported.
  • Always check specific channel documentation.

Next steps