Save data in conversation (Dialogflow)

Explore in Dialogflow

Click Continue to import our Save data sample in Dialogflow. Then, follow the steps below to deploy and test the sample:

  1. Enter an agent name and create a new Dialogflow agent for the sample.
  2. After the agent is done importing, click Go to agent.
  3. From the main navigation menu, go to Fulfillment.
  4. Enable the Inline Editor, then click Deploy. The editor contains the sample code.
  5. From the main navigation menu, go to Integrations, then click Google Assistant.
  6. In the modal window that appears, enable Auto-preview changes and click Test to open the Actions simulator.
  7. In the simulator, enter Talk to my test app to test the sample!
Continue

Part of providing an excellent user experience is often being able to save data between turns of a conversation or across multiple conversations with a user. This is helpful if you're providing helpful reprompts in a single conversation, saving game scores across sessions, or remembering small pieces of information for a user.

The requirements vary slightly based on whether you need to save data within a conversation or across conversations. To save data in a conversation, you can use the conversationToken field of your AppResponse object.

To save data across conversations, take the following steps instead:

  1. Determine whether the user is verified or a guest.
  2. Store or access user data using the userStorage field of your AppResponse object.

Saving data between turns of a conversation

The conversationToken field is a string that contains an opaque token that is recirculated to the Action every conversation turn. For example, if you set the value to "count=1" in your AppResponse for the first turn of the conversation, the AppRequest received by your Action for the second turn of the conversation contains "count=1" in its conversationToken.

The token is always initialized to an empty string at the beginning of a conversation. If you use the Actions on Google Node.js client library, you can interface with the conversation token as a JSON object using conv.data, where conv is your instance of Conversation.

The following sample shows how to save a counter in the conversationToken field of your AppResponse:

Node.js

conv.data.firstNum = firstNum;
conv.ask(`Got it, the first number is ${firstNum}.`);
conv.ask(`What's the second number?`);

Java

ResponseBuilder responseBuilder = getResponseBuilder(request);
responseBuilder.getConversationData().put("firstNum", firstNum);
responseBuilder.add("Got it, the first number is " + firstNum + ".");
responseBuilder.add("What's the second number?");
return responseBuilder.build();

JSON

Note that the JSON below describes a webhook response which uses outputContexts instead of conversationToken.

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "richResponse": {
        "items": [
          {
            "simpleResponse": {
              "textToSpeech": "Got it, the first number is 23."
            }
          },
          {
            "simpleResponse": {
              "textToSpeech": "What's the second number?"
            }
          }
        ]
      }
    }
  },
  "outputContexts": [
    {
      "name": "projects/save-data-df-js/agent/sessions/ABwppHGfFkWJdHKPpBEYiGkhdoakWmYj_2sZa4o8pbGG9nj4q5_GfDTtNEXOY34mLX8G4o_d7oZdUW9bnBZC/contexts/_actions_on_google",
      "lifespanCount": 99,
      "parameters": {
        "data": "{\"firstNum\":23}"
      }
    }
  ]
}

JSON

Note that the JSON below describes a webhook response.

{
  "expectUserResponse": true,
  "expectedInputs": [
    {
      "possibleIntents": [
        {
          "intent": "actions.intent.TEXT"
        }
      ],
      "inputPrompt": {
        "richInitialPrompt": {
          "items": [
            {
              "simpleResponse": {
                "textToSpeech": "Got it, the first number is 23."
              }
            },
            {
              "simpleResponse": {
                "textToSpeech": "What's the second number?"
              }
            }
          ]
        }
      }
    }
  ],
  "conversationToken": "{\"data\":{\"firstNum\":23}}"
}

See our Provide helpful reprompts and fail gracefully best practice guide for a practical usage example.

Saving data across conversations

The userStorage field of your AppResponse object is a string that contains an opaque token supplied by the Action that is saved across conversations for a particular user. For example, a game can save the highest score of a user in userStorage and use its value in the welcome message each time the user starts a new conversation.

Determining and handling user verification status

A user's verification status can have a value of GUEST or VERIFIED. At the start of each conversation, Actions on Google sets the user's verification status based on a variety of indicators when the conversation starts. As one example, a user logged in to the Google Assistant on their mobile device has a verification status of VERIFIED.

The following are possible reasons for a user to have a verification status of GUEST:

  • The user has personal results turned off.
  • The user disabled their Web & App Activity. Keep in mind that some users may have this setting disabled at the domain level.
  • If a device has Voice Match enabled, and the match fails or the user invokes the Assistant without using their voice (such as a long press on a Google Home).
  • The user isn't signed in.

Always check the user's verification status before storing data with userStorage or starting an account linking flow to prevent guest users from interacting with a feature that will fail for them.

If you use the Actions On Google Client Library for Node.js, you can interface with the user storage as a JSON object using conv.user.storage, where conv is your instance of Conversation. The following sample shows how to save a counter in the userStorage field of your AppResponse:

Node.js

app.intent('Save Sum', (conv) => {
  if (conv.user.verification === 'VERIFIED') {
    conv.user.storage.sum = conv.data.sum;
    conv.close(`Alright, I'll store that for next time. See you then.`);
  } else {
    conv.close(`I can't save that right now, but we can add ` +
      `new numbers next time!`);
  }
});

Java

@ForIntent("Save Sum")
public ActionResponse saveSum(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  Integer sum = ((Double) request.getConversationData().get("sum")).intValue();
  String verificationStatus = request.getUser().getUserVerificationStatus();
  if (verificationStatus.equals("VERIFIED")) {
    responseBuilder.getUserStorage().put("sum", sum);
    responseBuilder.add("Alright, I'll store that for next time. See you then.");
  } else {
    responseBuilder.add("I can't save that right now, but we can add new numbers next time!");
  }
  responseBuilder.endConversation();
  return responseBuilder.build();
}

Node.js

if (conv.user.verification === 'VERIFIED') {
  conv.user.storage.sum = conv.data.sum;
  conv.close(`Alright, I'll store that for next time. See you then.`);
} else {
  conv.close(`I can't save that right now, but we can add ` +
    `new numbers next time!`);
}

Java

ResponseBuilder responseBuilder = getResponseBuilder(request);
Integer sum = ((Double) request.getConversationData().get("sum")).intValue();
String verificationStatus = request.getUser().getUserVerificationStatus();
if (verificationStatus.equals("VERIFIED")) {
  responseBuilder.getUserStorage().put("sum", sum);
  responseBuilder.add("Alright, I'll store that for next time. See you then.");
} else {
  responseBuilder.add("I can't save that right now, but we can add new numbers next time!");
}
responseBuilder.endConversation();
return responseBuilder.build();

JSON

Note that the JSON below describes a webhook response.

{
  "payload": {
    "google": {
      "expectUserResponse": false,
      "richResponse": {
        "items": [
          {
            "simpleResponse": {
              "textToSpeech": "Alright, I'll store that for next time. See you then."
            }
          }
        ]
      },
      "userStorage": "{\"data\":{\"sum\":68}}"
    }
  }
}

JSON

Note that the JSON below describes a webhook response.

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "simpleResponse": {
            "textToSpeech": "Alright, I'll store that for next time. See you then."
          }
        }
      ]
    }
  },
  "conversationToken": "{\"data\":{\"firstNum\":23,\"sum\":68}}",
  "userStorage": "{\"data\":{\"sum\":68}}"
}

See our Personalize the conversation with user preferences best practice guide for a practical usage example.

Legal note: Obtaining consent prior to accessing userStorage. Some countries have regulations that require developers to obtain consent from the user before they can access, or save certain information (like personal information) in the userStorage. If you operate in one of these countries and you want to access, or save such information in userStorage, you must use the Confirmation helper to ask consent to the user and obtain the consent before you can start storing such information in userStorage.

User storage expiration

When the Assistant can match an identity to the user, the contents of userStorage never expires, and only the user or the Action itself can clear it.

When the Assistant can't match an identity to the user, the content of userStorage is cleared at the end of the conversation. Here are some examples cases where the Assistant can't match an identity to the user:

  • Voice match is set up and there is no match.
  • The user disabled personal data.

Clear content of the userStorage field

You can clear the content of the userStorage field of your Action by setting the resetUserStorage field of your AppResponse to true. If you set the value of userStorage to an empty string, the value of userStorage remains unaltered in the next turn of conversation. This lets you avoid sending back the whole userStorage in turns where its content didn't change.

If you are using the Actions On Google Client Library for Node.js, you can just set the value of conv.user.storage to {} (empty object).

Node.js

app.intent('Forget Number', (conv) => {
  conv.user.storage = {};
  conv.ask(`Alright, I forgot your last result.`);
  conv.ask(`Let's add two new numbers. What is the first number?`);
});

Java

@ForIntent("Forget Number")
public ActionResponse forgetNumber(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  responseBuilder.getUserStorage().clear();
  responseBuilder.add("Alright, I forgot your last result.");
  responseBuilder.add("Let's add two new numbers. What is the first number?");
  return responseBuilder.build();
}

Node.js

conv.user.storage = {};
conv.ask(`Alright, I forgot your last result.`);
conv.ask(`Let's add two new numbers. What is the first number?`);

Java

ResponseBuilder responseBuilder = getResponseBuilder(request);
responseBuilder.getUserStorage().clear();
responseBuilder.add("Alright, I forgot your last result.");
responseBuilder.add("Let's add two new numbers. What is the first number?");
return responseBuilder.build();

JSON

Note that the JSON below describes a webhook response.

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "richResponse": {
        "items": [
          {
            "simpleResponse": {
              "textToSpeech": "Alright, I forgot your last result."
            }
          },
          {
            "simpleResponse": {
              "textToSpeech": "Let's add two new numbers. What is the first number?"
            }
          }
        ]
      },
      "userStorage": "{\"data\":{}}"
    }
  }
}

JSON

Note that the JSON below describes a webhook response.

{
  "expectUserResponse": true,
  "expectedInputs": [
    {
      "possibleIntents": [
        {
          "intent": "actions.intent.TEXT"
        }
      ],
      "inputPrompt": {
        "richInitialPrompt": {
          "items": [
            {
              "simpleResponse": {
                "textToSpeech": "Alright, I forgot your last result."
              }
            },
            {
              "simpleResponse": {
                "textToSpeech": "Let's add two new numbers. What is the first number?"
              }
            }
          ]
        }
      }
    }
  ],
  "userStorage": "{\"data\":{}}"
}

As a user, you can view the content of the userStorage field in an Action you invoked. You can also remove your stored user data from that specific Action by stopping the service from remembering you.

  1. Open the Assistant app on your phone.
  2. Tap the drawer icon.

  3. In the Explore tab, find the Action you want to view or clear the user storage for and tap on it to open the details page.
  4. Scroll to the bottom of the page.
    • To view the content of the userStorage field, tap [View stored data].
    • To remove the stored user data, tap Stop $action from remembering me.