Save user and conversation data


A bot is inherently stateless. Once your bot is deployed, it may not run in the same process or on the same machine from one turn to the next. However, your bot may need to track the context of a conversation so that it can manage its behavior and remember answers to previous questions. The state and storage features of the Bot Framework SDK allow you to add state to your bot. Bots use state management and storage objects to manage and persist state. The state manager provides an abstraction layer that lets you access state properties using property accessors, independent of the type of underlying storage.


  • Knowledge of bot basics and how bots manage state is required.
  • The code in this article is based on the State Management Bot sample. You'll need a copy of the sample in either C#, JavaScript or Python.

About this sample

Upon receiving user input, this sample checks the stored conversation state to see if this user has previously been prompted to provide their name. If not, the user's name is requested and that input is stored within user state. If so, the name stored within user state is used to converse with the user and their input data, along with the time received and input channel Id, is returned back to the user. The time and channel Id values are retrieved from the user conversation data and then saved to conversation state. The following diagram shows the relationship between the bot, user profile, and conversation data classes.

Define classes

The first step in setting up state management is to define the classes containing the information to manage in the user and conversation state. The example used in this article, defines the following classes:

  • In UserProfile.cs, you define a UserProfile class for the user information that the bot will collect.
  • In ConversationData.cs, you define a ConversationData class to control our conversation state while gathering user information.

The following code examples show the definitions for the UserProfile and ConversationData classes.


// Defines a state property used to track information about the user.
public class UserProfile
    public string Name { get; set; }


// Defines a state property used to track conversation data.
public class ConversationData
    // The time-stamp of the most recent incoming message.
    public string Timestamp { get; set; }

    // The ID of the user's channel.
    public string ChannelId { get; set; }

    // Track whether we have already asked the user's name
    public bool PromptedUserForName { get; set; } = false;

Create conversation and user state objects

Next, you register MemoryStorage that is used to create UserState and ConversationState objects. The user and conversation state objects are created at Startup and dependency injected into the bot constructor. Other services for a bot that are registered are: a credential provider, an adapter, and the bot implementation.


// Create the storage we'll be using for User and Conversation state.
// (Memory is great for testing purposes - examples of implementing storage with
// Azure Blob Storage or Cosmos DB are below).
var storage = new MemoryStorage();
// Create the User state passing in the storage layer.
var userState = new UserState(storage);

// Create the Conversation state passing in the storage layer.
var conversationState = new ConversationState(storage);


private BotState _conversationState;
private BotState _userState;

public StateManagementBot(ConversationState conversationState, UserState userState)
    _conversationState = conversationState;
    _userState = userState;

Add state property accessors

Now you create property accessors using the CreateProperty method that provides a handle to the BotState object. Each state property accessor allows you to get or set the value of the associated state property. Before you use the state properties, use each accessor to load the property from storage and get it from the state cache. To get the properly scoped key associated with the state property, you call the GetAsync method.


var conversationStateAccessors =  _conversationState.CreateProperty<ConversationData>(nameof(ConversationData));
var userStateAccessors = _userState.CreateProperty<UserProfile>(nameof(UserProfile));

Access state from your bot

The preceding section covers the initialization-time steps to add state property accessors to our bot. Now, you can use those accessors at run-time to read and write state information. The sample code below uses the following logic flow:

  • If userProfile.Name is empty and conversationData.PromptedUserForName is true, you retrieve the user name provided and store this within user state.
  • If userProfile.Name is empty and conversationData.PromptedUserForName is false, you ask for the user's name.
  • If userProfile.Name was previously stored, you retrieve message time and channel Id from the user input, echo all data back to the user, and store the retrieved data within conversation state.


protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
    // Get the state properties from the turn context.

    var conversationStateAccessors =  _conversationState.CreateProperty<ConversationData>(nameof(ConversationData));
    var conversationData = await conversationStateAccessors.GetAsync(turnContext, () => new ConversationData());

    var userStateAccessors = _userState.CreateProperty<UserProfile>(nameof(UserProfile));
    var userProfile = await userStateAccessors.GetAsync(turnContext, () => new UserProfile());

    if (string.IsNullOrEmpty(userProfile.Name))
        // First time around this is set to false, so we will prompt user for name.
        if (conversationData.PromptedUserForName)
            // Set the name to what the user provided.
            userProfile.Name = turnContext.Activity.Text?.Trim();

            // Acknowledge that we got their name.
            await turnContext.SendActivityAsync($"Thanks {userProfile.Name}. To see conversation data, type anything.");

            // Reset the flag to allow the bot to go through the cycle again.
            conversationData.PromptedUserForName = false;
            // Prompt the user for their name.
            await turnContext.SendActivityAsync($"What is your name?");

            // Set the flag to true, so we don't prompt in the next turn.
            conversationData.PromptedUserForName = true;
        // Add message details to the conversation data.
        // Convert saved Timestamp to local DateTimeOffset, then to string for display.
        var messageTimeOffset = (DateTimeOffset) turnContext.Activity.Timestamp;
        var localMessageTime = messageTimeOffset.ToLocalTime();
        conversationData.Timestamp = localMessageTime.ToString();
        conversationData.ChannelId = turnContext.Activity.ChannelId.ToString();

        // Display state data.
        await turnContext.SendActivityAsync($"{userProfile.Name} sent: {turnContext.Activity.Text}");
        await turnContext.SendActivityAsync($"Message received at: {conversationData.Timestamp}");
        await turnContext.SendActivityAsync($"Message received from: {conversationData.ChannelId}");

Before you exit the turn handler, you use the state management objects' SaveChangesAsync() method to write all state changes back to storage.


public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
    await base.OnTurnAsync(turnContext, cancellationToken);

    // Save any state changes that might have occurred during the turn.
    await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken);
    await _userState.SaveChangesAsync(turnContext, false, cancellationToken);

Test the bot

Download and install the latest Bot Framework Emulator

  1. Run the sample locally on your machine. If you need instructions, refer to the README file for C# Sample or JS Sample.
  2. Use the Emulator to test the bot as shown below.

test state bot sample

Additional resources

Privacy: If you intend to store user's personal data, you should ensure compliance with General Data Protection Regulation.

State management: All of the state management calls are asynchronous, and last-writer-wins by default. In practice, you should get, set, and save state as close together in your bot as possible.

Critical business data: Use bot state to store preferences, user name, or the last thing they ordered, but do not use it to store critical business data. For critical data, create your own storage components or write directly to storage.

Recognizer-Text: The sample uses the Microsoft/Recognizers-Text libraries to parse and validate user input. For more information, see the overview page.

Next steps

Now that you know how to configure state to help you read and write bot data to storage, let's learn how ask the user a series of questions, validate their answers, and save their input.